fastquadtree 0.6.1__cp38-abi3-win_amd64.whl → 0.8.0__cp38-abi3-win_amd64.whl
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/__init__.py +4 -380
- fastquadtree/_base_quadtree.py +263 -0
- fastquadtree/_bimap.py +22 -22
- fastquadtree/_item.py +35 -7
- fastquadtree/_native.pyd +0 -0
- fastquadtree/point_quadtree.py +161 -0
- fastquadtree/rect_quadtree.py +98 -0
- {fastquadtree-0.6.1.dist-info → fastquadtree-0.8.0.dist-info}/METADATA +32 -141
- fastquadtree-0.8.0.dist-info/RECORD +12 -0
- {fastquadtree-0.6.1.dist-info → fastquadtree-0.8.0.dist-info}/WHEEL +1 -1
- fastquadtree/__init__.pyi +0 -70
- fastquadtree-0.6.1.dist-info/RECORD +0 -10
- {fastquadtree-0.6.1.dist-info → fastquadtree-0.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: fastquadtree
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.8.0
|
4
4
|
Classifier: Programming Language :: Python :: 3
|
5
5
|
Classifier: Programming Language :: Python :: 3 :: Only
|
6
6
|
Classifier: Programming Language :: Rust
|
@@ -12,11 +12,18 @@ Classifier: Topic :: Software Development :: Libraries
|
|
12
12
|
Classifier: Typing :: Typed
|
13
13
|
Classifier: License :: OSI Approved :: MIT License
|
14
14
|
Requires-Dist: ruff>=0.6.0 ; extra == 'dev'
|
15
|
-
Requires-Dist: pytest>=
|
16
|
-
Requires-Dist: pytest-cov>=
|
15
|
+
Requires-Dist: pytest>=8.4.2 ; extra == 'dev'
|
16
|
+
Requires-Dist: pytest-cov>=7.0.0 ; extra == 'dev'
|
17
17
|
Requires-Dist: coverage>=7.5 ; extra == 'dev'
|
18
18
|
Requires-Dist: mypy>=1.10 ; extra == 'dev'
|
19
19
|
Requires-Dist: build>=1.2.1 ; extra == 'dev'
|
20
|
+
Requires-Dist: mkdocs>=1.6 ; extra == 'dev'
|
21
|
+
Requires-Dist: mkdocs-material ; extra == 'dev'
|
22
|
+
Requires-Dist: mkdocstrings[python] ; extra == 'dev'
|
23
|
+
Requires-Dist: mkdocs-autorefs ; extra == 'dev'
|
24
|
+
Requires-Dist: mkdocs-git-revision-date-localized-plugin ; extra == 'dev'
|
25
|
+
Requires-Dist: mkdocs-minify-plugin ; extra == 'dev'
|
26
|
+
Requires-Dist: maturin>=1.5 ; extra == 'dev'
|
20
27
|
Provides-Extra: dev
|
21
28
|
License-File: LICENSE
|
22
29
|
Summary: Rust-accelerated quadtree for Python with fast inserts, range queries, and k-NN search.
|
@@ -26,10 +33,12 @@ Requires-Python: >=3.8
|
|
26
33
|
Description-Content-Type: text/markdown
|
27
34
|
Project-URL: Homepage, https://github.com/Elan456/fastquadtree
|
28
35
|
Project-URL: Repository, https://github.com/Elan456/fastquadtree
|
36
|
+
Project-URL: Documentation, https://elan456.github.io/fastquadtree/
|
29
37
|
Project-URL: Issues, https://github.com/Elan456/fastquadtree/issues
|
30
38
|
|
31
39
|
# fastquadtree
|
32
40
|
|
41
|
+
[](https://elan456.github.io/fastquadtree/)
|
33
42
|
[](https://pypi.org/project/fastquadtree/)
|
34
43
|
[](https://pypi.org/project/fastquadtree/)
|
35
44
|
[](https://pypi.org/project/fastquadtree/#files)
|
@@ -37,7 +46,7 @@ Project-URL: Issues, https://github.com/Elan456/fastquadtree/issues
|
|
37
46
|
|
38
47
|
[](https://pepy.tech/projects/fastquadtree)
|
39
48
|
|
40
|
-
[](https://github.com/Elan456/fastquadtree/actions/workflows/
|
49
|
+
[](https://github.com/Elan456/fastquadtree/actions/workflows/release.yml)
|
41
50
|
[](https://codecov.io/gh/Elan456/fastquadtree)
|
42
51
|
|
43
52
|
[](https://pyo3.rs/)
|
@@ -51,6 +60,8 @@ Project-URL: Issues, https://github.com/Elan456/fastquadtree/issues
|
|
51
60
|
|
52
61
|
Rust-optimized quadtree with a simple Python API.
|
53
62
|
|
63
|
+
👉 **Docs:** https://elan456.github.io/fastquadtree/
|
64
|
+
|
54
65
|
- Python package: **`fastquadtree`**
|
55
66
|
- Python ≥ 3.8
|
56
67
|
- Import path: `from fastquadtree import QuadTree`
|
@@ -66,7 +77,6 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
|
|
66
77
|
|
67
78
|
### Summary (largest dataset, PyQtree baseline)
|
68
79
|
- Points: **250,000**, Queries: **500**
|
69
|
-
--------------------
|
70
80
|
- Fastest total: **fastquadtree** at **0.120 s**
|
71
81
|
|
72
82
|
| Library | Build (s) | Query (s) | Total (s) | Speed vs PyQtree |
|
@@ -79,7 +89,7 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
|
|
79
89
|
| PyQtree | 1.492 | 0.263 | 1.755 | 1.00× |
|
80
90
|
| quads | 1.407 | 0.484 | 1.890 | 0.93× |
|
81
91
|
|
82
|
-
|
92
|
+
### Benchmark Configuration
|
83
93
|
| Parameter | Value |
|
84
94
|
|---|---:|
|
85
95
|
| Bounds | (0, 0, 1000, 1000) |
|
@@ -87,11 +97,13 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
|
|
87
97
|
| Max depth | 16 |
|
88
98
|
| Queries per experiment | 500 |
|
89
99
|
|
100
|
+
See the [benchmark section](https://elan456.github.io/fastquadtree/benchmark/) for details.
|
101
|
+
|
90
102
|
## Install
|
91
103
|
|
92
104
|
```bash
|
93
105
|
pip install fastquadtree
|
94
|
-
|
106
|
+
```
|
95
107
|
|
96
108
|
If you are developing locally:
|
97
109
|
|
@@ -101,76 +113,12 @@ maturin develop --release
|
|
101
113
|
```
|
102
114
|
|
103
115
|
## Quickstart
|
104
|
-
|
105
|
-
```python
|
106
|
-
from fastquadtree import QuadTree
|
107
|
-
|
108
|
-
# Bounds are (min_x, min_y, max_x, max_y)
|
109
|
-
qt = QuadTree(bounds=(0, 0, 1000, 1000), capacity=20) # max_depth is optional
|
110
|
-
|
111
|
-
# Insert points with auto ids
|
112
|
-
id1 = qt.insert((10, 10))
|
113
|
-
id2 = qt.insert((200, 300))
|
114
|
-
id3 = qt.insert((999, 500), id=42) # you can supply your own id
|
115
|
-
|
116
|
-
# Axis-aligned rectangle query
|
117
|
-
hits = qt.query((0, 0, 250, 350)) # returns [(id, x, y), ...] by default
|
118
|
-
print(hits) # e.g. [(1, 10.0, 10.0), (2, 200.0, 300.0)]
|
119
|
-
|
120
|
-
# Nearest neighbor
|
121
|
-
best = qt.nearest_neighbor((210, 310)) # -> (id, x, y) or None
|
122
|
-
print(best)
|
123
|
-
|
124
|
-
# k-nearest neighbors
|
125
|
-
top3 = qt.nearest_neighbors((210, 310), 3)
|
126
|
-
print(top3) # list of up to 3 (id, x, y) tuples
|
127
|
-
|
128
|
-
# Delete items by ID and location
|
129
|
-
deleted = qt.delete(id2, (200, 300)) # True if found and deleted
|
130
|
-
print(f"Deleted: {deleted}")
|
131
|
-
print(f"Remaining items: {qt.count_items()}")
|
132
|
-
|
133
|
-
# For object tracking with track_objects=True
|
134
|
-
qt_tracked = QuadTree((0, 0, 1000, 1000), capacity=4, track_objects=True)
|
135
|
-
player1 = {"name": "Alice", "score": 100}
|
136
|
-
player2 = {"name": "Bob", "score": 200}
|
137
|
-
|
138
|
-
id1 = qt_tracked.insert((50, 50), obj=player1)
|
139
|
-
id2 = qt_tracked.insert((150, 150), obj=player2)
|
140
|
-
|
141
|
-
# Delete by object reference (O(1) lookup!)
|
142
|
-
deleted = qt_tracked.delete_by_object(player1)
|
143
|
-
print(f"Deleted player: {deleted}") # True
|
144
|
-
```
|
145
|
-
|
146
|
-
### Working with Python objects
|
147
|
-
|
148
|
-
You can keep the tree pure and manage your own id → object map, or let the wrapper manage it.
|
149
|
-
|
150
|
-
**Wrapper Managed Objects**
|
151
|
-
|
152
|
-
```python
|
153
|
-
from fastquadtree import QuadTree
|
154
|
-
|
155
|
-
qt = QuadTree((0, 0, 1000, 1000), capacity=16, track_objects=True)
|
156
|
-
|
157
|
-
# Store the object alongside the point
|
158
|
-
qt.insert((25, 40), obj={"name": "apple"})
|
159
|
-
|
160
|
-
# Ask for Item objects within a bounding box
|
161
|
-
items = qt.query((0, 0, 100, 100), as_items=True)
|
162
|
-
for it in items:
|
163
|
-
print(it.id, it.x, it.y, it.obj)
|
164
|
-
```
|
165
|
-
|
166
|
-
You can also attach or replace an object later:
|
167
|
-
|
168
|
-
```python
|
169
|
-
qt.attach(123, my_object) # binds object to id 123
|
170
|
-
```
|
116
|
+
[See the quickstart guide](https://elan456.github.io/fastquadtree/quickstart/)
|
171
117
|
|
172
118
|
## API
|
173
119
|
|
120
|
+
[See the full API](https://elan456.github.io/fastquadtree/api/quadtree/)
|
121
|
+
|
174
122
|
### `QuadTree(bounds, capacity, max_depth=None, track_objects=False, start_id=1)`
|
175
123
|
|
176
124
|
* `bounds` — tuple `(min_x, min_y, max_x, max_y)` defines the 2D area covered by the quadtree
|
@@ -179,36 +127,17 @@ qt.attach(123, my_object) # binds object to id 123
|
|
179
127
|
* `track_objects` — if `True`, the wrapper maintains an id → object map for convenience.
|
180
128
|
* `start_id` — starting value for auto-assigned ids
|
181
129
|
|
182
|
-
###
|
183
|
-
|
184
|
-
Full docs are in the docstrings of the [Python Shim](pysrc/fastquadtree/__init__.py)
|
130
|
+
### Key Methods
|
185
131
|
|
186
132
|
- `insert(xy, *, id=None, obj=None) -> int`
|
187
133
|
|
188
|
-
- `insert_many_points(points) -> int`
|
189
|
-
|
190
134
|
- `query(rect, *, as_items=False) -> list`
|
191
135
|
|
192
136
|
- `nearest_neighbor(xy, *, as_item=False) -> (id, x, y) | Item | None`
|
193
137
|
|
194
|
-
- `nearest_neighbors(xy, k, *, as_items=False) -> list`
|
195
|
-
|
196
138
|
- `delete(id, xy) -> bool`
|
197
139
|
|
198
|
-
|
199
|
-
|
200
|
-
- `attach(id, obj) -> None (requires track_objects=True)`
|
201
|
-
|
202
|
-
- `count_items() -> int`
|
203
|
-
|
204
|
-
- `get(id) -> object | None`
|
205
|
-
|
206
|
-
- `get_all_rectangles() -> list[tuple] (for visualization)`
|
207
|
-
|
208
|
-
### `Item` (returned when `as_items=True`)
|
209
|
-
|
210
|
-
* Attributes: `id`, `x`, `y`, and a lazy `obj` property
|
211
|
-
* Accessing `obj` performs a dictionary lookup only if tracking is enabled
|
140
|
+
There are more methods and object tracking versions in the [docs](https://elan456.github.io/fastquadtree/api/quadtree/).
|
212
141
|
|
213
142
|
### Geometric conventions
|
214
143
|
|
@@ -222,52 +151,17 @@ Full docs are in the docstrings of the [Python Shim](pysrc/fastquadtree/__init__
|
|
222
151
|
* Choose `capacity` so that leaves keep a small batch of points. Typical values are 8 to 64.
|
223
152
|
* If your data is very skewed, set a `max_depth` to prevent long chains.
|
224
153
|
* For fastest local runs, use `maturin develop --release`.
|
225
|
-
* The wrapper
|
226
|
-
|
227
|
-
|
228
|
-
### Native vs Shim Benchmark
|
229
|
-
|
230
|
-
**Setup**
|
231
|
-
- Points: 500,000
|
232
|
-
- Queries: 500
|
233
|
-
- Repeats: 5
|
234
|
-
|
235
|
-
**Timing (seconds)**
|
154
|
+
* The wrapper maintains an object map only if the quadtree was constructed with `track_objects=True`. If you don't need it, leave it off for best performance.
|
155
|
+
* Refer to the [Native vs Shim Benchmark](https://elan456.github.io/fastquadtree/benchmark/#native-vs-shim-benchmark) for overhead details.
|
236
156
|
|
237
|
-
|
238
|
-
|---|---:|---:|---:|
|
239
|
-
| Native | 0.483 | 4.380 | 4.863 |
|
240
|
-
| Shim (no map) | 0.668 | 4.167 | 4.835 |
|
241
|
-
| Shim (track+objs) | 1.153 | 4.458 | 5.610 |
|
157
|
+
### Pygame Ball Pit Demo
|
242
158
|
|
243
|
-
|
159
|
+

|
244
160
|
|
245
|
-
|
246
|
-
|
161
|
+
A simple demo of moving objects with collision detection using **fastquadtree**.
|
162
|
+
You can toggle between quadtree mode and brute-force mode to see the performance difference.
|
247
163
|
|
248
|
-
|
249
|
-
To run the benchmarks yourself, first install the dependencies:
|
250
|
-
|
251
|
-
```bash
|
252
|
-
pip install -r benchmarks/requirements.txt
|
253
|
-
```
|
254
|
-
|
255
|
-
Then run:
|
256
|
-
|
257
|
-
```bash
|
258
|
-
python benchmarks/cross_library_bench.py
|
259
|
-
python benchmarks/benchmark_native_vs_shim.py
|
260
|
-
```
|
261
|
-
|
262
|
-
## Run Visualizer
|
263
|
-
A visualizer is included to help you understand how the quadtree subdivides space.
|
264
|
-
|
265
|
-
```bash
|
266
|
-
pip install -r interactive/requirements.txt
|
267
|
-
python interactive/interactive_v2.py
|
268
|
-
```
|
269
|
-
|
270
|
-
Check the CLI arguments for the cross-library benchmark in `benchmarks/quadtree_bench/main.py`.
|
164
|
+
See the [runnables guide](https://elan456.github.io/fastquadtree/runnables/) for setup instructions.
|
271
165
|
|
272
166
|
## FAQ
|
273
167
|
|
@@ -278,10 +172,7 @@ Allowed. For k-nearest, duplicates are de-duplicated by id. For range queries yo
|
|
278
172
|
Yes! Use `delete(id, xy)` to remove specific items. You must provide both the ID and exact location for precise deletion. This handles cases where multiple items exist at the same location. If you're using `track_objects=True`, you can also use `delete_by_object(obj)` for convenient object-based deletion with O(1) lookup. The tree automatically merges nodes when item counts drop below capacity.
|
279
173
|
|
280
174
|
**Can I store rectangles or circles?**
|
281
|
-
|
282
|
-
|
283
|
-
**Threading**
|
284
|
-
Use one tree per thread if you need heavy parallel inserts from Python.
|
175
|
+
Yes, you can store rectangles using the `RectQuadTree` class. Circles can be approximated with bounding boxes. See the [RectQuadTree docs](https://elan456.github.io/fastquadtree/api/rect_quadtree/) for details.
|
285
176
|
|
286
177
|
## License
|
287
178
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
fastquadtree-0.8.0.dist-info/METADATA,sha256=CK8OPp9c36mESYvCPafheE8Rdt9oVbtuJOoDJjvXiRw,8632
|
2
|
+
fastquadtree-0.8.0.dist-info/WHEEL,sha256=CG8OzNtm0LMpJ2zhrjswlO8N-965OeMLklsQAG-nMvQ,94
|
3
|
+
fastquadtree-0.8.0.dist-info/licenses/LICENSE,sha256=46IVFhoCIwMo-ocq4olyEB1eBvvtaKic5yGLeKXnDuc,1092
|
4
|
+
fastquadtree/__init__.py,sha256=jy7uimnd7QDllmNOgnRByKom1aONIKOWhCvCJxndsnM,200
|
5
|
+
fastquadtree/_base_quadtree.py,sha256=sfHAvKsWbrBX6RSUNi32Lh0fxCu8D6hAnyp1-SJj0Ok,8676
|
6
|
+
fastquadtree/_bimap.py,sha256=oNvJOk-BjYLo6aNJnG0i3uEQCq8FsUTGRE6c6_kVC2c,3439
|
7
|
+
fastquadtree/_item.py,sha256=cXiKXCjCfOWK19LUbxnzvAx6xffejd3F1axxYR9SMeM,1400
|
8
|
+
fastquadtree/_native.pyd,sha256=wr7KjDtXszGOlm8GvAkKjeZugzOq_o23lbqV-D8_6Ko,271360
|
9
|
+
fastquadtree/point_quadtree.py,sha256=Xs2Igat4ekewnMd2wHVYu0pm7f7duvLdEcm_DUrO2B4,5744
|
10
|
+
fastquadtree/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
+
fastquadtree/rect_quadtree.py,sha256=_t3RVZYSSxZatGCtv4G9Cokn6mJ8BMDsmWHAo7fsAaE,3569
|
12
|
+
fastquadtree-0.8.0.dist-info/RECORD,,
|
fastquadtree/__init__.pyi
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
from typing import (
|
2
|
-
Any,
|
3
|
-
Iterable,
|
4
|
-
Literal as _Literal, # avoid polluting public namespace
|
5
|
-
overload,
|
6
|
-
)
|
7
|
-
|
8
|
-
Bounds = tuple[float, float, float, float]
|
9
|
-
Point = tuple[float, float]
|
10
|
-
|
11
|
-
class Item:
|
12
|
-
id_: int
|
13
|
-
x: float
|
14
|
-
y: float
|
15
|
-
obj: Any | None
|
16
|
-
|
17
|
-
class QuadTree:
|
18
|
-
# Expose the raw native class for power users
|
19
|
-
NativeQuadTree: type
|
20
|
-
|
21
|
-
def __init__(
|
22
|
-
self,
|
23
|
-
bounds: Bounds,
|
24
|
-
capacity: int,
|
25
|
-
*,
|
26
|
-
max_depth: int | None = None,
|
27
|
-
track_objects: bool = False,
|
28
|
-
start_id: int = 1,
|
29
|
-
) -> None: ...
|
30
|
-
|
31
|
-
# Inserts
|
32
|
-
def insert(self, xy: Point, *, id_: int | None = ..., obj: Any = ...) -> int: ...
|
33
|
-
def insert_many_points(self, points: Iterable[Point]) -> int: ...
|
34
|
-
def attach(self, id_: int, obj: Any) -> None: ...
|
35
|
-
|
36
|
-
# Deletions
|
37
|
-
def delete(self, id_: int, xy: Point) -> bool: ...
|
38
|
-
def delete_by_object(self, obj: Any) -> bool: ...
|
39
|
-
|
40
|
-
# Queries
|
41
|
-
@overload
|
42
|
-
def query(
|
43
|
-
self, rect: Bounds, *, as_items: _Literal[False] = ...
|
44
|
-
) -> list[tuple[int, float, float]]: ...
|
45
|
-
@overload
|
46
|
-
def query(self, rect: Bounds, *, as_items: _Literal[True]) -> list[Item]: ...
|
47
|
-
@overload
|
48
|
-
def nearest_neighbor(
|
49
|
-
self, xy: Point, *, as_item: _Literal[False] = ...
|
50
|
-
) -> tuple[int, float, float] | None: ...
|
51
|
-
@overload
|
52
|
-
def nearest_neighbor(
|
53
|
-
self, xy: Point, *, as_item: _Literal[True]
|
54
|
-
) -> Item | None: ...
|
55
|
-
@overload
|
56
|
-
def nearest_neighbors(
|
57
|
-
self, xy: Point, k: int, *, as_items: _Literal[False] = ...
|
58
|
-
) -> list[tuple[int, float, float]]: ...
|
59
|
-
@overload
|
60
|
-
def nearest_neighbors(
|
61
|
-
self, xy: Point, k: int, *, as_items: _Literal[True]
|
62
|
-
) -> list[Item]: ...
|
63
|
-
|
64
|
-
# Misc
|
65
|
-
def get(self, id_: int) -> Any | None: ...
|
66
|
-
def get_all_rectangles(self) -> list[Bounds]: ...
|
67
|
-
def get_all_objects(self) -> list[Any]: ...
|
68
|
-
def get_all_items(self) -> list[Item]: ...
|
69
|
-
def count_items(self) -> int: ...
|
70
|
-
def __len__(self) -> int: ...
|
@@ -1,10 +0,0 @@
|
|
1
|
-
fastquadtree-0.6.1.dist-info/METADATA,sha256=H8lkeuXlgH2BCxZSzEvFAKnLANM4H52NG78hT7AObUk,10746
|
2
|
-
fastquadtree-0.6.1.dist-info/WHEEL,sha256=F7HFsRl56IeLjGhxiHeG3IpurHNaGNHQJeIy7gdwTO4,94
|
3
|
-
fastquadtree-0.6.1.dist-info/licenses/LICENSE,sha256=46IVFhoCIwMo-ocq4olyEB1eBvvtaKic5yGLeKXnDuc,1092
|
4
|
-
fastquadtree/__init__.py,sha256=o7zH6lOu2lTxMafl9FNVZiecM0KVxqhC3FNSRbpaaqY,12435
|
5
|
-
fastquadtree/__init__.pyi,sha256=_HGJ9lpIZ9-JTSfMKSn8aif_tu60m2vgOXR4d0uXnRY,2062
|
6
|
-
fastquadtree/_bimap.py,sha256=SPFFw9hWAVRKZVXyDGYFFNw4MLV8qUlOq8gDbLcTqkg,3402
|
7
|
-
fastquadtree/_item.py,sha256=p-ymnE9S-c8Lc00KhNKN9Pt4bd05nZ6g-DB8q8D9zYk,533
|
8
|
-
fastquadtree/_native.pyd,sha256=OHlc3FG-kZbjt2YW2QM9I809C1FRQlV6RQXcPcQLNhM,254464
|
9
|
-
fastquadtree/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
fastquadtree-0.6.1.dist-info/RECORD,,
|
File without changes
|