This block is a recreation, inspired from @JacobJoaquin, and is more or less data art.
This block is forked from mbostock's block: Nested Treemap, and hence vizualises the treemap of the famous flare.csv. Hold on your mouse on a cell for some details.
Even if this block is a recreation, the technique can be used to distord the treemap in order to fit a particular contour/shape (cf. d3-shaped Treemap).
xxxxxxxxxx
<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>
https://d3js.org/d3.v4.min.js
https://d3js.org/d3-scale-chromatic.v1.min.js