d3-quadtree

四叉树递归地将二维空间划分为正方形,将每个正方形划分为四个大小相等的子正方形。每个独立点存在于一个唯一的叶节点;重合点由链表表示。四叉树可以加速各种空间操作,例如用于计算多体力的 Barnes–Hut 近似法、碰撞检测以及搜索附近点。

687474703a2f2f626c2e6f636b732e6f72672f6d626f73746f636b2f7261772f393037383639302f7468756d626e61696c2e706e67

687474703a2f2f626c2e6f636b732e6f72672f6d626f73746f636b2f7261772f343334333231342f7468756d626e61696c2e706e67

安装

如果您使用 npm,请运行 npm install d3-quadtree。您也可以在 GitHub 上下载最新版本。对于现代浏览器中的原生 HTML,请从 Skypak 导入 d3-quadtree。

<script type="module">

import {quadtree} from "https://cdn.skypack.dev/d3-quadtree@3";

const tree = quadtree();

</script>

对于旧版环境,您可以从基于 npm 的 CDN(如 jsDelivr)加载 d3-quadtree 的 UMD 包;它会导出一个全局变量 d3

<script src="https://cdn.jsdelivr.net.cn/npm/d3-quadtree@3"></script>
<script>

const tree = d3.quadtree();

</script>

API 参考

# d3.quadtree([data[, x, y]]) <>

创建一个新的、空的四叉树,其 范围 为空,并使用默认的 xy 访问器。如果指定了 data,则将指定的数据数组 添加 到四叉树中。这等同于

const tree = d3.quadtree()
    .addAll(data);

如果还指定了 xy,则在将指定的数据数组添加到四叉树之前,将 xy 访问器设置为指定函数,这等同于

const tree = d3.quadtree()
    .x(x)
    .y(y)
    .addAll(data);

# quadtree.x([x]) <>

如果指定了 x,则设置当前的 x 坐标访问器并返回四叉树。如果未指定 x,则返回当前的 x 访问器,默认情况为

function x(d) {
  return d[0];
}

x 访问器用于在 添加 到树中和 从中移除 数据时派生数据的 x 坐标。在 查找 时,它也用于重新访问先前添加到树中的数据的坐标;因此,xy 访问器必须一致,给定相同的输入应返回相同的值。

# quadtree.y([y]) <>

如果指定了 y,则设置当前的 y 坐标访问器并返回四叉树。如果未指定 y,则返回当前的 y 访问器,默认情况为

function y(d) {
  return d[1];
}

y 访问器用于在 添加 到树中和 从中移除 数据时派生数据的 y 坐标。在 查找 时,它也用于重新访问先前添加到树中的数据的坐标;因此,xy 访问器必须一致,给定相同的输入应返回相同的值。

# quadtree.extent([extent]) <>

如果指定了 extent,则将四叉树扩展为 覆盖 指定的点 [[x0, y0], [x1, y1]] 并返回四叉树。如果未指定 extent,则返回四叉树的当前范围 [[x0, y0], [x1, y1]],其中 x0y0 是包含的下界,x1y1 是包含的上界,或者在四叉树没有范围时返回 undefined。也可以通过调用 quadtree.coverquadtree.add 来扩展范围。

# quadtree.cover(x, y) <>

将四叉树扩展为覆盖指定点 ⟨x,y⟩,并返回四叉树。如果四叉树的范围已覆盖指定点,则此方法不执行任何操作。如果四叉树有范围,则范围将重复加倍以覆盖指定点,并根据需要包装 节点;如果四叉树为空,则范围将初始化为 [[⌊x⌋, ⌊y⌋], [⌈x⌉, ⌈y⌉]]。(舍入是必需的,以便如果范围后来加倍,现有象限的边界不会因浮点误差而改变。)

# quadtree.add(datum) <>

使用当前的 xy 访问器派生其坐标 ⟨x,y⟩,将指定的 datum 添加到四叉树,并返回四叉树。如果新点超出四叉树的当前 范围,四叉树将自动扩展以 覆盖 新点。

# quadtree.addAll(data) <>

使用当前的 xy 访问器派生每个元素的坐标 ⟨x,y⟩,将指定的 data 数组添加到四叉树,并返回此四叉树。这大致相当于重复调用 quadtree.add

for (let i = 0, n = data.length; i < n; ++i) {
  quadtree.add(data[i]);
}

