An isometric treemap using tree colors to help in differentiating subtrees and perceiving hierarchy depth.
forked from nitaku's block: Isometric treemap with tree colors (flare)
| license: mit |
An isometric treemap using tree colors to help in differentiating subtrees and perceiving hierarchy depth.
forked from nitaku's block: Isometric treemap with tree colors (flare)
| { | |
| "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 | |
| } | |
| ] | |
| } | |
| ] | |
| } |
| 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) |
| .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; | |
| } |
| <!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> |
| // 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); |