Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active October 15, 2015 20:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nitaku/b82c356f1969911042d6 to your computer and use it in GitHub Desktop.
Save nitaku/b82c356f1969911042d6 to your computer and use it in GitHub Desktop.
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);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment