chart = {
function retrieveNodesAndDepth(quadtree) {
const nodes = [];
let maxdepth = 0;
quadtree.root().depth = 0;
quadtree.visit((node, x1, y1, x2, y2) => {
node.x1 = x1;
node.y1 = y1;
node.x2 = x2;
node.y2 = y2;
nodes.push(node);
for (var i=0; i < node.length; i++) {
if (node[i]) {
node[i].depth = node.depth+1;
maxdepth = (node[i].depth > maxdepth) ? node[i].depth : maxdepth;
}
}
return false;
});
return {"nodes": nodes, "depth": maxdepth};
}
function drawSelection(quadtree) {
const pt = d3.select("#pt");
const x = pt.attr('cx'), y = pt.attr('cy');
quadtree.root().mindist = 0;
const bestqueue = new Array(quadtree.root());
const resultqueue = [];
const scannedqueue = [];
knearest(bestqueue, resultqueue, x, y, k, scannedqueue);
resultqueue.forEach((d) => d.data.selected=true);
scannedqueue.forEach((d) => d.length ? d.scanned=true : d.data.scanned = true);
point.classed("scanned", (d) => d.scanned);
point.classed("selected", (d) => d.selected);
rect.style('fill', (d) => d.scanned ? color(d.depth) : 'none');
rect.classed('scanned', (d) => d.scanned);
resultqueue.forEach((d) => d.data.selected=false);
scannedqueue.forEach((d) => d.length ? d.scanned=false : d.data.scanned = false);
}
const div = DOM.element("div");
d3.select(div).append("style").text(`
.point {
fill: #999;
stroke: #fff;
}
.point.scanned {
fill: orange;
stroke: #000;
}
.point.selected {
fill: red;
}
.node {
fill: none;
stroke: none;
}
.node.scanned {
stroke: #333;
}
`);
const height = width;
const data = d3.range(n).map(_ => [Math.random() * width, Math.random() * height]);
const quadtree = d3.quadtree()
.extent([[-1, -1], [width + 1, height + 1]])
.addAll(data);
const {nodes, depth} = retrieveNodesAndDepth(quadtree);
const color = d3.scaleLinear()
.domain([0, depth])
.range(["#fff", "#555"]);
const svg = d3.select(div).append("svg")
.attr("width", width)
.attr("height", height)
.on("mouseover", (e) => {
var xy = d3.pointer(e);
svg.selectAll("#pt")
.attr("cx", xy[0])
.attr("cy", xy[1]);
drawSelection(quadtree);
});
const rect = svg.selectAll(".node")
.data(nodes)
.enter().append("rect")
.attr("class", "node")
.attr("x", function(d) { return d.x1; })
.attr("y", function(d) { return d.y1; })
.attr("width", function(d) { return d.x2 - d.x1; })
.attr("height", function(d) { return d.y2 - d.y1; });
const point = svg.selectAll(".point")
.data(data)
.enter().append("circle")
.attr("class", "point")
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; })
.attr("r", 3);
svg.append("circle")
.attr("id", "pt")
.attr("r", 3)
.attr("cx", width/2)
.attr("cy", height/2)
.style("fill", "black");
return div;
}