但是,此方法可以生成更紧凑的四叉树,因为在添加数据之前会先计算 data 的范围。

# quadtree.remove(datum) <>

使用当前的 xy 访问器派生其坐标 ⟨x,y⟩,从四叉树中移除指定的 datum,并返回四叉树。如果指定的 datum 不存在于此四叉树中,则此方法不执行任何操作。

# quadtree.removeAll(data) <>

使用当前的 xy 访问器派生其坐标 ⟨x,y⟩,从四叉树中移除指定的 data,并返回四叉树。如果指定的 datum 不存在于此四叉树中,则会被忽略。

# quadtree.copy()

返回四叉树的副本。返回的四叉树中的所有 节点 都是四叉树中相应节点的副本;但是,四叉树中的任何数据都通过引用共享,而不是复制。

# quadtree.root() <>

返回四叉树的根 节点

# quadtree.data() <>

返回四叉树中的所有数据数组。

# quadtree.size() <>

返回四叉树中的数据总数。

# quadtree.find(x, y[, radius]) <>

返回距离给定搜索 radius 内的 ⟨x,y⟩ 位置最近的数据。如果未指定 radius,则默认为无穷大。如果在搜索区域内没有数据,则返回 undefined。

# quadtree.visit(callback) <>

以先序遍历访问四叉树中的每个 节点,为每个节点调用指定的 callback,参数为 nodex0y0x1y1,其中 node 是正在访问的节点,⟨x0, y0⟩ 是节点的下界,⟨x1, y1⟩ 是上界,并返回四叉树。(假设正 x 向右,正 y 向下,如 Canvas 和 SVG 中通常那样,⟨x0, y0⟩ 是左上角,⟨x1, y1⟩ 是右下角;但坐标系是任意的,所以更正式地说 x0 <= x1y0 <= y1。)

如果 callback 对给定节点返回 true,则不访问该节点的子节点;否则,将访问所有子节点。这可用于快速访问树的某些部分,例如在使用 Barnes–Hut 近似法 时。但请注意,子象限始终以兄弟顺序访问:左上、右上、左下、右下。在 搜索 等情况下,按特定顺序访问兄弟节点可能会更快。

例如,以下代码访问四叉树并返回矩形范围 [xmin, ymin, xmax, ymax] 内的所有节点,忽略不可能包含任何此类节点的象限。

function search(quadtree, xmin, ymin, xmax, ymax) {
  const results = [];
  quadtree.visit((node, x1, y1, x2, y2) => {
    if (!node.length) {
      do {
        let d = node.data;
        if (d[0] >= xmin && d[0] < xmax && d[1] >= ymin && d[1] < ymax) {
          results.push(d);
        }
      } while (node = node.next);
    }
    return x1 >= xmax || y1 >= ymax || x2 < xmin || y2 < ymin;
  });
  return results;
}

# quadtree.visitAfter(callback) <>

以后序遍历访问四叉树中的每个 节点,为每个节点调用指定的 callback,参数为 nodex0y0x1y1,其中 node 是正在访问的节点,⟨x0, y0⟩ 是节点的下界,⟨x1, y1⟩ 是上界,并返回四叉树。(假设正 x 向右,正 y 向下,如 Canvas 和 SVG 中通常那样,⟨x0, y0⟩ 是左上角,⟨x1, y1⟩ 是右下角;但坐标系是任意的,所以更正式地说 x0 <= x1y0 <= y1。)返回 root

节点

四叉树的内部节点以从左到右、从上到下的顺序表示为四元素数组。

  • 0 - 左上象限(如果存在)。
  • 1 - 右上象限(如果存在)。
  • 2 - 左下象限(如果存在)。
  • 3 - 右下象限(如果存在)。

如果子象限为空,则可能为 undefined。

叶节点表示为具有以下属性的对象。

  • data - 与该点关联的数据,传递给 quadtree.add
  • next - 该叶节点中的下一个数据(如果存在)。

length 属性可用于区分叶节点和内部节点:叶节点为 undefined,内部节点为 4。例如,要遍历叶节点中的所有数据。

if (!node.length) do console.log(node.data); while (node = node.next);

点的 xy 坐标在点位于四叉树中时**不得修改**。要更新点的位置,请 移除 该点,然后以新位置重新 添加 它。或者,您可以完全丢弃现有四叉树并从头开始创建新四叉树;如果许多点已移动,这可能更有效。

GitHub

https://github.com/MathGaps/d3-quadtree-flutter