While studying how d3-quadtree was built, I wanted to be able to interactively modify a quadtree.
This is a remix of Mike Bostock's example.
In diving through d3-quadtree, one finds "fun" bits of code like this one.
| license: gpl-3.0 | |
| height: 960 |
While studying how d3-quadtree was built, I wanted to be able to interactively modify a quadtree.
This is a remix of Mike Bostock's example.
In diving through d3-quadtree, one finds "fun" bits of code like this one.
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <style> | |
| svg { | |
| cursor: crosshair; | |
| position: absolute; | |
| } | |
| circle { | |
| fill: rgba(240,0,0,1); | |
| } | |
| rect { | |
| fill: none; | |
| stroke-dasharray: 5, 5; | |
| stroke: rgba(0,0,0,1); | |
| stroke-width: 1px; | |
| } | |
| rect.leaf { | |
| stroke-dasharray: none; | |
| fill: rgba(0,0,0,0.1); | |
| } | |
| rect.leaf:hover { | |
| fill: rgba(0,0,240,0.3); | |
| } | |
| text { | |
| font-family: Helvetica; | |
| font-size: 24px; | |
| font-weight: bold; | |
| text-anchor: middle; | |
| } | |
| </style> | |
| <svg id="quadtree" width="960" height="960"></svg> | |
| <script src="https://d3js.org/d3.v4.js"></script> | |
| <script> | |
| var svg = d3.select("#quadtree"); | |
| var width = 960, | |
| height = 960, | |
| radius = 1.5; | |
| var text = svg.append("text") | |
| .text("Click to add a point.") | |
| .attr("x", width / 2) | |
| .attr("y", height / 2); | |
| svg.on("click", click); | |
| let points = [], | |
| quads = [], | |
| extent = [[1, 1],[width - 1, height - 1]]; | |
| var quadtree = d3.quadtree() | |
| .x(function(d) { return d.x; }) | |
| .y(function(d) { return d.y; }); | |
| function click() { | |
| if (text) { | |
| text.remove(); | |
| text = undefined; | |
| } | |
| var mouse = d3.mouse(this), | |
| newPoint = {x: mouse[0], y: mouse[1]}; | |
| points.push(newPoint); | |
| quadtree.add(newPoint); | |
| quads = []; | |
| // Pre-order traversal of the quadtree. | |
| quadtree.visit(function(node, x0, y0, x1, y1) { | |
| if (node.data) { // Leaf node to be rendered. | |
| quads.push({ | |
| "data": node.data, | |
| "x0": x0, | |
| "y0": y0, | |
| "x1": x1, | |
| "y1": y1 | |
| }); | |
| } | |
| }); | |
| extent = quadtree.extent(); | |
| quads.push({ | |
| "x0": extent[0][0], | |
| "y0": extent[0][1], | |
| "x1": extent[1][0], | |
| "y1": extent[1][1] | |
| }); | |
| update(); | |
| } | |
| function update() { | |
| var circle, rectangle; | |
| circle = svg.selectAll("circle") | |
| .data(points); | |
| circle.exit().remove(); | |
| circle.enter().append("circle") | |
| .attr("cx", function(d) { return d.x; }) | |
| .attr("cy", function(d) { return d.y; }) | |
| .attr("r", radius) | |
| .merge(circle); | |
| // Rather than attempt to "key" quads, simply redraw them all. | |
| rectangle = svg.selectAll("rect").remove(); | |
| rectangle = svg.selectAll("rect") | |
| .data(quads); | |
| rectangle.exit().remove(); | |
| rectangle.enter().append("rect") | |
| .attr("x", function(d) { return d.x0; }) | |
| .attr("y", function(d) { return d.y0; }) | |
| .attr("width", function(d) { return d.x1 - d.x0; }) | |
| .attr("height", function(d) { return d.y1 - d.y0; }) | |
| .classed("leaf", function(d) { return d.data; }) | |
| .merge(circle); | |
| } | |
| </script> |
TODO
Right click on square to delete a point from the quadtree.