From c863e59b6c85fa134844cb7758e31bd0b5126f3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Tue, 7 Jul 2020 16:15:02 +0200 Subject: [PATCH] findInCircle closes #30 --- README.md | 5 +++++ src/findInCircle.js | 22 ++++++++++++++++++++++ src/quadtree.js | 2 ++ test/findInCircle-test.js | 21 +++++++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 src/findInCircle.js create mode 100644 test/findInCircle-test.js diff --git a/README.md b/README.md index 05692ff..2eed0f9 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,11 @@ Returns the total number of data in the quadtree. Returns the datum closest to the position ⟨*x*,*y*⟩ with the given search *radius*. If *radius* is not specified, it defaults to infinity. If there is no datum within the search area, returns undefined. +# quadtree.findInCircle(x, y[, radius][, filter]) + [<>](https://github.com/d3/d3-quadtree/blob/master/src/findInCircle.js "Source") + +Returns all the data within the given search *radius* of the position ⟨*x*,*y*⟩ that satisfy the filter *filter*, if specified. If *radius* is not specified, it defaults to infinity. If there is no acceptable datum within the search area, returns an empty array. + # quadtree.visit(callback) [<>](https://github.com/d3/d3-quadtree/blob/master/src/visit.js "Source") diff --git a/src/findInCircle.js b/src/findInCircle.js new file mode 100644 index 0000000..1ad3a10 --- /dev/null +++ b/src/findInCircle.js @@ -0,0 +1,22 @@ +export default function(x, y, radius, filter) { + const quadtree = this, + result = [], + radius2 = radius * radius, + accept = filter + ? d => filter(d) && result.push(d) + : d => result.push(d); + + quadtree.visit(function(node, x1, y1, x2, y2) { + if (node.length) { + return x1 >= x + radius || y1 >= y + radius || x2 < x - radius || y2 < y - radius; + } + + const dx = +quadtree._x.call(null, node.data) - x, + dy = +quadtree._y.call(null, node.data) - y; + if (dx * dx + dy * dy < radius2) { + do { accept(node.data); } while (node = node.next); + } + }); + + return result; +} diff --git a/src/quadtree.js b/src/quadtree.js index 5d58593..2bad3d4 100644 --- a/src/quadtree.js +++ b/src/quadtree.js @@ -3,6 +3,7 @@ import tree_cover from "./cover.js"; import tree_data from "./data.js"; import tree_extent from "./extent.js"; import tree_find from "./find.js"; +import tree_findInCircle from "./findInCircle.js"; import tree_remove, {removeAll as tree_removeAll} from "./remove.js"; import tree_root from "./root.js"; import tree_size from "./size.js"; @@ -63,6 +64,7 @@ treeProto.cover = tree_cover; treeProto.data = tree_data; treeProto.extent = tree_extent; treeProto.find = tree_find; +treeProto.findInCircle = tree_findInCircle; treeProto.remove = tree_remove; treeProto.removeAll = tree_removeAll; treeProto.root = tree_root; diff --git a/test/findInCircle-test.js b/test/findInCircle-test.js new file mode 100644 index 0000000..c0ea45a --- /dev/null +++ b/test/findInCircle-test.js @@ -0,0 +1,21 @@ +var tape = require("tape"), + d3_quadtree = require("../"); + +tape("quadtree.findInCircle(x, y, radius) returns all the points within the search radius of the given [x, y]", function(test) { + const points = [[0, 0], [100, 0], [0, 100], [100, 100]]; + const q = d3_quadtree.quadtree(points); + test.deepEqual(q.findInCircle(20, 20, Infinity), points); + test.deepEqual(q.findInCircle(20, 20, 20 * Math.SQRT2 + 1e-6), [points[0]]); + test.deepEqual(q.findInCircle(20, 20, 20 * Math.SQRT2 - 1e-6), []); + test.deepEqual(q.findInCircle(50, 0, 51), [points[0], points[1]]); + test.end(); +}); + +tape("quadtree.findInCircle(x, y, radius, filter) returns all the points within the search radius of the given [x, y] and passing filter", function(test) { + const points = [[0, 0, "a"], [0, 0, "b"], [100, 0, "a"], [0, 100, "b"], [100, 100, "a"]]; + const q = d3_quadtree.quadtree(points); + const filter = d => d[2] === "a"; + test.deepEqual(q.findInCircle(20, 20, Infinity, filter), points.filter(filter)); + test.deepEqual(q.findInCircle(0, 0, 2, filter), [points[0]]); + test.end(); +});