const d3 = window.d3;
const appDiv = document.getElementById("app");
const parentStyle = `
display: flex;
width: 900px;
height: 500px;
justify-content: center;
align-items: center;
font-family: sans-serif;
font-weight: 100;
`;
const width = 400;
const height = 400;
const childStyle = `
width: ${width}px;
height: ${height}px;
`;
appDiv.innerHTML = `
`;
const points = [
{
group: "a",
x: 3,
y: 2
},
{
group: "a",
x: 6,
y: 8
},
{
group: "a",
x: 8,
y: 1
},
{
group: "b",
x: 1,
y: 5
},
{
group: "b",
x: 9,
y: 6
}
];
const xExtent = [0, 10];
const xRange = [0, width];
const xScale = d3
.scaleLinear()
.domain(xExtent)
.range(xRange);
const yExtent = [0, 10];
const yRange = [height, 0];
const yScale = d3
.scaleLinear()
.domain(yExtent)
.range(yRange);
const line = d3
.line()
.x(d => xScale(d.x))
.y(d => yScale(d.y));
const color = group => (group === "a" ? "blue" : "red");
const opacity = 0.3;
const nested = d3
.nest()
.key(d => d.group)
.entries(points)
.map(point => {
return {
...point,
pathString: line(point.values)
};
});
const paths = nested.map(point => {
const { key } = point;
const pathStyle = `
fill: none;
stroke: ${color(point.key)};
opacity: ${opacity};
`;
const html = ``;
return {
...point,
html
};
});
const circles = points.map(point => {
const { x, y, group } = point;
const transform = `translate(${xScale(x)}, ${yScale(y)})`;
const style = `fill: ${color(group)}; opacity: ${opacity}`;
return `
`;
});
// Based on: https://gist.github.com/mbostock/4163057
// Sample the SVG path string "d" uniformly with the specified precision.
function getPoints(d, step) {
var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("d", d);
var length = path.getTotalLength();
return d3.range(0, length, step).map(function(t) {
var point = path.getPointAtLength(t);
return { x: point.x, y: point.y, t: t / length };
});
}
const chart = (div, withExtraPoints) => {
let allPoints = points.map(point => {
const arr = [xScale(point.x), yScale(point.y)];
arr.group = point.group;
return arr;
});
if (withExtraPoints) {
const linePoints = paths
.map(path => {
const { pathString, key } = path;
const extraPoints = getPoints(pathString, 20)
.filter(p => p.x && p.y)
.map(point => {
const arr = [point.x, point.y];
arr.group = key;
return arr;
});
return extraPoints;
})
.flat();
allPoints = allPoints.concat(linePoints);
}
const delaunay = d3.Delaunay.from(allPoints);
const voronoi = delaunay.voronoi();
const cells = allPoints.map((point, i) => {
const cellStyle = `
fill: none;
stroke: black;
pointer-events: all;
opacity: 0.2;
`;
const { group } = point;
const d = voronoi.renderCell(i);
if (!d) {
return "";
}
return ``;
});
const childNode = div;
childNode.node().innerHTML = `
`;
div
.selectAll(".cell")
.on("mouseover", function() {
const { group } = this.dataset;
childNode.selectAll(`.data`).classed("hover", false);
childNode.selectAll(`.group-${group}`).classed("hover", true);
})
.on("mouseout", function() {
childNode.selectAll(`.data`).classed("hover", false);
});
};
const before = d3.select("#before");
const after = d3.select("#after");
chart(before);
chart(after, true);