fastquadtree 0.8.0__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/.github/workflows/release.yml +1 -1
- fastquadtree-0.9.0/Cargo.lock +170 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/Cargo.toml +2 -2
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/PKG-INFO +38 -37
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/README.md +36 -36
- fastquadtree-0.9.0/assets/interactive_v2_rect_screenshot.png +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/benchmarks/benchmark_native_vs_shim.py +50 -5
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/docs/benchmark.md +5 -1
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/docs/index.md +15 -4
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/docs/quickstart.md +1 -1
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/docs/runnables.md +12 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/interactive/interactive_v2_rect.py +2 -2
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/mkdocs.yml +1 -1
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/pyproject.toml +1 -0
- fastquadtree-0.9.0/pysrc/fastquadtree/pyqtree.py +127 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/pysrc/fastquadtree/rect_quadtree.py +3 -1
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/src/lib.rs +4 -2
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/src/rect_quadtree.rs +24 -29
- fastquadtree-0.9.0/tests/test_pyqtree_shim_compat.py +154 -0
- fastquadtree-0.8.0/Cargo.lock +0 -296
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/.github/workflows/docs.yml +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/.gitignore +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/.pre-commit-config.yaml +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/LICENSE +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/assets/ballpit.png +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/assets/interactive_v2_screenshot.png +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/assets/quadtree_bench_throughput.png +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/assets/quadtree_bench_time.png +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/benchmarks/cross_library_bench.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/benchmarks/quadtree_bench/__init__.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/benchmarks/quadtree_bench/engines.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/benchmarks/quadtree_bench/main.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/benchmarks/quadtree_bench/plotting.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/benchmarks/quadtree_bench/runner.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/benchmarks/requirements.txt +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/benchmarks/runner.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/benchmarks/system_info_collector.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/docs/api/point_item.md +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/docs/api/quadtree.md +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/docs/api/rect_item.md +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/docs/api/rect_quadtree.md +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/interactive/ballpit.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/interactive/interactive.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/interactive/interactive_v2.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/interactive/requirements.txt +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/pysrc/fastquadtree/__init__.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/pysrc/fastquadtree/_base_quadtree.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/pysrc/fastquadtree/_bimap.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/pysrc/fastquadtree/_item.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/pysrc/fastquadtree/point_quadtree.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/pysrc/fastquadtree/py.typed +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/src/geom.rs +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/src/quadtree.rs +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/insertions.rs +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/nearest_neighbor.rs +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/query.rs +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/rect_quadtree.rs +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/rectangle_traversal.rs +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/test_bimap.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/test_clear.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/test_delete.rs +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/test_delete_by_object.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/test_delete_python.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/test_python.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/test_rect_quadtree.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/test_unconventional_bounds.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/test_wrapper_edges.py +0 -0
- {fastquadtree-0.8.0 → fastquadtree-0.9.0}/tests/unconventional_bounds.rs +0 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
2
|
+
# It is not intended for manual editing.
|
3
|
+
version = 4
|
4
|
+
|
5
|
+
[[package]]
|
6
|
+
name = "autocfg"
|
7
|
+
version = "1.5.0"
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
9
|
+
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
10
|
+
|
11
|
+
[[package]]
|
12
|
+
name = "fastquadtree"
|
13
|
+
version = "0.9.0"
|
14
|
+
dependencies = [
|
15
|
+
"pyo3",
|
16
|
+
"smallvec",
|
17
|
+
]
|
18
|
+
|
19
|
+
[[package]]
|
20
|
+
name = "heck"
|
21
|
+
version = "0.5.0"
|
22
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
23
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
24
|
+
|
25
|
+
[[package]]
|
26
|
+
name = "indoc"
|
27
|
+
version = "2.0.6"
|
28
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
29
|
+
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
30
|
+
|
31
|
+
[[package]]
|
32
|
+
name = "libc"
|
33
|
+
version = "0.2.175"
|
34
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
35
|
+
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
36
|
+
|
37
|
+
[[package]]
|
38
|
+
name = "memoffset"
|
39
|
+
version = "0.9.1"
|
40
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
41
|
+
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
42
|
+
dependencies = [
|
43
|
+
"autocfg",
|
44
|
+
]
|
45
|
+
|
46
|
+
[[package]]
|
47
|
+
name = "once_cell"
|
48
|
+
version = "1.21.3"
|
49
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
50
|
+
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
51
|
+
|
52
|
+
[[package]]
|
53
|
+
name = "portable-atomic"
|
54
|
+
version = "1.11.1"
|
55
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
56
|
+
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
57
|
+
|
58
|
+
[[package]]
|
59
|
+
name = "proc-macro2"
|
60
|
+
version = "1.0.101"
|
61
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
62
|
+
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
63
|
+
dependencies = [
|
64
|
+
"unicode-ident",
|
65
|
+
]
|
66
|
+
|
67
|
+
[[package]]
|
68
|
+
name = "pyo3"
|
69
|
+
version = "0.26.0"
|
70
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
71
|
+
checksum = "7ba0117f4212101ee6544044dae45abe1083d30ce7b29c4b5cbdfa2354e07383"
|
72
|
+
dependencies = [
|
73
|
+
"indoc",
|
74
|
+
"libc",
|
75
|
+
"memoffset",
|
76
|
+
"once_cell",
|
77
|
+
"portable-atomic",
|
78
|
+
"pyo3-build-config",
|
79
|
+
"pyo3-ffi",
|
80
|
+
"pyo3-macros",
|
81
|
+
"unindent",
|
82
|
+
]
|
83
|
+
|
84
|
+
[[package]]
|
85
|
+
name = "pyo3-build-config"
|
86
|
+
version = "0.26.0"
|
87
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
88
|
+
checksum = "4fc6ddaf24947d12a9aa31ac65431fb1b851b8f4365426e182901eabfb87df5f"
|
89
|
+
dependencies = [
|
90
|
+
"target-lexicon",
|
91
|
+
]
|
92
|
+
|
93
|
+
[[package]]
|
94
|
+
name = "pyo3-ffi"
|
95
|
+
version = "0.26.0"
|
96
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
97
|
+
checksum = "025474d3928738efb38ac36d4744a74a400c901c7596199e20e45d98eb194105"
|
98
|
+
dependencies = [
|
99
|
+
"libc",
|
100
|
+
"pyo3-build-config",
|
101
|
+
]
|
102
|
+
|
103
|
+
[[package]]
|
104
|
+
name = "pyo3-macros"
|
105
|
+
version = "0.26.0"
|
106
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
107
|
+
checksum = "2e64eb489f22fe1c95911b77c44cc41e7c19f3082fc81cce90f657cdc42ffded"
|
108
|
+
dependencies = [
|
109
|
+
"proc-macro2",
|
110
|
+
"pyo3-macros-backend",
|
111
|
+
"quote",
|
112
|
+
"syn",
|
113
|
+
]
|
114
|
+
|
115
|
+
[[package]]
|
116
|
+
name = "pyo3-macros-backend"
|
117
|
+
version = "0.26.0"
|
118
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
119
|
+
checksum = "100246c0ecf400b475341b8455a9213344569af29a3c841d29270e53102e0fcf"
|
120
|
+
dependencies = [
|
121
|
+
"heck",
|
122
|
+
"proc-macro2",
|
123
|
+
"pyo3-build-config",
|
124
|
+
"quote",
|
125
|
+
"syn",
|
126
|
+
]
|
127
|
+
|
128
|
+
[[package]]
|
129
|
+
name = "quote"
|
130
|
+
version = "1.0.40"
|
131
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
132
|
+
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
133
|
+
dependencies = [
|
134
|
+
"proc-macro2",
|
135
|
+
]
|
136
|
+
|
137
|
+
[[package]]
|
138
|
+
name = "smallvec"
|
139
|
+
version = "1.15.1"
|
140
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
141
|
+
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
142
|
+
|
143
|
+
[[package]]
|
144
|
+
name = "syn"
|
145
|
+
version = "2.0.106"
|
146
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
147
|
+
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
148
|
+
dependencies = [
|
149
|
+
"proc-macro2",
|
150
|
+
"quote",
|
151
|
+
"unicode-ident",
|
152
|
+
]
|
153
|
+
|
154
|
+
[[package]]
|
155
|
+
name = "target-lexicon"
|
156
|
+
version = "0.13.3"
|
157
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
158
|
+
checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c"
|
159
|
+
|
160
|
+
[[package]]
|
161
|
+
name = "unicode-ident"
|
162
|
+
version = "1.0.19"
|
163
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
164
|
+
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
165
|
+
|
166
|
+
[[package]]
|
167
|
+
name = "unindent"
|
168
|
+
version = "0.2.4"
|
169
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
170
|
+
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[package]
|
2
2
|
name = "fastquadtree"
|
3
|
-
version = "0.
|
3
|
+
version = "0.9.0"
|
4
4
|
edition = "2021"
|
5
5
|
readme = "README.md"
|
6
6
|
|
@@ -8,7 +8,7 @@ readme = "README.md"
|
|
8
8
|
crate-type = ["rlib", "cdylib"]
|
9
9
|
|
10
10
|
[dependencies]
|
11
|
-
pyo3 = { version = "0.
|
11
|
+
pyo3 = { version = "0.26", features = ["extension-module", "abi3-py38"] }
|
12
12
|
smallvec = "1.15.1"
|
13
13
|
|
14
14
|
[profile.release]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: fastquadtree
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.9.0
|
4
4
|
Classifier: Programming Language :: Python :: 3
|
5
5
|
Classifier: Programming Language :: Python :: 3 :: Only
|
6
6
|
Classifier: Programming Language :: Rust
|
@@ -24,6 +24,7 @@ Requires-Dist: mkdocs-autorefs ; extra == 'dev'
|
|
24
24
|
Requires-Dist: mkdocs-git-revision-date-localized-plugin ; extra == 'dev'
|
25
25
|
Requires-Dist: mkdocs-minify-plugin ; extra == 'dev'
|
26
26
|
Requires-Dist: maturin>=1.5 ; extra == 'dev'
|
27
|
+
Requires-Dist: pyqtree==1.0.0 ; extra == 'dev'
|
27
28
|
Provides-Extra: dev
|
28
29
|
License-File: LICENSE
|
29
30
|
Summary: Rust-accelerated quadtree for Python with fast inserts, range queries, and k-NN search.
|
@@ -38,33 +39,54 @@ Project-URL: Issues, https://github.com/Elan456/fastquadtree/issues
|
|
38
39
|
|
39
40
|
# fastquadtree
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
[](https://pypi.org/project/fastquadtree/)
|
44
|
-
[](https://pypi.org/project/fastquadtree/#files)
|
45
|
-
[](LICENSE)
|
42
|
+
<img src="https://raw.githubusercontent.com/Elan456/fastquadtree/main/assets/interactive_v2_screenshot.png"
|
43
|
+
alt="Interactive Screenshot" align="right" width="420">
|
46
44
|
|
47
|
-
|
45
|
+
Rust-optimized quadtree with a clean Python API
|
48
46
|
|
47
|
+
[](https://pypi.org/project/fastquadtree/)
|
48
|
+
[](https://pypi.org/project/fastquadtree/)
|
49
|
+
[](https://pepy.tech/projects/fastquadtree)
|
49
50
|
[](https://github.com/Elan456/fastquadtree/actions/workflows/release.yml)
|
50
|
-
[](https://codecov.io/gh/Elan456/fastquadtree)
|
51
51
|
|
52
|
-
[](https://pyo3.rs/)
|
53
|
+
[](https://www.maturin.rs/)
|
54
54
|
[](https://github.com/astral-sh/ruff)
|
55
55
|
|
56
|
+
[](https://elan456.github.io/fastquadtree/)
|
57
|
+
[](https://pypi.org/project/fastquadtree/#files)
|
58
|
+
[](https://codecov.io/gh/Elan456/fastquadtree)
|
59
|
+
[](LICENSE)
|
56
60
|
|
61
|
+
<br clear="right"/>
|
57
62
|
|
58
|
-
|
63
|
+
## Why use fastquadtree
|
59
64
|
|
65
|
+
- Clean [Python API](https://elan456.github.io/fastquadtree/api/quadtree/) with modern typing hints
|
66
|
+
- The fastest quadtree Python package ([>10x faster](https://elan456.github.io/fastquadtree/benchmark/) than pyqtree)
|
67
|
+
- Prebuilt wheels for Windows, macOS, and Linux
|
68
|
+
- Support for [inserting bounding boxes](https://elan456.github.io/fastquadtree/api/rect_quadtree/) or points
|
69
|
+
- Fast KNN and range queries
|
70
|
+
- Optional object tracking for id ↔ object mapping
|
71
|
+
- [100% test coverage](https://codecov.io/gh/Elan456/fastquadtree) and CI on GitHub Actions
|
60
72
|
|
61
|
-
|
73
|
+
----
|
62
74
|
|
63
75
|
👉 **Docs:** https://elan456.github.io/fastquadtree/
|
64
76
|
|
65
|
-
|
66
|
-
|
67
|
-
|
77
|
+
## Examples
|
78
|
+
See examples of how fastquadtree can be used in the [runnables](https://elan456.github.io/fastquadtree/runnables/) section.
|
79
|
+
|
80
|
+
|
81
|
+
## Install
|
82
|
+
```bash
|
83
|
+
pip install fastquadtree
|
84
|
+
```
|
85
|
+
|
86
|
+
```python
|
87
|
+
from fastquadtree import QuadTree # Point handling
|
88
|
+
from fastquadtree import RectQuadTree # Bounding box handling
|
89
|
+
```
|
68
90
|
|
69
91
|
## Benchmarks
|
70
92
|
|
@@ -89,28 +111,7 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
|
|
89
111
|
| PyQtree | 1.492 | 0.263 | 1.755 | 1.00× |
|
90
112
|
| quads | 1.407 | 0.484 | 1.890 | 0.93× |
|
91
113
|
|
92
|
-
|
93
|
-
| Parameter | Value |
|
94
|
-
|---|---:|
|
95
|
-
| Bounds | (0, 0, 1000, 1000) |
|
96
|
-
| Max points per node | 128 |
|
97
|
-
| Max depth | 16 |
|
98
|
-
| Queries per experiment | 500 |
|
99
|
-
|
100
|
-
See the [benchmark section](https://elan456.github.io/fastquadtree/benchmark/) for details.
|
101
|
-
|
102
|
-
## Install
|
103
|
-
|
104
|
-
```bash
|
105
|
-
pip install fastquadtree
|
106
|
-
```
|
107
|
-
|
108
|
-
If you are developing locally:
|
109
|
-
|
110
|
-
```bash
|
111
|
-
# optimized dev install
|
112
|
-
maturin develop --release
|
113
|
-
```
|
114
|
+
See the [benchmark section](https://elan456.github.io/fastquadtree/benchmark/) for details, including configurations, system info, and native vs shim benchmarks.
|
114
115
|
|
115
116
|
## Quickstart
|
116
117
|
[See the quickstart guide](https://elan456.github.io/fastquadtree/quickstart/)
|
@@ -1,32 +1,53 @@
|
|
1
1
|
# fastquadtree
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
[](https://pypi.org/project/fastquadtree/)
|
6
|
-
[](https://pypi.org/project/fastquadtree/#files)
|
7
|
-
[](LICENSE)
|
3
|
+
<img src="https://raw.githubusercontent.com/Elan456/fastquadtree/main/assets/interactive_v2_screenshot.png"
|
4
|
+
alt="Interactive Screenshot" align="right" width="420">
|
8
5
|
|
9
|
-
|
6
|
+
Rust-optimized quadtree with a clean Python API
|
10
7
|
|
8
|
+
[](https://pypi.org/project/fastquadtree/)
|
9
|
+
[](https://pypi.org/project/fastquadtree/)
|
10
|
+
[](https://pepy.tech/projects/fastquadtree)
|
11
11
|
[](https://github.com/Elan456/fastquadtree/actions/workflows/release.yml)
|
12
|
-
[](https://codecov.io/gh/Elan456/fastquadtree)
|
13
12
|
|
14
|
-
[](https://pyo3.rs/)
|
14
|
+
[](https://www.maturin.rs/)
|
16
15
|
[](https://github.com/astral-sh/ruff)
|
17
16
|
|
17
|
+
[](https://elan456.github.io/fastquadtree/)
|
18
|
+
[](https://pypi.org/project/fastquadtree/#files)
|
19
|
+
[](https://codecov.io/gh/Elan456/fastquadtree)
|
20
|
+
[](LICENSE)
|
18
21
|
|
22
|
+
<br clear="right"/>
|
19
23
|
|
20
|
-
|
24
|
+
## Why use fastquadtree
|
21
25
|
|
26
|
+
- Clean [Python API](https://elan456.github.io/fastquadtree/api/quadtree/) with modern typing hints
|
27
|
+
- The fastest quadtree Python package ([>10x faster](https://elan456.github.io/fastquadtree/benchmark/) than pyqtree)
|
28
|
+
- Prebuilt wheels for Windows, macOS, and Linux
|
29
|
+
- Support for [inserting bounding boxes](https://elan456.github.io/fastquadtree/api/rect_quadtree/) or points
|
30
|
+
- Fast KNN and range queries
|
31
|
+
- Optional object tracking for id ↔ object mapping
|
32
|
+
- [100% test coverage](https://codecov.io/gh/Elan456/fastquadtree) and CI on GitHub Actions
|
22
33
|
|
23
|
-
|
34
|
+
----
|
24
35
|
|
25
36
|
👉 **Docs:** https://elan456.github.io/fastquadtree/
|
26
37
|
|
27
|
-
|
28
|
-
|
29
|
-
|
38
|
+
## Examples
|
39
|
+
See examples of how fastquadtree can be used in the [runnables](https://elan456.github.io/fastquadtree/runnables/) section.
|
40
|
+
|
41
|
+
|
42
|
+
## Install
|
43
|
+
```bash
|
44
|
+
pip install fastquadtree
|
45
|
+
```
|
46
|
+
|
47
|
+
```python
|
48
|
+
from fastquadtree import QuadTree # Point handling
|
49
|
+
from fastquadtree import RectQuadTree # Bounding box handling
|
50
|
+
```
|
30
51
|
|
31
52
|
## Benchmarks
|
32
53
|
|
@@ -51,28 +72,7 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
|
|
51
72
|
| PyQtree | 1.492 | 0.263 | 1.755 | 1.00× |
|
52
73
|
| quads | 1.407 | 0.484 | 1.890 | 0.93× |
|
53
74
|
|
54
|
-
|
55
|
-
| Parameter | Value |
|
56
|
-
|---|---:|
|
57
|
-
| Bounds | (0, 0, 1000, 1000) |
|
58
|
-
| Max points per node | 128 |
|
59
|
-
| Max depth | 16 |
|
60
|
-
| Queries per experiment | 500 |
|
61
|
-
|
62
|
-
See the [benchmark section](https://elan456.github.io/fastquadtree/benchmark/) for details.
|
63
|
-
|
64
|
-
## Install
|
65
|
-
|
66
|
-
```bash
|
67
|
-
pip install fastquadtree
|
68
|
-
```
|
69
|
-
|
70
|
-
If you are developing locally:
|
71
|
-
|
72
|
-
```bash
|
73
|
-
# optimized dev install
|
74
|
-
maturin develop --release
|
75
|
-
```
|
75
|
+
See the [benchmark section](https://elan456.github.io/fastquadtree/benchmark/) for details, including configurations, system info, and native vs shim benchmarks.
|
76
76
|
|
77
77
|
## Quickstart
|
78
78
|
[See the quickstart guide](https://elan456.github.io/fastquadtree/quickstart/)
|
Binary file
|
@@ -7,14 +7,16 @@ import random
|
|
7
7
|
import statistics as stats
|
8
8
|
from time import perf_counter as now
|
9
9
|
|
10
|
+
from pyqtree import Index as PyQTreeIndex
|
10
11
|
from system_info_collector import collect_system_info, format_system_info_markdown_lite
|
11
12
|
from tqdm import tqdm
|
12
13
|
|
13
14
|
from fastquadtree import QuadTree as ShimQuadTree
|
14
15
|
from fastquadtree._native import QuadTree as NativeQuadTree
|
16
|
+
from fastquadtree.pyqtree import Index as FQTIndex
|
15
17
|
|
16
18
|
BOUNDS = (0.0, 0.0, 1000.0, 1000.0)
|
17
|
-
CAPACITY =
|
19
|
+
CAPACITY = 64
|
18
20
|
MAX_DEPTH = 10
|
19
21
|
SEED = 42
|
20
22
|
|
@@ -69,6 +71,30 @@ def bench_shim(points, queries, *, track_objects: bool, with_objs: bool):
|
|
69
71
|
return t_build, t_query
|
70
72
|
|
71
73
|
|
74
|
+
def bench_pyqtree(points, queries, fqt: bool):
|
75
|
+
"""
|
76
|
+
Benchmarks the pyqtree compatibility shim vs the original pyqtree.
|
77
|
+
|
78
|
+
Set fqt to True to use the shim, False to use the original pyqtree.
|
79
|
+
"""
|
80
|
+
t0 = now()
|
81
|
+
qt = (
|
82
|
+
FQTIndex(bbox=BOUNDS, max_items=CAPACITY, max_depth=MAX_DEPTH)
|
83
|
+
if fqt
|
84
|
+
else PyQTreeIndex(bbox=BOUNDS, max_items=CAPACITY, max_depth=MAX_DEPTH)
|
85
|
+
)
|
86
|
+
for i, p in enumerate(points):
|
87
|
+
box = (p[0], p[1], p[0], p[1])
|
88
|
+
qt.insert(i, box)
|
89
|
+
t_build = now() - t0
|
90
|
+
|
91
|
+
t0 = now()
|
92
|
+
for q in queries:
|
93
|
+
_ = qt.intersect(q)
|
94
|
+
t_query = now() - t0
|
95
|
+
return t_build, t_query
|
96
|
+
|
97
|
+
|
72
98
|
def median_times(fn, points, queries, repeats: int, desc: str = "Running"):
|
73
99
|
"""Run benchmark multiple times and return median times."""
|
74
100
|
builds, queries_t = [], []
|
@@ -119,14 +145,28 @@ def main():
|
|
119
145
|
points,
|
120
146
|
queries,
|
121
147
|
args.repeats,
|
122
|
-
desc="Shim (no
|
148
|
+
desc="Shim (no tracking)",
|
123
149
|
)
|
124
150
|
s_build_map, s_query_map = median_times(
|
125
151
|
lambda pts, qs: bench_shim(pts, qs, track_objects=True, with_objs=True),
|
126
152
|
points,
|
127
153
|
queries,
|
128
154
|
args.repeats,
|
129
|
-
desc="Shim (
|
155
|
+
desc="Shim (tracking)",
|
156
|
+
)
|
157
|
+
p_build, p_query = median_times(
|
158
|
+
lambda pts, qs: bench_pyqtree(pts, qs, fqt=False),
|
159
|
+
points,
|
160
|
+
queries,
|
161
|
+
args.repeats,
|
162
|
+
desc="pyqtree (original)",
|
163
|
+
)
|
164
|
+
fqt_build, fqt_query = median_times(
|
165
|
+
lambda pts, qs: bench_pyqtree(pts, qs, fqt=True),
|
166
|
+
points,
|
167
|
+
queries,
|
168
|
+
args.repeats,
|
169
|
+
desc="pyqtree (FQT shim)",
|
130
170
|
)
|
131
171
|
print()
|
132
172
|
|
@@ -146,14 +186,19 @@ def main():
|
|
146
186
|
| Variant | Build | Query | Total |
|
147
187
|
|---|---:|---:|---:|
|
148
188
|
| Native | {fmt(n_build)} | {fmt(n_query)} | {fmt(n_build + n_query)} |
|
149
|
-
| Shim (no
|
150
|
-
| Shim (
|
189
|
+
| Shim (no tracking) | {fmt(s_build_no_map)} | {fmt(s_query_no_map)} | {fmt(s_build_no_map + s_query_no_map)} |
|
190
|
+
| Shim (tracking) | {fmt(s_build_map)} | {fmt(s_query_map)} | {fmt(s_build_map + s_query_map)} |
|
191
|
+
| pyqtree (fastquadtree) | {fmt(fqt_build)} | {fmt(fqt_query)} | {fmt(fqt_build + fqt_query)} |
|
192
|
+
| pyqtree (original) | {fmt(p_build)} | {fmt(p_query)} | {fmt(p_build + p_query)} |
|
151
193
|
|
152
194
|
### Summary
|
153
195
|
|
154
196
|
Using the shim with object tracking increases build time by {fmt(s_build_map / n_build)}x and query time by {fmt(s_query_map / n_query)}x.
|
155
197
|
**Total slowdown = {fmt((s_build_map + s_query_map) / (n_build + n_query))}x.**
|
156
198
|
|
199
|
+
If you directly replace pyqtree with the drop-in `fastquadtree.pyqtree.Index` shim, you get a build time of {fmt(fqt_build)}s and query time of {fmt(fqt_query)}s.
|
200
|
+
This is a total speedup of {fmt((p_build + p_query) / (fqt_build + fqt_query))}x compared to the original pyqtree and requires no code changes.
|
201
|
+
|
157
202
|
Adding the object map only impacts the build time, not the query time.
|
158
203
|
"""
|
159
204
|
print(md.strip())
|
@@ -17,7 +17,7 @@ Quadtrees are the focus of the benchmark, but Rtrees are included for reference.
|
|
17
17
|
|
18
18
|
| Library | Build (s) | Query (s) | Total (s) | Speed vs PyQtree |
|
19
19
|
|---|---:|---:|---:|---:|
|
20
|
-
| fastquadtree | 0.031 | 0.089 | 0.120 | 14.64
|
20
|
+
| **fastquadtree** | 0.031 | 0.089 | 0.120 | **14.64×** |
|
21
21
|
| Shapely STRtree | 0.179 | 0.100 | 0.279 | 6.29× |
|
22
22
|
| nontree-QuadTree | 0.595 | 0.605 | 1.200 | 1.46× |
|
23
23
|
| Rtree | 0.961 | 0.300 | 1.261 | 1.39× |
|
@@ -33,6 +33,8 @@ Quadtrees are the focus of the benchmark, but Rtrees are included for reference.
|
|
33
33
|
| Max depth | 16 |
|
34
34
|
| Queries per experiment | 500 |
|
35
35
|
|
36
|
+
---------
|
37
|
+
|
36
38
|
## Native vs Shim
|
37
39
|
|
38
40
|
### Configuration
|
@@ -55,6 +57,8 @@ Using the shim with object tracking increases build time by 3.604x and query tim
|
|
55
57
|
|
56
58
|
Adding the object map only impacts the build time, not the query time.
|
57
59
|
|
60
|
+
---------
|
61
|
+
|
58
62
|
## System Info
|
59
63
|
- **OS**: Windows 11 AMD64
|
60
64
|
- **Python**: CPython 3.12.2
|
@@ -35,14 +35,25 @@
|
|
35
35
|
|
36
36
|
## Why use fastquadtree
|
37
37
|
|
38
|
-
-
|
39
|
-
-
|
38
|
+
- Clean [Python API](api/quadtree.md) with modern typing hints
|
39
|
+
- The fastest quadtree Python package ([>10x faster](benchmark.md) than pyqtree)
|
40
|
+
- Prebuilt wheels for Windows, macOS, and Linux
|
41
|
+
- Support for [inserting bounding boxes](api/rect_quadtree.md) or points
|
42
|
+
- Fast KNN and range queries
|
40
43
|
- Optional object tracking for id ↔ object mapping
|
41
|
-
-
|
44
|
+
- [100% test coverage](https://codecov.io/gh/Elan456/fastquadtree) and CI on GitHub Actions
|
45
|
+
|
46
|
+
## Examples
|
47
|
+
See examples of how fastquadtree can be used in the [runnables](runnables.md) section.
|
48
|
+
|
42
49
|
|
43
50
|
## Install
|
44
51
|
```bash
|
45
52
|
pip install fastquadtree
|
46
53
|
```
|
47
54
|
|
48
|
-
|
55
|
+
```python
|
56
|
+
from fastquadtree import QuadTree # Point handling
|
57
|
+
from fastquadtree import RectQuadTree # Bounding box handling
|
58
|
+
from fastquadtree.pyqtree import Index # Drop-in replacement for pyqtree
|
59
|
+
```
|
@@ -5,6 +5,10 @@
|
|
5
5
|
- Add and delete boids with mouse clicks
|
6
6
|
- Visualize KNN and range queries
|
7
7
|
|
8
|
+
The interactive demo is a great way to see how fastquadtree works in practice.
|
9
|
+
You can see how the quadtree subdivides as you add points, and validate the accuracy of the queries visually.
|
10
|
+
By pressing 1, you can visualize the KNN query for each boid.
|
11
|
+
|
8
12
|
```bash
|
9
13
|
pip install -r interactive/requirements.txt
|
10
14
|
python interactive/interactive_v2.py
|
@@ -16,15 +20,23 @@ python interactive/interactive_v2.py
|
|
16
20
|
- Similar to the above demo, but uses rectangles instead of points
|
17
21
|
- If the rectangles intersect at all with the query area, they will be highlighted in red
|
18
22
|
|
23
|
+
If you are creating a game or simulation environment where entities have bounding boxes, you can use the
|
24
|
+
rectangular quadtree to quickly check which entities are intersecting with another.
|
25
|
+
|
19
26
|
```bash
|
20
27
|
pip install -r interactive/requirements.txt
|
21
28
|
python interactive/interactive_v2_rect.py
|
22
29
|
```
|
23
30
|
|
31
|
+

|
32
|
+
|
24
33
|
## 2. Ball Pit
|
25
34
|
- Spawn balls in a pit with physics-based collisions
|
26
35
|
- Easily switch between brute force and quadtree collision detection to see the performance difference
|
27
36
|
|
37
|
+
The ball pit demo shows how quadtrees offer massive performance improvements for collision detection.
|
38
|
+
Rectangular queries are used to find potential collisions, and then precise circle-circle collision checks are performed.
|
39
|
+
|
28
40
|
```bash
|
29
41
|
pip install -r interactive/requirements.txt
|
30
42
|
python interactive/ball_pit.py
|
@@ -85,8 +85,8 @@ class Box:
|
|
85
85
|
spd = random.uniform(60.0, 160.0)
|
86
86
|
self.vx = math.cos(ang) * spd
|
87
87
|
self.vy = math.sin(ang) * spd
|
88
|
-
self.w = random.uniform(8.0,
|
89
|
-
self.h = random.uniform(8.0,
|
88
|
+
self.w = random.uniform(8.0, 200.0)
|
89
|
+
self.h = random.uniform(8.0, 200.0)
|
90
90
|
|
91
91
|
def update(self, dt):
|
92
92
|
self.x += self.vx * dt
|