Skip to content

Instantly share code, notes, and snippets.

@meowteusz
Last active June 11, 2019 03:42
Show Gist options
  • Save meowteusz/dd34949868808647aad759a1f52548d5 to your computer and use it in GitHub Desktop.
Save meowteusz/dd34949868808647aad759a1f52548d5 to your computer and use it in GitHub Desktop.
D3 Clickable Quadtrees

Build as you go quadtrees! Click anywhere on the svg to add a point to the quadtree. Rectangles showing partitions automatically draw around it to show which node the point is stored in. Tree is also responsive and can resize to desired viewport width and height.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Clickable Quadtree</title>
<style>
cirlce {
fill: #4682b4;
fill-opacity: 0.4;
}
.node {
fill: none;
stroke: #ccc;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<svg width="960" height="500"></svg>
</body>
<script src="https://d3js.org/d3.v4.js"></script>
<script>
let svg = d3.select("svg"),
width = 960,
height = 500,
radius = 4;
let dataset = d3.range(2).map( () => [Math.floor(Math.random() * 1024), Math.floor(Math.random() * 1024)]);
let quadtree = d3.quadtree()
.extent([[0, 0], [width, height]])
.addAll(dataset);
svg.selectAll(".node")
.data(nodes(quadtree))
.enter()
.append("rect")
.transition()
.duration(1000)
.attr("class", "node")
.attr("x", d => (d.x0/1024)*width)
.attr("y", d => (d.y0/1024)*height)
.attr("width", d => ((width/1024)*d.y1) - ((width/1024)*d.y0))
.attr("height", d => ((height/1024)*d.x1) - ((height/1024)*d.x0));
let xScale = d3.scaleLinear()
.domain([0, 1024])
.range([0, width]),
yScale = d3.scaleLinear()
.domain([0, 1024])
.range([0, height]);
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", d => xScale(d[0]))
.attr("cy", d => yScale(d[1]))
.attr("r", radius)
.style("fill", "#4682b4");
svg.on("click", function () {
let coords = d3.mouse(this);
let newData = [
Math.floor(xScale.invert(coords[0])),
Math.floor(yScale.invert(coords[1]))
];
dataset.push(newData);
quadtree.add(newData);
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", d => xScale(d[0]))
.attr("cy", d => yScale(d[1]))
.attr("r", radius)
.style("fill", "#4682b4");
svg.selectAll(".node").remove();
svg.selectAll(".node")
.data(nodes(quadtree))
.enter()
.append("rect")
.attr("class", "node")
.attr("x", d => (d.x0 / 1024) * width)
.attr("y", d => (d.y0 / 1024) * height)
.attr("width", d => ((width / 1024) * d.y1) - ((width / 1024) * d.y0))
.attr("height", d => ((height / 1024) * d.x1) - ((height / 1024) * d.x0));
});
function nodes(quadtree) {
let nodes = [];
quadtree.visit(function(node, x0, y0, x1, y1) {
node.x0 = x0,
node.y0 = y0,
node.x1 = x1,
node.y1 = y1;
nodes.push(node);
});
return nodes;
}
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment