|
<html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<title>Wavy treemap</title> |
|
<meta content="D3's source, displayed as a wavy treemap" name="description"> |
|
<style> |
|
path { |
|
fill:white; |
|
} |
|
.node--hover path { |
|
fill: lightgrey; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<svg width="960" height="500"></svg> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> |
|
<script> |
|
var WITH_TRANSITION = true, |
|
WITHOUT_TRANSITION = false; |
|
|
|
//begin: distrosion conf. |
|
var sampling = 5, |
|
amplitude = 10, |
|
dxAlongY, //function defining the distorsion along the y-axis; set later |
|
dyAlongX; //function defining the distorsion along the x-axis; set later |
|
//end: distorsion conf. |
|
|
|
//begin: drawing conf. |
|
var margin = {top: 5, right: 5, bottom: 5, left: 5}, |
|
svgSize = 500, |
|
svgWidth = size, |
|
svgHeight = size, |
|
size = svgSize - margin.left - margin.top - 2*amplitude, |
|
width = size, |
|
height = size; |
|
//end: drawing conf. |
|
|
|
var drawingArea = d3.select("svg").append("g").attr("transform", "translate(15,15)") |
|
|
|
var format = d3.format(",d"); |
|
var stratify = d3.stratify() |
|
.parentId(function(d) { return d.id.substring(0, d.id.lastIndexOf(".")); }); |
|
var treemap = d3.treemap() |
|
.size([width, height]) |
|
.paddingOuter(3) |
|
.paddingInner(1) |
|
.round(true); |
|
d3.csv("flare.csv", function(error, data) { |
|
if (error) throw error; |
|
var root = stratify(data) |
|
.sum(function(d) { return d.value; }) |
|
.sort(function(a, b) { return b.height - a.height || b.value - a.value; }); |
|
treemap(root); |
|
var cell = drawingArea |
|
.selectAll(".node") |
|
.data(root.descendants()) |
|
.enter().append("g") |
|
.attr("transform", function(d) { return "translate(" + d.x0 + "," + d.y0 + ")"; }) |
|
.attr("class", "node") |
|
.each(function(d) { d.node = this; }) |
|
.on("mouseover", hovered(true)) |
|
.on("mouseout", hovered(false)) |
|
.style("stroke", function(d) { return d3.interpolateGreys(1-d.depth/8); }); |
|
cell.append("title") |
|
.text(function(d) { return d.id + "\n" + format(d.value); }); |
|
|
|
cell = cell.append("path") |
|
.attr("id", function(d) { return "path-" + d.id; }) |
|
|
|
//draw the default, linear, treemap |
|
distord(cell, dxAlongY_linear, dyAlongX_linear, WITHOUT_TRANSITION); |
|
|
|
|
|
|
|
dyAlongX = dyAlongX_linear; // no distrorsion |
|
// uncomment below code for other distorsion along the x-axis |
|
dyAlongX = dyAlongX_wave; // wave distrosion |
|
|
|
dxAlongY = dxAlongY_linear; // no distorsion |
|
// uncomment below code for other distorsion along the y-axis |
|
dxAlongY = dxAlongY_wave; // wave distrosion |
|
// dxAlongY = dxAlongY_circle; // cicle-based distorsion |
|
// dxAlongY = dxAlongY_thirdCircle; // centered-1/3height-circle distorsion |
|
|
|
//transition to the distorted treemap |
|
distord(cell, dxAlongY, dyAlongX, WITH_TRANSITION); |
|
}); |
|
|
|
/******************************************/ |
|
/* draw path-based cells instead of rects */ |
|
/* by interpolating lines into pathes */ |
|
/******************************************/ |
|
|
|
function distord(cellSelection, dxAlongY, dyAlongX, withTransition) { |
|
var duration = withTransition? 5000 : 0; |
|
|
|
|
|
//begin: pre-compute distorsions for optimization purpose |
|
var dyAlongXs = {}, dxAlongYs = {}; |
|
//begin: store y-deltas along the x-axis |
|
var x=0; |
|
while (x<=width) { |
|
dyAlongXs[x] = dyAlongX(x); |
|
x+=sampling; |
|
} |
|
//end: store y-deltas along the x-axis |
|
//begin: store x-deltas along the y-axis |
|
var y = 0; |
|
while (y<=height) { |
|
dxAlongYs[y] = dxAlongY(y); |
|
y+=sampling; |
|
} |
|
//end: store x-deltas along the y-axis |
|
//end: pre-compute distorsions for optimization purpose |
|
|
|
cellSelection.transition().duration(duration).attr("d", function(d) { |
|
var cellWidth = (d.x1 - d.x0), cellHeight = (d.y1 - d.y0); |
|
var absX, absY, //absolute x,y |
|
relX, relY; //relative x,y, because the "g.cell" cell is already translated |
|
var dyAlongX0 = dyAlongX(d.x0), dxAlongY0 = dxAlongY(d.y0), |
|
dyAlongX1 = dyAlongX(d.x1), dxAlongY1 = dxAlongY(d.y1); |
|
var path; |
|
path = "M" + [0+dxAlongY0,0+dyAlongX0]; |
|
//begin: path of distorted top line (r->l) |
|
absX = Math.ceil(d.x0/sampling)*sampling; //first sampling-based x |
|
relX = absX - d.x0; |
|
while (absX<d.x1) { |
|
path += "L" + [relX+dxAlongY0, 0+dyAlongXs[absX]]; |
|
absX += sampling; |
|
relX += sampling; |
|
} |
|
path += "L" + [cellWidth+dxAlongY0, 0+dyAlongX1]; |
|
//end: path of distorted top line (r->l) |
|
//begin: path of distorted right line (t->b) |
|
absY = Math.ceil(d.y0/sampling)*sampling; //first sampling-based y |
|
relY = absY-d.y0; |
|
while (absY<d.y1) { |
|
path += "L" + [cellWidth+dxAlongYs[absY], relY+dyAlongX1]; |
|
absY += sampling; |
|
relY += sampling; |
|
} |
|
path += "L" + [cellWidth+dxAlongY1, cellHeight+dyAlongX1]; |
|
//end: path of distorted right line (t->b) |
|
//begin: path of distorted bottom line (l->r) |
|
absX = Math.floor(d.x1/sampling)*sampling; //first sampling-based x |
|
relX = absX - d.x0; |
|
while (absX>d.x0) { |
|
path += "L" + [relX+dxAlongY1, cellHeight+dyAlongXs[absX]]; |
|
absX -= sampling; |
|
relX -= sampling; |
|
} |
|
path += "L" + [0+dxAlongY1, cellHeight+dyAlongX0]; |
|
//end: path of distorted bottm line (l->r) |
|
//begin: path of distorted left line (b->t) |
|
absY = Math.floor(d.y1/sampling)*sampling; //first sampling-based y |
|
relY = absY - d.y0; |
|
while (absY>d.y0) { |
|
path += "L" + [0+dxAlongYs[absY], relY+dyAlongX0]; |
|
absY -= sampling; |
|
relY -= sampling; |
|
} |
|
path += "L" + [0+dxAlongY0, 0+dyAlongX0]; |
|
//end: path of distorted left line (b->t) |
|
return path+"Z"; |
|
}); |
|
} |
|
|
|
function hovered(hover) { |
|
return function(d) { |
|
d3.selectAll(d.ancestors().map(function(d) { return d.node; })) |
|
.classed("node--hover", hover); |
|
}; |
|
}; |
|
|
|
/******************************/ |
|
/* definitions of distorsions */ |
|
/******************************/ |
|
|
|
var dyAlongX_linear = function (y) { |
|
return 0; |
|
} |
|
|
|
var dyAlongX_wave = function(x) { |
|
return amplitude*Math.sin(x/width*Math.PI*2*3+Math.PI); |
|
}; |
|
|
|
var dxAlongY_linear = function (y) { |
|
return 0; |
|
} |
|
|
|
var dxAlongY_wave = function (y) { |
|
return amplitude*Math.sin(y/height*Math.PI*2*3); |
|
}; |
|
|
|
var dxAlongY_circle = function(y) { |
|
return (Math.sqrt(Math.pow(height/2,2) - Math.pow(y-height/2,2))); |
|
}; |
|
var dxAlongY_thirdCircle = function(y) { |
|
if (y>height/3 && y<2*height/3) { |
|
return Math.sqrt(Math.pow(height/6,2) - Math.pow(y-height/2,2)); |
|
} else { |
|
return 0; |
|
} |
|
}; |
|
</script> |
|
</body> |
|
</html> |