This block is a continuation of a previous block. Compared to the previous block (which was a recreation), this one aims to be a more realistic use case, and illustrates how one can distord a treemap in order to fit a particular shape.
This block vizualises the treemap of the D3'source code (data comes from mbostock's block: D3 Source Treemap). Hold on your mouse on a cell for some details.
xxxxxxxxxx
<html>
<head>
<meta charset="utf-8">
<title>Wavy treemap</title>
<meta content="D3's source, displayed as a distorded treemap" name="description">
<style>
#layouter {
position: relative;
}
#treemap {
position: absolute;
top: 61px;
left: 0;
}
#treemap path {
fill:white;
}
#treemap .node--hover {
fill: lightgrey;
}
</style>
</head>
<body>
<div id="layouter">
<svg version="1.1" xmlns="https://www.w3.org/2000/svg" width="500" height="500" viewBox="-10 -10 116 111">
<clipPath id="clip">
<path d="M0,0h7.75a45.5,45.5 0 1 1 0,91h-7.75v-20h7.75a25.5,25.5 0 1 0 0,-51h-7.75zm36.2510,0h32a27.75,27.75 0 0 1 21.331,45.5a27.75,27.75 0 0 1 -21.331,45.5h-32a53.6895,53.6895 0 0 0 18.7464,-20h13.2526a7.75,7.75 0 1 0 0,-15.5h-7.75a53.6895,53.6895 0 0 0 0,-20h7.75a7.75,7.75 0 1 0 0,-15.5h-13.2526a53.6895,53.6895 0 0 0 -18.7464,-20z"/>
</clipPath>
<linearGradient id="gradient-1" gradientUnits="userSpaceOnUse" x1="7" y1="64" x2="50" y2="107">
<stop offset="0" stop-color="#f9a03c"/>
<stop offset="1" stop-color="#f7974e"/>
</linearGradient>
<linearGradient id="gradient-2" gradientUnits="userSpaceOnUse" x1="2" y1="-2" x2="87" y2="84">
<stop offset="0" stop-color="#f26d58"/>
<stop offset="1" stop-color="#f9a03c"/>
</linearGradient>
<linearGradient id="gradient-3" gradientUnits="userSpaceOnUse" x1="45" y1="-10" x2="108" y2="53">
<stop offset="0" stop-color="#b84e51"/>
<stop offset="1" stop-color="#f68e48"/>
</linearGradient>
<g clip-path="url(#clip)">
<path d="M-100,-102m-28,0v300h300z" fill="url(#gradient-1)"/>
<path d="M-100,-102m28,0h300v300z" fill="url(#gradient-3)"/>
<path d="M-100,-102l300,300" fill="none" stroke="url(#gradient-2)" stroke-width="40"/>
</g>
</svg>
<svg id="treemap" width="960" height="500">
</svg>
<div>
<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 svgSize = 392,
svgWidth = svgSize,
svgHeight = svgSize,
size = svgSize,
width = size,
height = size;
//end: drawing conf.
var drawingArea = d3.select("#treemap").append("g");
var format = d3.format(",d");
var treemap = d3.treemap()
.size([width, height])
.paddingOuter(3)
.paddingInner(1)
.round(true);
var type = function(d) {
d.size = +d.size;
return d;
};
d3.csv("d3.csv", type,function(error, data) {
if (error) throw error;
var root = d3.stratify()
.id(function(d) { return d.path; })
.parentId(function(d) { return d.path.substring(0, d.path.lastIndexOf("/")); })
(data)
.sum(function(d) { return d.size; })
.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("path")
.attr("class", "node")
.each(function(d) { d.node = this; })
.on("mouseover", hovered(true))
.on("mouseout", hovered(false))
.style("stroke", function(d) { return d3.interpolateOranges(1-d.depth/8); });
cell.append("title")
.text(function(d) { return d.id + "\n" + format(d.value) + " loc"; });
// draw the iniital, linear, treemap
distord(cell, dxAlongY_linear, dyAlongX_linear, WITHOUT_TRANSITION);
//transition to the d3-distorted treemap
distord(cell, dxAlongY_d3, dyAlongX_linear, 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 absX, absY; //absolute x,y
var dyAlongX0 = dyAlongX(d.x0), dxAlongY0 = dxAlongY(d.y0),
dyAlongX1 = dyAlongX(d.x1), dxAlongY1 = dxAlongY(d.y1);
var path;
path = "M" + [d.x0+dxAlongY0, d.y0+dyAlongX0];
//begin: path of distorted top line (r->l)
absX = Math.ceil(d.x0/sampling)*sampling; //first sampling-based x
while (absX<d.x1) {
path += "L" + [absX+dxAlongY0, d.y0+dyAlongXs[absX]];
absX += sampling;
}
path += "L" + [d.x1+dxAlongY0, d.y0+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
while (absY<d.y1) {
path += "L" + [d.x1+dxAlongYs[absY], absY+dyAlongX1];
absY += sampling;
}
path += "L" + [d.x1+dxAlongY1, d.y1+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
while (absX>d.x0) {
path += "L" + [absX+dxAlongY1,d.y1+dyAlongXs[absX]];
absX -= sampling;
}
path += "L" + [d.x0+dxAlongY1, d.y1+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
while (absY>d.y0) {
path += "L" + [d.x0+dxAlongYs[absY], absY+dyAlongX0];
absY -= sampling;
}
path += "L" + [d.x0+dxAlongY0, d.y0+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 available distrositions */
/******************************************/
var dyAlongX_linear = function (y) {
return 0;
}
dxAlongY_linear = function (y) {
return 480;
}
dxAlongY_d3 = function (y) {
var r = 150,
cy = 120,
cx = 370;
if (y<height/2 ) {
return Math.sqrt(Math.pow(r,2) - Math.pow(y-cy,2)) + cx;
} else {
return Math.sqrt(Math.pow(r,2) - Math.pow(y-(height-cy),2)) + cx;
}
}
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js
https://d3js.org/d3-scale-chromatic.v1.min.js