packages feed

fay-0.23.1.8: examples/D3TreeSample.hs

module D3TreeSample where

import FFI
import Data.Char

data Event
data D3SvgCanvas
data D3SvgDiagonal
data D3SvgLink
data D3SvgNode
data D3LayoutTree
data D3LayoutTreeNodes
data D3LayoutTreeLinks

--------- D3: A JavaScript visualization library for HTML and SVG. http://d3js.org
--------- Usage example with Fay

--------- JSON FEED

data JsonTree = JsonNode { name :: String, children :: JsonForest }
type JsonForest = [JsonTree]

unfoldTree :: (Int -> (String, [Int])) -> Int -> JsonTree
unfoldTree f b = let (a, bs) = f b in JsonNode a (unfoldForest f bs)

unfoldForest :: (Int -> (String, [Int])) -> [Int] -> JsonForest
unfoldForest f = map (unfoldTree f)

createJson :: Int -> JsonTree
createJson = unfoldTree (\n -> ([chr (n + ord 'a')], replicate n (n-1)))

--------- FFI

addWindowEvent :: String -> (Event -> Fay ()) -> Fay ()
addWindowEvent = ffi "window.addEventListener(%1, %2)"

createSvgCanvas :: Int -> Int -> Fay D3SvgCanvas
createSvgCanvas = ffi "d3.select('#viz')\
                                        \.append('svg:svg').attr('width', %1).attr('height', %2)\
                                        \.append('svg:g').attr('transform', 'translate(40, 40)')"

createTree :: Int -> Int -> Fay D3LayoutTree
createTree = ffi "d3.layout.tree().size([%1,%2])"

createDiagonal :: Fay D3SvgDiagonal
createDiagonal = ffi "d3.svg.diagonal().projection(function(d) { return [d.x, d.y]; })"

createNodes :: D3LayoutTree -> JsonTree -> Fay D3LayoutTreeNodes
createNodes = ffi "%1.nodes(%2)"

createLinks :: D3LayoutTree -> D3LayoutTreeNodes -> Fay D3LayoutTreeLinks
createLinks = ffi "%1.links(%2)"

displayLinks :: D3SvgCanvas -> D3LayoutTreeLinks -> D3SvgDiagonal -> Fay D3SvgLink
displayLinks = ffi "%1.selectAll('pathlink').data(%2)\
                                                     \.enter().append('svg:path')\
                                                     \.attr('class', 'link')\
                                                     \.attr('d', %3)"

displayNodes :: D3SvgCanvas -> D3LayoutTreeNodes -> Fay D3SvgNode
displayNodes = ffi "%1.selectAll('g.node').data(%2)\
                                                   \.enter().append('svg:g')\
                                                   \.attr('transform', \
                                                   \function(d) { return 'translate(' + d.x + ',' + d.y + ')'; })"

addDotToNodes :: D3SvgNode -> Double -> Fay ()
addDotToNodes = ffi "%1.append('svg:circle').attr('r', %2)"

addTextToNodes :: D3SvgNode -> Fay()
addTextToNodes = ffi "%1.append('svg:text')\
                                           \.attr('dx', function(d) { return d.children ? -8 : 8; })\
                                           \.attr('dy', 3)\
                                           \.attr('text-anchor', function(d) { return d.children ? 'end' : 'start'; })\
                                           \.text(function(d) { return d.name; })"

--------- START

d3TreeSample :: Event -> Fay()
d3TreeSample event = do
    svgCanvas <- createSvgCanvas 800 600
    d3Tree <- createTree 600 300
    diagonal <- createDiagonal

    nodes <- createNodes d3Tree (createJson 4)
    links <- createLinks d3Tree nodes

    svgLink <- displayLinks svgCanvas links diagonal
    svgNode <- displayNodes svgCanvas nodes

    addDotToNodes svgNode 2.5
    addTextToNodes svgNode


main :: Fay ()
main = do
    addWindowEvent "load" d3TreeSample