An isometric treemap using tree colors to help in differentiating subtrees and perceiving hierarchy depth.
Last active
October 15, 2015 20:18
-
-
Save nitaku/b82c356f1969911042d6 to your computer and use it in GitHub Desktop.
Isometric treemap with tree colors (flare)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "flare", | |
"children": [ | |
{ | |
"name": "analytics", | |
"children": [ | |
{ | |
"name": "cluster", | |
"children": [ | |
{ | |
"name": "AgglomerativeCluster", | |
"size": 3938 | |
}, | |
{ | |
"name": "CommunityStructure", | |
"size": 3812 | |
}, | |
{ | |
"name": "HierarchicalCluster", | |
"size": 6714 | |
}, | |
{ | |
"name": "MergeEdge", | |
"size": 743 | |
} | |
] | |
}, | |
{ | |
"name": "graph", | |
"children": [ | |
{ | |
"name": "BetweennessCentrality", | |
"size": 3534 | |
}, | |
{ | |
"name": "LinkDistance", | |
"size": 5731 | |
}, | |
{ | |
"name": "MaxFlowMinCut", | |
"size": 7840 | |
}, | |
{ | |
"name": "ShortestPaths", | |
"size": 5914 | |
}, | |
{ | |
"name": "SpanningTree", | |
"size": 3416 | |
} | |
] | |
}, | |
{ | |
"name": "optimization", | |
"children": [ | |
{ | |
"name": "AspectRatioBanker", | |
"size": 7074 | |
} | |
] | |
} | |
] | |
}, | |
{ | |
"name": "animate", | |
"children": [ | |
{ | |
"name": "Easing", | |
"size": 17010 | |
}, | |
{ | |
"name": "FunctionSequence", | |
"size": 5842 | |
}, | |
{ | |
"name": "interpolate", | |
"children": [ | |
{ | |
"name": "ArrayInterpolator", | |
"size": 1983 | |
}, | |
{ | |
"name": "ColorInterpolator", | |
"size": 2047 | |
}, | |
{ | |
"name": "DateInterpolator", | |
"size": 1375 | |
}, | |
{ | |
"name": "Interpolator", | |
"size": 8746 | |
}, | |
{ | |
"name": "MatrixInterpolator", | |
"size": 2202 | |
}, | |
{ | |
"name": "NumberInterpolator", | |
"size": 1382 | |
}, | |
{ | |
"name": "ObjectInterpolator", | |
"size": 1629 | |
}, | |
{ | |
"name": "PointInterpolator", | |
"size": 1675 | |
}, | |
{ | |
"name": "RectangleInterpolator", | |
"size": 2042 | |
} | |
] | |
}, | |
{ | |
"name": "ISchedulable", | |
"size": 1041 | |
}, | |
{ | |
"name": "Parallel", | |
"size": 5176 | |
}, | |
{ | |
"name": "Pause", | |
"size": 449 | |
}, | |
{ | |
"name": "Scheduler", | |
"size": 5593 | |
}, | |
{ | |
"name": "Sequence", | |
"size": 5534 | |
}, | |
{ | |
"name": "Transition", | |
"size": 9201 | |
}, | |
{ | |
"name": "Transitioner", | |
"size": 19975 | |
}, | |
{ | |
"name": "TransitionEvent", | |
"size": 1116 | |
}, | |
{ | |
"name": "Tween", | |
"size": 6006 | |
} | |
] | |
}, | |
{ | |
"name": "data", | |
"children": [ | |
{ | |
"name": "converters", | |
"children": [ | |
{ | |
"name": "Converters", | |
"size": 721 | |
}, | |
{ | |
"name": "DelimitedTextConverter", | |
"size": 4294 | |
}, | |
{ | |
"name": "GraphMLConverter", | |
"size": 9800 | |
}, | |
{ | |
"name": "IDataConverter", | |
"size": 1314 | |
}, | |
{ | |
"name": "JSONConverter", | |
"size": 2220 | |
} | |
] | |
}, | |
{ | |
"name": "DataField", | |
"size": 1759 | |
}, | |
{ | |
"name": "DataSchema", | |
"size": 2165 | |
}, | |
{ | |
"name": "DataSet", | |
"size": 586 | |
}, | |
{ | |
"name": "DataSource", | |
"size": 3331 | |
}, | |
{ | |
"name": "DataTable", | |
"size": 772 | |
}, | |
{ | |
"name": "DataUtil", | |
"size": 3322 | |
} | |
] | |
}, | |
{ | |
"name": "display", | |
"children": [ | |
{ | |
"name": "DirtySprite", | |
"size": 8833 | |
}, | |
{ | |
"name": "LineSprite", | |
"size": 1732 | |
}, | |
{ | |
"name": "RectSprite", | |
"size": 3623 | |
}, | |
{ | |
"name": "TextSprite", | |
"size": 10066 | |
} | |
] | |
}, | |
{ | |
"name": "flex", | |
"children": [ | |
{ | |
"name": "FlareVis", | |
"size": 4116 | |
} | |
] | |
}, | |
{ | |
"name": "physics", | |
"children": [ | |
{ | |
"name": "DragForce", | |
"size": 1082 | |
}, | |
{ | |
"name": "GravityForce", | |
"size": 1336 | |
}, | |
{ | |
"name": "IForce", | |
"size": 319 | |
}, | |
{ | |
"name": "NBodyForce", | |
"size": 10498 | |
}, | |
{ | |
"name": "Particle", | |
"size": 2822 | |
}, | |
{ | |
"name": "Simulation", | |
"size": 9983 | |
}, | |
{ | |
"name": "Spring", | |
"size": 2213 | |
}, | |
{ | |
"name": "SpringForce", | |
"size": 1681 | |
} | |
] | |
}, | |
{ | |
"name": "query", | |
"children": [ | |
{ | |
"name": "AggregateExpression", | |
"size": 1616 | |
}, | |
{ | |
"name": "And", | |
"size": 1027 | |
}, | |
{ | |
"name": "Arithmetic", | |
"size": 3891 | |
}, | |
{ | |
"name": "Average", | |
"size": 891 | |
}, | |
{ | |
"name": "BinaryExpression", | |
"size": 2893 | |
}, | |
{ | |
"name": "Comparison", | |
"size": 5103 | |
}, | |
{ | |
"name": "CompositeExpression", | |
"size": 3677 | |
}, | |
{ | |
"name": "Count", | |
"size": 781 | |
}, | |
{ | |
"name": "DateUtil", | |
"size": 4141 | |
}, | |
{ | |
"name": "Distinct", | |
"size": 933 | |
}, | |
{ | |
"name": "Expression", | |
"size": 5130 | |
}, | |
{ | |
"name": "ExpressionIterator", | |
"size": 3617 | |
}, | |
{ | |
"name": "Fn", | |
"size": 3240 | |
}, | |
{ | |
"name": "If", | |
"size": 2732 | |
}, | |
{ | |
"name": "IsA", | |
"size": 2039 | |
}, | |
{ | |
"name": "Literal", | |
"size": 1214 | |
}, | |
{ | |
"name": "Match", | |
"size": 3748 | |
}, | |
{ | |
"name": "Maximum", | |
"size": 843 | |
}, | |
{ | |
"name": "methods", | |
"children": [ | |
{ | |
"name": "add", | |
"size": 593 | |
}, | |
{ | |
"name": "and", | |
"size": 330 | |
}, | |
{ | |
"name": "average", | |
"size": 287 | |
}, | |
{ | |
"name": "count", | |
"size": 277 | |
}, | |
{ | |
"name": "distinct", | |
"size": 292 | |
}, | |
{ | |
"name": "div", | |
"size": 595 | |
}, | |
{ | |
"name": "eq", | |
"size": 594 | |
}, | |
{ | |
"name": "fn", | |
"size": 460 | |
}, | |
{ | |
"name": "gt", | |
"size": 603 | |
}, | |
{ | |
"name": "gte", | |
"size": 625 | |
}, | |
{ | |
"name": "iff", | |
"size": 748 | |
}, | |
{ | |
"name": "isa", | |
"size": 461 | |
}, | |
{ | |
"name": "lt", | |
"size": 597 | |
}, | |
{ | |
"name": "lte", | |
"size": 619 | |
}, | |
{ | |
"name": "max", | |
"size": 283 | |
}, | |
{ | |
"name": "min", | |
"size": 283 | |
}, | |
{ | |
"name": "mod", | |
"size": 591 | |
}, | |
{ | |
"name": "mul", | |
"size": 603 | |
}, | |
{ | |
"name": "neq", | |
"size": 599 | |
}, | |
{ | |
"name": "not", | |
"size": 386 | |
}, | |
{ | |
"name": "or", | |
"size": 323 | |
}, | |
{ | |
"name": "orderby", | |
"size": 307 | |
}, | |
{ | |
"name": "range", | |
"size": 772 | |
}, | |
{ | |
"name": "select", | |
"size": 296 | |
}, | |
{ | |
"name": "stddev", | |
"size": 363 | |
}, | |
{ | |
"name": "sub", | |
"size": 600 | |
}, | |
{ | |
"name": "sum", | |
"size": 280 | |
}, | |
{ | |
"name": "update", | |
"size": 307 | |
}, | |
{ | |
"name": "variance", | |
"size": 335 | |
}, | |
{ | |
"name": "where", | |
"size": 299 | |
}, | |
{ | |
"name": "xor", | |
"size": 354 | |
}, | |
{ | |
"name": "_", | |
"size": 264 | |
} | |
] | |
}, | |
{ | |
"name": "Minimum", | |
"size": 843 | |
}, | |
{ | |
"name": "Not", | |
"size": 1554 | |
}, | |
{ | |
"name": "Or", | |
"size": 970 | |
}, | |
{ | |
"name": "Query", | |
"size": 13896 | |
}, | |
{ | |
"name": "Range", | |
"size": 1594 | |
}, | |
{ | |
"name": "StringUtil", | |
"size": 4130 | |
}, | |
{ | |
"name": "Sum", | |
"size": 791 | |
}, | |
{ | |
"name": "Variable", | |
"size": 1124 | |
}, | |
{ | |
"name": "Variance", | |
"size": 1876 | |
}, | |
{ | |
"name": "Xor", | |
"size": 1101 | |
} | |
] | |
}, | |
{ | |
"name": "scale", | |
"children": [ | |
{ | |
"name": "IScaleMap", | |
"size": 2105 | |
}, | |
{ | |
"name": "LinearScale", | |
"size": 1316 | |
}, | |
{ | |
"name": "LogScale", | |
"size": 3151 | |
}, | |
{ | |
"name": "OrdinalScale", | |
"size": 3770 | |
}, | |
{ | |
"name": "QuantileScale", | |
"size": 2435 | |
}, | |
{ | |
"name": "QuantitativeScale", | |
"size": 4839 | |
}, | |
{ | |
"name": "RootScale", | |
"size": 1756 | |
}, | |
{ | |
"name": "Scale", | |
"size": 4268 | |
}, | |
{ | |
"name": "ScaleType", | |
"size": 1821 | |
}, | |
{ | |
"name": "TimeScale", | |
"size": 5833 | |
} | |
] | |
}, | |
{ | |
"name": "util", | |
"children": [ | |
{ | |
"name": "Arrays", | |
"size": 8258 | |
}, | |
{ | |
"name": "Colors", | |
"size": 10001 | |
}, | |
{ | |
"name": "Dates", | |
"size": 8217 | |
}, | |
{ | |
"name": "Displays", | |
"size": 12555 | |
}, | |
{ | |
"name": "Filter", | |
"size": 2324 | |
}, | |
{ | |
"name": "Geometry", | |
"size": 10993 | |
}, | |
{ | |
"name": "heap", | |
"children": [ | |
{ | |
"name": "FibonacciHeap", | |
"size": 9354 | |
}, | |
{ | |
"name": "HeapNode", | |
"size": 1233 | |
} | |
] | |
}, | |
{ | |
"name": "IEvaluable", | |
"size": 335 | |
}, | |
{ | |
"name": "IPredicate", | |
"size": 383 | |
}, | |
{ | |
"name": "IValueProxy", | |
"size": 874 | |
}, | |
{ | |
"name": "math", | |
"children": [ | |
{ | |
"name": "DenseMatrix", | |
"size": 3165 | |
}, | |
{ | |
"name": "IMatrix", | |
"size": 2815 | |
}, | |
{ | |
"name": "SparseMatrix", | |
"size": 3366 | |
} | |
] | |
}, | |
{ | |
"name": "Maths", | |
"size": 17705 | |
}, | |
{ | |
"name": "Orientation", | |
"size": 1486 | |
}, | |
{ | |
"name": "palette", | |
"children": [ | |
{ | |
"name": "ColorPalette", | |
"size": 6367 | |
}, | |
{ | |
"name": "Palette", | |
"size": 1229 | |
}, | |
{ | |
"name": "ShapePalette", | |
"size": 2059 | |
}, | |
{ | |
"name": "SizePalette", | |
"size": 2291 | |
} | |
] | |
}, | |
{ | |
"name": "Property", | |
"size": 5559 | |
}, | |
{ | |
"name": "Shapes", | |
"size": 19118 | |
}, | |
{ | |
"name": "Sort", | |
"size": 6887 | |
}, | |
{ | |
"name": "Stats", | |
"size": 6557 | |
}, | |
{ | |
"name": "Strings", | |
"size": 22026 | |
} | |
] | |
}, | |
{ | |
"name": "vis", | |
"children": [ | |
{ | |
"name": "axis", | |
"children": [ | |
{ | |
"name": "Axes", | |
"size": 1302 | |
}, | |
{ | |
"name": "Axis", | |
"size": 24593 | |
}, | |
{ | |
"name": "AxisGridLine", | |
"size": 652 | |
}, | |
{ | |
"name": "AxisLabel", | |
"size": 636 | |
}, | |
{ | |
"name": "CartesianAxes", | |
"size": 6703 | |
} | |
] | |
}, | |
{ | |
"name": "controls", | |
"children": [ | |
{ | |
"name": "AnchorControl", | |
"size": 2138 | |
}, | |
{ | |
"name": "ClickControl", | |
"size": 3824 | |
}, | |
{ | |
"name": "Control", | |
"size": 1353 | |
}, | |
{ | |
"name": "ControlList", | |
"size": 4665 | |
}, | |
{ | |
"name": "DragControl", | |
"size": 2649 | |
}, | |
{ | |
"name": "ExpandControl", | |
"size": 2832 | |
}, | |
{ | |
"name": "HoverControl", | |
"size": 4896 | |
}, | |
{ | |
"name": "IControl", | |
"size": 763 | |
}, | |
{ | |
"name": "PanZoomControl", | |
"size": 5222 | |
}, | |
{ | |
"name": "SelectionControl", | |
"size": 7862 | |
}, | |
{ | |
"name": "TooltipControl", | |
"size": 8435 | |
} | |
] | |
}, | |
{ | |
"name": "data", | |
"children": [ | |
{ | |
"name": "Data", | |
"size": 20544 | |
}, | |
{ | |
"name": "DataList", | |
"size": 19788 | |
}, | |
{ | |
"name": "DataSprite", | |
"size": 10349 | |
}, | |
{ | |
"name": "EdgeSprite", | |
"size": 3301 | |
}, | |
{ | |
"name": "NodeSprite", | |
"size": 19382 | |
}, | |
{ | |
"name": "render", | |
"children": [ | |
{ | |
"name": "ArrowType", | |
"size": 698 | |
}, | |
{ | |
"name": "EdgeRenderer", | |
"size": 5569 | |
}, | |
{ | |
"name": "IRenderer", | |
"size": 353 | |
}, | |
{ | |
"name": "ShapeRenderer", | |
"size": 2247 | |
} | |
] | |
}, | |
{ | |
"name": "ScaleBinding", | |
"size": 11275 | |
}, | |
{ | |
"name": "Tree", | |
"size": 7147 | |
}, | |
{ | |
"name": "TreeBuilder", | |
"size": 9930 | |
} | |
] | |
}, | |
{ | |
"name": "events", | |
"children": [ | |
{ | |
"name": "DataEvent", | |
"size": 2313 | |
}, | |
{ | |
"name": "SelectionEvent", | |
"size": 1880 | |
}, | |
{ | |
"name": "TooltipEvent", | |
"size": 1701 | |
}, | |
{ | |
"name": "VisualizationEvent", | |
"size": 1117 | |
} | |
] | |
}, | |
{ | |
"name": "legend", | |
"children": [ | |
{ | |
"name": "Legend", | |
"size": 20859 | |
}, | |
{ | |
"name": "LegendItem", | |
"size": 4614 | |
}, | |
{ | |
"name": "LegendRange", | |
"size": 10530 | |
} | |
] | |
}, | |
{ | |
"name": "operator", | |
"children": [ | |
{ | |
"name": "distortion", | |
"children": [ | |
{ | |
"name": "BifocalDistortion", | |
"size": 4461 | |
}, | |
{ | |
"name": "Distortion", | |
"size": 6314 | |
}, | |
{ | |
"name": "FisheyeDistortion", | |
"size": 3444 | |
} | |
] | |
}, | |
{ | |
"name": "encoder", | |
"children": [ | |
{ | |
"name": "ColorEncoder", | |
"size": 3179 | |
}, | |
{ | |
"name": "Encoder", | |
"size": 4060 | |
}, | |
{ | |
"name": "PropertyEncoder", | |
"size": 4138 | |
}, | |
{ | |
"name": "ShapeEncoder", | |
"size": 1690 | |
}, | |
{ | |
"name": "SizeEncoder", | |
"size": 1830 | |
} | |
] | |
}, | |
{ | |
"name": "filter", | |
"children": [ | |
{ | |
"name": "FisheyeTreeFilter", | |
"size": 5219 | |
}, | |
{ | |
"name": "GraphDistanceFilter", | |
"size": 3165 | |
}, | |
{ | |
"name": "VisibilityFilter", | |
"size": 3509 | |
} | |
] | |
}, | |
{ | |
"name": "IOperator", | |
"size": 1286 | |
}, | |
{ | |
"name": "label", | |
"children": [ | |
{ | |
"name": "Labeler", | |
"size": 9956 | |
}, | |
{ | |
"name": "RadialLabeler", | |
"size": 3899 | |
}, | |
{ | |
"name": "StackedAreaLabeler", | |
"size": 3202 | |
} | |
] | |
}, | |
{ | |
"name": "layout", | |
"children": [ | |
{ | |
"name": "AxisLayout", | |
"size": 6725 | |
}, | |
{ | |
"name": "BundledEdgeRouter", | |
"size": 3727 | |
}, | |
{ | |
"name": "CircleLayout", | |
"size": 9317 | |
}, | |
{ | |
"name": "CirclePackingLayout", | |
"size": 12003 | |
}, | |
{ | |
"name": "DendrogramLayout", | |
"size": 4853 | |
}, | |
{ | |
"name": "ForceDirectedLayout", | |
"size": 8411 | |
}, | |
{ | |
"name": "IcicleTreeLayout", | |
"size": 4864 | |
}, | |
{ | |
"name": "IndentedTreeLayout", | |
"size": 3174 | |
}, | |
{ | |
"name": "Layout", | |
"size": 7881 | |
}, | |
{ | |
"name": "NodeLinkTreeLayout", | |
"size": 12870 | |
}, | |
{ | |
"name": "PieLayout", | |
"size": 2728 | |
}, | |
{ | |
"name": "RadialTreeLayout", | |
"size": 12348 | |
}, | |
{ | |
"name": "RandomLayout", | |
"size": 870 | |
}, | |
{ | |
"name": "StackedAreaLayout", | |
"size": 9121 | |
}, | |
{ | |
"name": "TreeMapLayout", | |
"size": 9191 | |
} | |
] | |
}, | |
{ | |
"name": "Operator", | |
"size": 2490 | |
}, | |
{ | |
"name": "OperatorList", | |
"size": 5248 | |
}, | |
{ | |
"name": "OperatorSequence", | |
"size": 4190 | |
}, | |
{ | |
"name": "OperatorSwitch", | |
"size": 2581 | |
}, | |
{ | |
"name": "SortOperator", | |
"size": 2023 | |
} | |
] | |
}, | |
{ | |
"name": "Visualization", | |
"size": 16540 | |
} | |
] | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
treehue = (node, hue_range, fraction, rev) -> | |
# 0 <= hue_range[0] <= hue_range[1] | |
r = hue_range[1]-hue_range[0] | |
node.hue = hue_range[0]+r/2 | |
if node.children? | |
n = node.children.length | |
ri = r/n | |
child_hue_ranges = [] | |
half_n = Math.floor(n/2) | |
for i in [0...half_n] | |
child_hue_ranges.push [ | |
hue_range[0] + ri*i + ri*(1-fraction)/2, | |
hue_range[0] + ri*(i+1) - ri*(1-fraction)/2 | |
] | |
child_hue_ranges.push [ | |
hue_range[0] + ri*(i+half_n) + ri*(1-fraction)/2, | |
hue_range[0] + ri*(i+1+half_n) - ri*(1-fraction)/2 | |
] | |
# if n is odd we need to push the middle item | |
if n%2 is 1 | |
child_hue_ranges.push [ | |
hue_range[0] + ri*(half_n) + ri*(1-fraction)/2, | |
hue_range[0] + ri*(1+half_n) - ri*(1-fraction)/2 | |
] | |
child_hue_ranges.reverse() if rev | |
for child, i in node.children | |
treehue(child, child_hue_ranges[i], fraction, i % 2 is 0) | |
treelum = d3.scale.linear() | |
.range([80,30]) | |
treechroma = d3.scale.sqrt() | |
.range([0,70]) | |
svg = d3.select('svg') | |
width = svg.node().getBoundingClientRect().width | |
height = svg.node().getBoundingClientRect().height | |
# append a group for zoomable content | |
zoomable_layer = svg.append('g') | |
# define a zoom behavior | |
zoom = d3.behavior.zoom() | |
.scaleExtent([1,100]) # min-max zoom | |
.on 'zoom', () -> | |
# GEOMETRIC ZOOM | |
zoomable_layer | |
.attr | |
transform: "translate(#{zoom.translate()})scale(#{zoom.scale()})" | |
# bind the zoom behavior to the main SVG | |
svg.call(zoom) | |
vis = zoomable_layer.append('g') | |
.attr | |
class: 'vis' | |
transform: "translate(#{width/2},#{height/3-112})" | |
# [x, y, h] -> [-Math.sqrt(3)/2*x+Math.sqrt(3)/2*y, 0.5*x+0.5*y-h] | |
isometric = (_3d_p) -> [-Math.sqrt(3)/2*_3d_p[0]+Math.sqrt(3)/2*_3d_p[1], +0.5*_3d_p[0]+0.5*_3d_p[1]-_3d_p[2]] | |
parallelepipedon = (d) -> | |
d.x = 0 if not d.x? | |
d.y = 0 if not d.y? | |
d.h = 0 if not d.h? | |
d.dx = 10 if not d.dx? | |
d.dy = 10 if not d.dy? | |
d.dh = 10 if not d.dh? | |
fb = isometric [d.x, d.y, d.h], | |
mlb = isometric [d.x+d.dx, d.y, d.h], | |
nb = isometric [d.x+d.dx, d.y+d.dy, d.h], | |
mrb = isometric [d.x, d.y+d.dy, d.h], | |
ft = isometric [d.x, d.y, d.h+d.dh], | |
mlt = isometric [d.x+d.dx, d.y, d.h+d.dh], | |
nt = isometric [d.x+d.dx, d.y+d.dy, d.h+d.dh], | |
mrt = isometric [d.x, d.y+d.dy, d.h+d.dh] | |
d.iso = { | |
face_bottom: [fb, mrb, nb, mlb], | |
face_left: [mlb, mlt, nt, nb], | |
face_right: [nt, mrt, mrb, nb], | |
face_top: [ft, mrt, nt, mlt], | |
outline: [ft, mrt, mrb, nb, mlb, mlt], | |
fb: fb, | |
mlb: mlb, | |
nb: nb, | |
mrb: mrb, | |
ft: ft, | |
mlt: mlt, | |
nt: nt, | |
mrt: mrt | |
} | |
return d | |
ordering = (a,b) -> b.i - a.i | |
iso_layout = (data, shape, scale) -> | |
scale = 1 if not scale? | |
data.forEach (d) -> | |
shape(d, scale) | |
# this uses the treemap ordering in some way... (!!!) | |
# also, use the index to obtain a total ordering | |
data.sort ordering | |
path_generator = (d) -> 'M' + d.map((p)->p.join(' ')).join('L') + 'z' | |
DH = 5 | |
PAD = 4 | |
treemap = d3.layout.treemap() | |
.size([400, 400]) | |
.value((d) -> d.size) | |
.sort((a,b) -> ordering(b,a)) # same as before, but inverted | |
.padding(PAD) | |
.round(false) # bugfix: d3 wrong ordering | |
color = d3.scale.category20c() | |
correct_x = d3.scale.linear() | |
.domain([0, width]) | |
.range([0, width*1.05]) | |
correct_y = d3.scale.linear() | |
.domain([0, height]) | |
.range([0, height*3/4]) | |
d3.json 'flare.json', (tree) -> | |
walk = (n, depth) -> | |
n.depth = depth | |
n.dh = DH | |
n.h = DH*depth | |
if n.children? | |
for child in n.children | |
walk(child, depth+1) | |
n.children.sort (a,b) -> a.size - b.size | |
n.size = d3.sum n.children, (d) -> d.size | |
walk(tree, 0) | |
# depth-first enumeration | |
i = 0 | |
walk_i = (n) -> | |
if n.children? | |
for child in n.children | |
walk_i(child) | |
n.i = i | |
i += 1 | |
walk_i(tree) | |
treehue(tree, [180,720], 0.7) | |
treelum | |
.domain([0, 3]) | |
treechroma | |
.domain([0, 3]) | |
data = treemap.nodes(tree) | |
iso_layout(data, parallelepipedon) | |
data.forEach (d, i) -> | |
# save the template color | |
d.template_color = d3.hcl(d.hue, treechroma(d.depth), treelum(d.depth)) | |
pipedons = vis.selectAll('.pipedon') | |
.data(data) | |
enter_pipedons = pipedons.enter().append('g') | |
.attr | |
class: 'pipedon' | |
enter_pipedons.append('path') | |
.attr | |
class: 'iso face bottom' | |
d: (d) -> path_generator(d.iso.face_bottom) | |
enter_pipedons.append('path') | |
.attr | |
class: 'iso face left' | |
d: (d) -> path_generator(d.iso.face_left) | |
fill: (d) -> d.template_color | |
enter_pipedons.append('path') | |
.attr | |
class: 'iso face right' | |
d: (d) -> path_generator(d.iso.face_right) | |
fill: (d) -> d3.hcl(d.template_color.h, d.template_color.c, d.template_color.l-12) | |
enter_pipedons.append('path') | |
.attr | |
class: 'iso face top' | |
d: (d) -> path_generator(d.iso.face_top) | |
fill: (d) -> d3.hcl(d.template_color.h, d.template_color.c, d.template_color.l+12) | |
enter_labels_g = enter_pipedons.append('g') | |
.classed('hidden', (d) -> d.children?) | |
enter_labels = enter_labels_g.append('svg') | |
.attr | |
class: 'label' | |
enter_labels.append('text') | |
.text((d) -> d.name.toUpperCase()) | |
.attr | |
dy: '.35em' | |
.each (node) -> | |
bbox = this.getBBox() | |
bbox_aspect = bbox.width / bbox.height | |
node_bbox = {width: node.dx, height: node.dy} | |
node_bbox_aspect = node_bbox.width / node_bbox.height | |
rotate = bbox_aspect >= 1 and node_bbox_aspect < 1 or bbox_aspect < 1 and node_bbox_aspect >= 1 | |
node.label_bbox = { | |
x: bbox.x+(bbox.width-correct_x(bbox.width))/2, | |
y: bbox.y+(bbox.height-correct_y(bbox.height))/2, | |
width: correct_x(bbox.width), | |
height: correct_y(bbox.height) | |
} | |
if rotate | |
node.label_bbox = { | |
x: node.label_bbox.y, | |
y: node.label_bbox.x, | |
width: node.label_bbox.height, | |
height: node.label_bbox.width | |
} | |
d3.select(this).attr('transform', 'rotate(90) translate(0,1)') | |
enter_labels | |
.each (d) -> | |
d.iso_x = isometric([d.x+d.dx/2, d.y+d.dy/2, d.h+d.dh])[0]-d.dx/2 | |
d.iso_y = isometric([d.x+d.dx/2, d.y+d.dy/2, d.h+d.dh])[1]-d.dy/2 | |
enter_labels | |
.attr | |
x: (d) -> d.iso_x | |
y: (d) -> d.iso_y | |
width: (node) -> node.dx | |
height: (node) -> node.dy | |
viewBox: (node) -> "#{node.label_bbox.x} #{node.label_bbox.y} #{node.label_bbox.width} #{node.label_bbox.height}" | |
preserveAspectRatio: 'none' | |
fill: (d) -> d3.hcl(d.template_color.h, d.template_color.c, d.template_color.l-12) | |
enter_labels_g | |
.attr | |
transform: (d) -> "translate(#{d.iso_x+d.dx/2},#{d.iso_y+d.dy/2}) scale(1, #{1/Math.sqrt(3)}) rotate(-45) translate(#{-(d.iso_x+d.dx/2)},#{-(d.iso_y+d.dy/2)})" | |
enter_pipedons.append('path') | |
.attr | |
class: 'iso outline' | |
d: (d) -> path_generator(d.iso.outline) | |
enter_pipedons.append('title') | |
.text((d) -> d.name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.iso.outline { | |
stroke: #111; | |
fill: none; | |
vector-effect: non-scaling-stroke; | |
} | |
.label { | |
pointer-events: none; | |
text-anchor: middle; | |
font-family: Impact; | |
} | |
.pipedon:hover .label { | |
fill: black; | |
} | |
.pipedon:hover .face { | |
fill: yellow; | |
} | |
.hidden { | |
display: none; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Isometric treemap with tree colors (flare)</title> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<link rel="stylesheet" href="index.css"> | |
</head> | |
<body> | |
<svg width="960px" height="500px"></svg> | |
<script src="index.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generated by CoffeeScript 1.4.0 | |
(function() { | |
var DH, PAD, color, correct_x, correct_y, height, iso_layout, isometric, ordering, parallelepipedon, path_generator, svg, treechroma, treehue, treelum, treemap, vis, width, zoom, zoomable_layer; | |
treehue = function(node, hue_range, fraction, rev) { | |
var child, child_hue_ranges, half_n, i, n, r, ri, _i, _j, _len, _ref, _results; | |
r = hue_range[1] - hue_range[0]; | |
node.hue = hue_range[0] + r / 2; | |
if (node.children != null) { | |
n = node.children.length; | |
ri = r / n; | |
child_hue_ranges = []; | |
half_n = Math.floor(n / 2); | |
for (i = _i = 0; 0 <= half_n ? _i < half_n : _i > half_n; i = 0 <= half_n ? ++_i : --_i) { | |
child_hue_ranges.push([hue_range[0] + ri * i + ri * (1 - fraction) / 2, hue_range[0] + ri * (i + 1) - ri * (1 - fraction) / 2]); | |
child_hue_ranges.push([hue_range[0] + ri * (i + half_n) + ri * (1 - fraction) / 2, hue_range[0] + ri * (i + 1 + half_n) - ri * (1 - fraction) / 2]); | |
} | |
if (n % 2 === 1) { | |
child_hue_ranges.push([hue_range[0] + ri * half_n + ri * (1 - fraction) / 2, hue_range[0] + ri * (1 + half_n) - ri * (1 - fraction) / 2]); | |
} | |
if (rev) { | |
child_hue_ranges.reverse(); | |
} | |
_ref = node.children; | |
_results = []; | |
for (i = _j = 0, _len = _ref.length; _j < _len; i = ++_j) { | |
child = _ref[i]; | |
_results.push(treehue(child, child_hue_ranges[i], fraction, i % 2 === 0)); | |
} | |
return _results; | |
} | |
}; | |
treelum = d3.scale.linear().range([80, 30]); | |
treechroma = d3.scale.sqrt().range([0, 70]); | |
svg = d3.select('svg'); | |
width = svg.node().getBoundingClientRect().width; | |
height = svg.node().getBoundingClientRect().height; | |
zoomable_layer = svg.append('g'); | |
zoom = d3.behavior.zoom().scaleExtent([1, 100]).on('zoom', function() { | |
return zoomable_layer.attr({ | |
transform: "translate(" + (zoom.translate()) + ")scale(" + (zoom.scale()) + ")" | |
}); | |
}); | |
svg.call(zoom); | |
vis = zoomable_layer.append('g').attr({ | |
"class": 'vis', | |
transform: "translate(" + (width / 2) + "," + (height / 3 - 112) + ")" | |
}); | |
isometric = function(_3d_p) { | |
return [-Math.sqrt(3) / 2 * _3d_p[0] + Math.sqrt(3) / 2 * _3d_p[1], +0.5 * _3d_p[0] + 0.5 * _3d_p[1] - _3d_p[2]]; | |
}; | |
parallelepipedon = function(d) { | |
var fb, ft, mlb, mlt, mrb, mrt, nb, nt; | |
if (!(d.x != null)) { | |
d.x = 0; | |
} | |
if (!(d.y != null)) { | |
d.y = 0; | |
} | |
if (!(d.h != null)) { | |
d.h = 0; | |
} | |
if (!(d.dx != null)) { | |
d.dx = 10; | |
} | |
if (!(d.dy != null)) { | |
d.dy = 10; | |
} | |
if (!(d.dh != null)) { | |
d.dh = 10; | |
} | |
fb = isometric([d.x, d.y, d.h], mlb = isometric([d.x + d.dx, d.y, d.h], nb = isometric([d.x + d.dx, d.y + d.dy, d.h], mrb = isometric([d.x, d.y + d.dy, d.h], ft = isometric([d.x, d.y, d.h + d.dh], mlt = isometric([d.x + d.dx, d.y, d.h + d.dh], nt = isometric([d.x + d.dx, d.y + d.dy, d.h + d.dh], mrt = isometric([d.x, d.y + d.dy, d.h + d.dh])))))))); | |
d.iso = { | |
face_bottom: [fb, mrb, nb, mlb], | |
face_left: [mlb, mlt, nt, nb], | |
face_right: [nt, mrt, mrb, nb], | |
face_top: [ft, mrt, nt, mlt], | |
outline: [ft, mrt, mrb, nb, mlb, mlt], | |
fb: fb, | |
mlb: mlb, | |
nb: nb, | |
mrb: mrb, | |
ft: ft, | |
mlt: mlt, | |
nt: nt, | |
mrt: mrt | |
}; | |
return d; | |
}; | |
ordering = function(a, b) { | |
return b.i - a.i; | |
}; | |
iso_layout = function(data, shape, scale) { | |
if (!(scale != null)) { | |
scale = 1; | |
} | |
data.forEach(function(d) { | |
return shape(d, scale); | |
}); | |
return data.sort(ordering); | |
}; | |
path_generator = function(d) { | |
return 'M' + d.map(function(p) { | |
return p.join(' '); | |
}).join('L') + 'z'; | |
}; | |
DH = 5; | |
PAD = 4; | |
treemap = d3.layout.treemap().size([400, 400]).value(function(d) { | |
return d.size; | |
}).sort(function(a, b) { | |
return ordering(b, a); | |
}).padding(PAD).round(false); | |
color = d3.scale.category20c(); | |
correct_x = d3.scale.linear().domain([0, width]).range([0, width * 1.05]); | |
correct_y = d3.scale.linear().domain([0, height]).range([0, height * 3 / 4]); | |
d3.json('flare.json', function(tree) { | |
var data, enter_labels, enter_labels_g, enter_pipedons, i, pipedons, walk, walk_i; | |
walk = function(n, depth) { | |
var child, _i, _len, _ref; | |
n.depth = depth; | |
n.dh = DH; | |
n.h = DH * depth; | |
if (n.children != null) { | |
_ref = n.children; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
child = _ref[_i]; | |
walk(child, depth + 1); | |
} | |
n.children.sort(function(a, b) { | |
return a.size - b.size; | |
}); | |
return n.size = d3.sum(n.children, function(d) { | |
return d.size; | |
}); | |
} | |
}; | |
walk(tree, 0); | |
i = 0; | |
walk_i = function(n) { | |
var child, _i, _len, _ref; | |
if (n.children != null) { | |
_ref = n.children; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
child = _ref[_i]; | |
walk_i(child); | |
} | |
} | |
n.i = i; | |
return i += 1; | |
}; | |
walk_i(tree); | |
treehue(tree, [180, 720], 0.7); | |
treelum.domain([0, 3]); | |
treechroma.domain([0, 3]); | |
data = treemap.nodes(tree); | |
iso_layout(data, parallelepipedon); | |
data.forEach(function(d, i) { | |
return d.template_color = d3.hcl(d.hue, treechroma(d.depth), treelum(d.depth)); | |
}); | |
pipedons = vis.selectAll('.pipedon').data(data); | |
enter_pipedons = pipedons.enter().append('g').attr({ | |
"class": 'pipedon' | |
}); | |
enter_pipedons.append('path').attr({ | |
"class": 'iso face bottom', | |
d: function(d) { | |
return path_generator(d.iso.face_bottom); | |
} | |
}); | |
enter_pipedons.append('path').attr({ | |
"class": 'iso face left', | |
d: function(d) { | |
return path_generator(d.iso.face_left); | |
}, | |
fill: function(d) { | |
return d.template_color; | |
} | |
}); | |
enter_pipedons.append('path').attr({ | |
"class": 'iso face right', | |
d: function(d) { | |
return path_generator(d.iso.face_right); | |
}, | |
fill: function(d) { | |
return d3.hcl(d.template_color.h, d.template_color.c, d.template_color.l - 12); | |
} | |
}); | |
enter_pipedons.append('path').attr({ | |
"class": 'iso face top', | |
d: function(d) { | |
return path_generator(d.iso.face_top); | |
}, | |
fill: function(d) { | |
return d3.hcl(d.template_color.h, d.template_color.c, d.template_color.l + 12); | |
} | |
}); | |
enter_labels_g = enter_pipedons.append('g').classed('hidden', function(d) { | |
return d.children != null; | |
}); | |
enter_labels = enter_labels_g.append('svg').attr({ | |
"class": 'label' | |
}); | |
enter_labels.append('text').text(function(d) { | |
return d.name.toUpperCase(); | |
}).attr({ | |
dy: '.35em' | |
}).each(function(node) { | |
var bbox, bbox_aspect, node_bbox, node_bbox_aspect, rotate; | |
bbox = this.getBBox(); | |
bbox_aspect = bbox.width / bbox.height; | |
node_bbox = { | |
width: node.dx, | |
height: node.dy | |
}; | |
node_bbox_aspect = node_bbox.width / node_bbox.height; | |
rotate = bbox_aspect >= 1 && node_bbox_aspect < 1 || bbox_aspect < 1 && node_bbox_aspect >= 1; | |
node.label_bbox = { | |
x: bbox.x + (bbox.width - correct_x(bbox.width)) / 2, | |
y: bbox.y + (bbox.height - correct_y(bbox.height)) / 2, | |
width: correct_x(bbox.width), | |
height: correct_y(bbox.height) | |
}; | |
if (rotate) { | |
node.label_bbox = { | |
x: node.label_bbox.y, | |
y: node.label_bbox.x, | |
width: node.label_bbox.height, | |
height: node.label_bbox.width | |
}; | |
return d3.select(this).attr('transform', 'rotate(90) translate(0,1)'); | |
} | |
}); | |
enter_labels.each(function(d) { | |
d.iso_x = isometric([d.x + d.dx / 2, d.y + d.dy / 2, d.h + d.dh])[0] - d.dx / 2; | |
return d.iso_y = isometric([d.x + d.dx / 2, d.y + d.dy / 2, d.h + d.dh])[1] - d.dy / 2; | |
}); | |
enter_labels.attr({ | |
x: function(d) { | |
return d.iso_x; | |
}, | |
y: function(d) { | |
return d.iso_y; | |
}, | |
width: function(node) { | |
return node.dx; | |
}, | |
height: function(node) { | |
return node.dy; | |
}, | |
viewBox: function(node) { | |
return "" + node.label_bbox.x + " " + node.label_bbox.y + " " + node.label_bbox.width + " " + node.label_bbox.height; | |
}, | |
preserveAspectRatio: 'none', | |
fill: function(d) { | |
return d3.hcl(d.template_color.h, d.template_color.c, d.template_color.l - 12); | |
} | |
}); | |
enter_labels_g.attr({ | |
transform: function(d) { | |
return "translate(" + (d.iso_x + d.dx / 2) + "," + (d.iso_y + d.dy / 2) + ") scale(1, " + (1 / Math.sqrt(3)) + ") rotate(-45) translate(" + (-(d.iso_x + d.dx / 2)) + "," + (-(d.iso_y + d.dy / 2)) + ")"; | |
} | |
}); | |
enter_pipedons.append('path').attr({ | |
"class": 'iso outline', | |
d: function(d) { | |
return path_generator(d.iso.outline); | |
} | |
}); | |
return enter_pipedons.append('title').text(function(d) { | |
return d.name; | |
}); | |
}); | |
}).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment