fastquadtree 0.7.0__tar.gz → 0.8.1__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.
Files changed (74) hide show
  1. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/Cargo.lock +1 -1
  2. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/Cargo.toml +1 -1
  3. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/PKG-INFO +17 -149
  4. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/README.md +13 -146
  5. fastquadtree-0.8.1/assets/ballpit.png +0 -0
  6. fastquadtree-0.8.1/assets/interactive_v2_rect_screenshot.png +0 -0
  7. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/benchmarks/benchmark_native_vs_shim.py +14 -8
  8. fastquadtree-0.8.1/benchmarks/cross_library_bench.py +6 -0
  9. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/benchmarks/quadtree_bench/engines.py +1 -1
  10. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/benchmarks/quadtree_bench/runner.py +2 -11
  11. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/benchmarks/requirements.txt +1 -2
  12. fastquadtree-0.8.1/benchmarks/system_info_collector.py +259 -0
  13. fastquadtree-0.8.1/docs/api/point_item.md +4 -0
  14. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/docs/api/quadtree.md +2 -0
  15. fastquadtree-0.8.1/docs/api/rect_item.md +4 -0
  16. fastquadtree-0.8.1/docs/api/rect_quadtree.md +4 -0
  17. fastquadtree-0.8.1/docs/benchmark.md +84 -0
  18. fastquadtree-0.8.1/docs/index.md +49 -0
  19. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/docs/quickstart.md +5 -15
  20. fastquadtree-0.8.1/docs/runnables.md +35 -0
  21. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/interactive/interactive.py +1 -1
  22. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/interactive/interactive_v2.py +1 -1
  23. fastquadtree-0.8.1/interactive/interactive_v2_rect.py +432 -0
  24. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/interactive/requirements.txt +1 -1
  25. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/mkdocs.yml +5 -1
  26. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/pyproject.toml +11 -4
  27. fastquadtree-0.8.1/pysrc/fastquadtree/__init__.py +5 -0
  28. fastquadtree-0.8.1/pysrc/fastquadtree/_base_quadtree.py +263 -0
  29. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/pysrc/fastquadtree/_bimap.py +22 -22
  30. fastquadtree-0.8.1/pysrc/fastquadtree/_item.py +52 -0
  31. fastquadtree-0.8.1/pysrc/fastquadtree/point_quadtree.py +161 -0
  32. fastquadtree-0.8.1/pysrc/fastquadtree/rect_quadtree.py +100 -0
  33. fastquadtree-0.8.1/src/lib.rs +169 -0
  34. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/src/quadtree.rs +1 -1
  35. fastquadtree-0.8.1/src/rect_quadtree.rs +295 -0
  36. fastquadtree-0.8.1/tests/rect_quadtree.rs +221 -0
  37. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/rectangle_traversal.rs +8 -8
  38. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/test_bimap.py +1 -1
  39. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/test_delete.rs +12 -12
  40. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/test_delete_python.py +4 -4
  41. fastquadtree-0.8.1/tests/test_rect_quadtree.py +199 -0
  42. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/test_unconventional_bounds.py +2 -2
  43. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/test_wrapper_edges.py +6 -6
  44. fastquadtree-0.7.0/assets/ballpit.png +0 -0
  45. fastquadtree-0.7.0/benchmarks/cross_library_bench.py +0 -3
  46. fastquadtree-0.7.0/benchmarks/quadtree_bench/system_info_collector.py +0 -140
  47. fastquadtree-0.7.0/docs/api/item.md +0 -2
  48. fastquadtree-0.7.0/docs/index.md +0 -4
  49. fastquadtree-0.7.0/pysrc/fastquadtree/__init__.py +0 -417
  50. fastquadtree-0.7.0/pysrc/fastquadtree/__init__.pyi +0 -71
  51. fastquadtree-0.7.0/pysrc/fastquadtree/_item.py +0 -24
  52. fastquadtree-0.7.0/src/lib.rs +0 -99
  53. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/.github/workflows/docs.yml +0 -0
  54. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/.github/workflows/release.yml +0 -0
  55. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/.gitignore +0 -0
  56. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/.pre-commit-config.yaml +0 -0
  57. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/LICENSE +0 -0
  58. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/assets/interactive_v2_screenshot.png +0 -0
  59. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/assets/quadtree_bench_throughput.png +0 -0
  60. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/assets/quadtree_bench_time.png +0 -0
  61. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/benchmarks/quadtree_bench/__init__.py +0 -0
  62. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/benchmarks/quadtree_bench/main.py +0 -0
  63. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/benchmarks/quadtree_bench/plotting.py +0 -0
  64. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/benchmarks/runner.py +0 -0
  65. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/interactive/ballpit.py +0 -0
  66. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/pysrc/fastquadtree/py.typed +0 -0
  67. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/src/geom.rs +0 -0
  68. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/insertions.rs +0 -0
  69. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/nearest_neighbor.rs +0 -0
  70. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/query.rs +0 -0
  71. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/test_clear.py +0 -0
  72. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/test_delete_by_object.py +0 -0
  73. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/test_python.py +0 -0
  74. {fastquadtree-0.7.0 → fastquadtree-0.8.1}/tests/unconventional_bounds.rs +0 -0
@@ -22,7 +22,7 @@ checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
22
22
 
23
23
  [[package]]
24
24
  name = "fastquadtree"
25
- version = "0.7.0"
25
+ version = "0.8.1"
26
26
  dependencies = [
27
27
  "pyo3",
28
28
  "smallvec",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "fastquadtree"
3
- version = "0.7.0"
3
+ version = "0.8.1"
4
4
  edition = "2021"
5
5
  readme = "README.md"
6
6
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastquadtree
3
- Version: 0.7.0
3
+ Version: 0.8.1
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3 :: Only
6
6
  Classifier: Programming Language :: Rust
@@ -12,8 +12,8 @@ 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>=7.0 ; extra == 'dev'
16
- Requires-Dist: pytest-cov>=4.1 ; extra == 'dev'
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'
@@ -23,6 +23,7 @@ Requires-Dist: mkdocstrings[python] ; extra == 'dev'
23
23
  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
+ Requires-Dist: maturin>=1.5 ; extra == 'dev'
26
27
  Provides-Extra: dev
27
28
  License-File: LICENSE
28
29
  Summary: Rust-accelerated quadtree for Python with fast inserts, range queries, and k-NN search.
@@ -45,7 +46,7 @@ Project-URL: Issues, https://github.com/Elan456/fastquadtree/issues
45
46
 
46
47
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/fastquadtree?period=total&units=INTERNATIONAL_SYSTEM&left_color=GRAY&right_color=BLUE&left_text=Total+Downloads)](https://pepy.tech/projects/fastquadtree)
47
48
 
48
- [![Build](https://github.com/Elan456/fastquadtree/actions/workflows/release.yml/badge.svg)](https://github.com/Elan456/fastquadtree/actions/workflows/ci.yml)
49
+ [![Build](https://github.com/Elan456/fastquadtree/actions/workflows/release.yml/badge.svg)](https://github.com/Elan456/fastquadtree/actions/workflows/release.yml)
49
50
  [![Codecov](https://codecov.io/gh/Elan456/fastquadtree/branch/main/graph/badge.svg)](https://codecov.io/gh/Elan456/fastquadtree)
50
51
 
51
52
  [![Rust core via PyO3](https://img.shields.io/badge/Rust-core%20via%20PyO3-orange)](https://pyo3.rs/)
@@ -76,7 +77,6 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
76
77
 
77
78
  ### Summary (largest dataset, PyQtree baseline)
78
79
  - Points: **250,000**, Queries: **500**
79
- --------------------
80
80
  - Fastest total: **fastquadtree** at **0.120 s**
81
81
 
82
82
  | Library | Build (s) | Query (s) | Total (s) | Speed vs PyQtree |
@@ -89,7 +89,7 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
89
89
  | PyQtree | 1.492 | 0.263 | 1.755 | 1.00× |
90
90
  | quads | 1.407 | 0.484 | 1.890 | 0.93× |
91
91
 
92
- #### Benchmark Configuration
92
+ ### Benchmark Configuration
93
93
  | Parameter | Value |
94
94
  |---|---:|
95
95
  | Bounds | (0, 0, 1000, 1000) |
@@ -97,11 +97,13 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
97
97
  | Max depth | 16 |
98
98
  | Queries per experiment | 500 |
99
99
 
100
+ See the [benchmark section](https://elan456.github.io/fastquadtree/benchmark/) for details.
101
+
100
102
  ## Install
101
103
 
102
104
  ```bash
103
105
  pip install fastquadtree
104
- ````
106
+ ```
105
107
 
106
108
  If you are developing locally:
107
109
 
@@ -111,77 +113,11 @@ maturin develop --release
111
113
  ```
112
114
 
113
115
  ## Quickstart
114
-
115
- ```python
116
- from fastquadtree import QuadTree
117
-
118
- # Bounds are (min_x, min_y, max_x, max_y)
119
- qt = QuadTree(bounds=(0, 0, 1000, 1000), capacity=20) # max_depth is optional
120
-
121
- # Insert points with auto ids
122
- id1 = qt.insert((10, 10))
123
- id2 = qt.insert((200, 300))
124
- id3 = qt.insert((999, 500), id=42) # you can supply your own id
125
-
126
- # Axis-aligned rectangle query
127
- hits = qt.query((0, 0, 250, 350)) # returns [(id, x, y), ...] by default
128
- print(hits) # e.g. [(1, 10.0, 10.0), (2, 200.0, 300.0)]
129
-
130
- # Nearest neighbor
131
- best = qt.nearest_neighbor((210, 310)) # -> (id, x, y) or None
132
- print(best)
133
-
134
- # k-nearest neighbors
135
- top3 = qt.nearest_neighbors((210, 310), 3)
136
- print(top3) # list of up to 3 (id, x, y) tuples
137
-
138
- # Delete items by ID and location
139
- deleted = qt.delete(id2, (200, 300)) # True if found and deleted
140
- print(f"Deleted: {deleted}")
141
- print(f"Remaining items: {qt.count_items()}")
142
-
143
- # For object tracking with track_objects=True
144
- qt_tracked = QuadTree((0, 0, 1000, 1000), capacity=4, track_objects=True)
145
- player1 = {"name": "Alice", "score": 100}
146
- player2 = {"name": "Bob", "score": 200}
147
-
148
- id1 = qt_tracked.insert((50, 50), obj=player1)
149
- id2 = qt_tracked.insert((150, 150), obj=player2)
150
-
151
- # Delete by object reference (O(1) lookup!)
152
- deleted = qt_tracked.delete_by_object(player1)
153
- print(f"Deleted player: {deleted}") # True
154
- ```
155
-
156
- ### Working with Python objects
157
-
158
- You can keep the tree pure and manage your own id → object map, or let the wrapper manage it.
159
-
160
- **Wrapper Managed Objects**
161
-
162
- ```python
163
- from fastquadtree import QuadTree
164
-
165
- qt = QuadTree((0, 0, 1000, 1000), capacity=16, track_objects=True)
166
-
167
- # Store the object alongside the point
168
- qt.insert((25, 40), obj={"name": "apple"})
169
-
170
- # Ask for Item objects within a bounding box
171
- items = qt.query((0, 0, 100, 100), as_items=True)
172
- for it in items:
173
- print(it.id, it.x, it.y, it.obj)
174
- ```
175
-
176
- You can also attach or replace an object later:
177
-
178
- ```python
179
- qt.attach(123, my_object) # binds object to id 123
180
- ```
116
+ [See the quickstart guide](https://elan456.github.io/fastquadtree/quickstart/)
181
117
 
182
118
  ## API
183
119
 
184
- [Full api for QuadTree](https://elan456.github.io/fastquadtree/api/quadtree/)
120
+ [See the full API](https://elan456.github.io/fastquadtree/api/quadtree/)
185
121
 
186
122
  ### `QuadTree(bounds, capacity, max_depth=None, track_objects=False, start_id=1)`
187
123
 
@@ -191,38 +127,17 @@ qt.attach(123, my_object) # binds object to id 123
191
127
  * `track_objects` — if `True`, the wrapper maintains an id → object map for convenience.
192
128
  * `start_id` — starting value for auto-assigned ids
193
129
 
194
- ### Core Methods
130
+ ### Key Methods
195
131
 
196
132
  - `insert(xy, *, id=None, obj=None) -> int`
197
133
 
198
- - `insert_many_points(points) -> int`
199
-
200
134
  - `query(rect, *, as_items=False) -> list`
201
135
 
202
136
  - `nearest_neighbor(xy, *, as_item=False) -> (id, x, y) | Item | None`
203
137
 
204
- - `nearest_neighbors(xy, k, *, as_items=False) -> list`
205
-
206
138
  - `delete(id, xy) -> bool`
207
139
 
208
- - `delete_by_object(obj) -> bool (requires track_objects=True)`
209
-
210
- - `clear(*, reset_ids=False) -> None`
211
-
212
- - `attach(id, obj) -> None (requires track_objects=True)`
213
-
214
- - `count_items() -> int`
215
-
216
- - `get(id) -> object | None`
217
-
218
- - `get_all_rectangles() -> list[tuple] (for visualization)`
219
-
220
- - `get_all_objects() -> list[object] (requires track_objects=True)`
221
-
222
- ### `Item` (returned when `as_items=True`)
223
-
224
- * Attributes: `id`, `x`, `y`, and a lazy `obj` property
225
- * 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/).
226
141
 
227
142
  ### Geometric conventions
228
143
 
@@ -236,52 +151,8 @@ qt.attach(123, my_object) # binds object to id 123
236
151
  * Choose `capacity` so that leaves keep a small batch of points. Typical values are 8 to 64.
237
152
  * If your data is very skewed, set a `max_depth` to prevent long chains.
238
153
  * For fastest local runs, use `maturin develop --release`.
239
- * The wrapper only maintains an ID -> Obj map only if the quadtree was constructed with `track_objects=True`. If you don't need it, leave it off for best performance. Look at the [Native vs Shim Benchmark](#native-vs-shim-benchmark) below for details.
240
-
241
-
242
- ### Native vs Shim Benchmark
243
-
244
- **Setup**
245
- - Points: 500,000
246
- - Queries: 500
247
- - Repeats: 5
248
-
249
- **Timing (seconds)**
250
-
251
- | Variant | Build | Query | Total |
252
- |---|---:|---:|---:|
253
- | Native | 0.483 | 4.380 | 4.863 |
254
- | Shim (no map) | 0.668 | 4.167 | 4.835 |
255
- | Shim (track+objs) | 1.153 | 4.458 | 5.610 |
256
-
257
- **Overhead vs Native**
258
-
259
- - No map: build 1.38x, query 0.95x, total 0.99x
260
- - Track + objs: build 2.39x, query 1.02x, total 1.15x
261
-
262
- ### Run benchmarks
263
- To run the benchmarks yourself, first install the dependencies:
264
-
265
- ```bash
266
- pip install -r benchmarks/requirements.txt
267
- ```
268
-
269
- Then run:
270
-
271
- ```bash
272
- python benchmarks/cross_library_bench.py
273
- python benchmarks/benchmark_native_vs_shim.py
274
- ```
275
-
276
- Check the CLI arguments for the cross-library benchmark in `benchmarks/quadtree_bench/main.py`.
277
-
278
- ## Run Visualizer
279
- A visualizer is included to help you understand how the quadtree subdivides space.
280
-
281
- ```bash
282
- pip install -r interactive/requirements.txt
283
- python interactive/interactive_v2.py
284
- ```
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.
285
156
 
286
157
  ### Pygame Ball Pit Demo
287
158
 
@@ -290,10 +161,7 @@ python interactive/interactive_v2.py
290
161
  A simple demo of moving objects with collision detection using **fastquadtree**.
291
162
  You can toggle between quadtree mode and brute-force mode to see the performance difference.
292
163
 
293
- ```bash
294
- pip install -r interactive/requirements.txt
295
- python interactive/ball_pit.py
296
- ```
164
+ See the [runnables guide](https://elan456.github.io/fastquadtree/runnables/) for setup instructions.
297
165
 
298
166
  ## FAQ
299
167
 
@@ -304,7 +172,7 @@ Allowed. For k-nearest, duplicates are de-duplicated by id. For range queries yo
304
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.
305
173
 
306
174
  **Can I store rectangles or circles?**
307
- The core stores points. To index objects with extent, insert whatever representative point you choose. For rectangles you can insert centers or build an AABB tree separately.
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.
308
176
 
309
177
  ## License
310
178
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/fastquadtree?period=total&units=INTERNATIONAL_SYSTEM&left_color=GRAY&right_color=BLUE&left_text=Total+Downloads)](https://pepy.tech/projects/fastquadtree)
10
10
 
11
- [![Build](https://github.com/Elan456/fastquadtree/actions/workflows/release.yml/badge.svg)](https://github.com/Elan456/fastquadtree/actions/workflows/ci.yml)
11
+ [![Build](https://github.com/Elan456/fastquadtree/actions/workflows/release.yml/badge.svg)](https://github.com/Elan456/fastquadtree/actions/workflows/release.yml)
12
12
  [![Codecov](https://codecov.io/gh/Elan456/fastquadtree/branch/main/graph/badge.svg)](https://codecov.io/gh/Elan456/fastquadtree)
13
13
 
14
14
  [![Rust core via PyO3](https://img.shields.io/badge/Rust-core%20via%20PyO3-orange)](https://pyo3.rs/)
@@ -39,7 +39,6 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
39
39
 
40
40
  ### Summary (largest dataset, PyQtree baseline)
41
41
  - Points: **250,000**, Queries: **500**
42
- --------------------
43
42
  - Fastest total: **fastquadtree** at **0.120 s**
44
43
 
45
44
  | Library | Build (s) | Query (s) | Total (s) | Speed vs PyQtree |
@@ -52,7 +51,7 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
52
51
  | PyQtree | 1.492 | 0.263 | 1.755 | 1.00× |
53
52
  | quads | 1.407 | 0.484 | 1.890 | 0.93× |
54
53
 
55
- #### Benchmark Configuration
54
+ ### Benchmark Configuration
56
55
  | Parameter | Value |
57
56
  |---|---:|
58
57
  | Bounds | (0, 0, 1000, 1000) |
@@ -60,11 +59,13 @@ fastquadtree **outperforms** all other quadtree Python packages, including the R
60
59
  | Max depth | 16 |
61
60
  | Queries per experiment | 500 |
62
61
 
62
+ See the [benchmark section](https://elan456.github.io/fastquadtree/benchmark/) for details.
63
+
63
64
  ## Install
64
65
 
65
66
  ```bash
66
67
  pip install fastquadtree
67
- ````
68
+ ```
68
69
 
69
70
  If you are developing locally:
70
71
 
@@ -74,77 +75,11 @@ maturin develop --release
74
75
  ```
75
76
 
76
77
  ## Quickstart
77
-
78
- ```python
79
- from fastquadtree import QuadTree
80
-
81
- # Bounds are (min_x, min_y, max_x, max_y)
82
- qt = QuadTree(bounds=(0, 0, 1000, 1000), capacity=20) # max_depth is optional
83
-
84
- # Insert points with auto ids
85
- id1 = qt.insert((10, 10))
86
- id2 = qt.insert((200, 300))
87
- id3 = qt.insert((999, 500), id=42) # you can supply your own id
88
-
89
- # Axis-aligned rectangle query
90
- hits = qt.query((0, 0, 250, 350)) # returns [(id, x, y), ...] by default
91
- print(hits) # e.g. [(1, 10.0, 10.0), (2, 200.0, 300.0)]
92
-
93
- # Nearest neighbor
94
- best = qt.nearest_neighbor((210, 310)) # -> (id, x, y) or None
95
- print(best)
96
-
97
- # k-nearest neighbors
98
- top3 = qt.nearest_neighbors((210, 310), 3)
99
- print(top3) # list of up to 3 (id, x, y) tuples
100
-
101
- # Delete items by ID and location
102
- deleted = qt.delete(id2, (200, 300)) # True if found and deleted
103
- print(f"Deleted: {deleted}")
104
- print(f"Remaining items: {qt.count_items()}")
105
-
106
- # For object tracking with track_objects=True
107
- qt_tracked = QuadTree((0, 0, 1000, 1000), capacity=4, track_objects=True)
108
- player1 = {"name": "Alice", "score": 100}
109
- player2 = {"name": "Bob", "score": 200}
110
-
111
- id1 = qt_tracked.insert((50, 50), obj=player1)
112
- id2 = qt_tracked.insert((150, 150), obj=player2)
113
-
114
- # Delete by object reference (O(1) lookup!)
115
- deleted = qt_tracked.delete_by_object(player1)
116
- print(f"Deleted player: {deleted}") # True
117
- ```
118
-
119
- ### Working with Python objects
120
-
121
- You can keep the tree pure and manage your own id → object map, or let the wrapper manage it.
122
-
123
- **Wrapper Managed Objects**
124
-
125
- ```python
126
- from fastquadtree import QuadTree
127
-
128
- qt = QuadTree((0, 0, 1000, 1000), capacity=16, track_objects=True)
129
-
130
- # Store the object alongside the point
131
- qt.insert((25, 40), obj={"name": "apple"})
132
-
133
- # Ask for Item objects within a bounding box
134
- items = qt.query((0, 0, 100, 100), as_items=True)
135
- for it in items:
136
- print(it.id, it.x, it.y, it.obj)
137
- ```
138
-
139
- You can also attach or replace an object later:
140
-
141
- ```python
142
- qt.attach(123, my_object) # binds object to id 123
143
- ```
78
+ [See the quickstart guide](https://elan456.github.io/fastquadtree/quickstart/)
144
79
 
145
80
  ## API
146
81
 
147
- [Full api for QuadTree](https://elan456.github.io/fastquadtree/api/quadtree/)
82
+ [See the full API](https://elan456.github.io/fastquadtree/api/quadtree/)
148
83
 
149
84
  ### `QuadTree(bounds, capacity, max_depth=None, track_objects=False, start_id=1)`
150
85
 
@@ -154,38 +89,17 @@ qt.attach(123, my_object) # binds object to id 123
154
89
  * `track_objects` — if `True`, the wrapper maintains an id → object map for convenience.
155
90
  * `start_id` — starting value for auto-assigned ids
156
91
 
157
- ### Core Methods
92
+ ### Key Methods
158
93
 
159
94
  - `insert(xy, *, id=None, obj=None) -> int`
160
95
 
161
- - `insert_many_points(points) -> int`
162
-
163
96
  - `query(rect, *, as_items=False) -> list`
164
97
 
165
98
  - `nearest_neighbor(xy, *, as_item=False) -> (id, x, y) | Item | None`
166
99
 
167
- - `nearest_neighbors(xy, k, *, as_items=False) -> list`
168
-
169
100
  - `delete(id, xy) -> bool`
170
101
 
171
- - `delete_by_object(obj) -> bool (requires track_objects=True)`
172
-
173
- - `clear(*, reset_ids=False) -> None`
174
-
175
- - `attach(id, obj) -> None (requires track_objects=True)`
176
-
177
- - `count_items() -> int`
178
-
179
- - `get(id) -> object | None`
180
-
181
- - `get_all_rectangles() -> list[tuple] (for visualization)`
182
-
183
- - `get_all_objects() -> list[object] (requires track_objects=True)`
184
-
185
- ### `Item` (returned when `as_items=True`)
186
-
187
- * Attributes: `id`, `x`, `y`, and a lazy `obj` property
188
- * Accessing `obj` performs a dictionary lookup only if tracking is enabled
102
+ There are more methods and object tracking versions in the [docs](https://elan456.github.io/fastquadtree/api/quadtree/).
189
103
 
190
104
  ### Geometric conventions
191
105
 
@@ -199,52 +113,8 @@ qt.attach(123, my_object) # binds object to id 123
199
113
  * Choose `capacity` so that leaves keep a small batch of points. Typical values are 8 to 64.
200
114
  * If your data is very skewed, set a `max_depth` to prevent long chains.
201
115
  * For fastest local runs, use `maturin develop --release`.
202
- * The wrapper only maintains an ID -> Obj map only if the quadtree was constructed with `track_objects=True`. If you don't need it, leave it off for best performance. Look at the [Native vs Shim Benchmark](#native-vs-shim-benchmark) below for details.
203
-
204
-
205
- ### Native vs Shim Benchmark
206
-
207
- **Setup**
208
- - Points: 500,000
209
- - Queries: 500
210
- - Repeats: 5
211
-
212
- **Timing (seconds)**
213
-
214
- | Variant | Build | Query | Total |
215
- |---|---:|---:|---:|
216
- | Native | 0.483 | 4.380 | 4.863 |
217
- | Shim (no map) | 0.668 | 4.167 | 4.835 |
218
- | Shim (track+objs) | 1.153 | 4.458 | 5.610 |
219
-
220
- **Overhead vs Native**
221
-
222
- - No map: build 1.38x, query 0.95x, total 0.99x
223
- - Track + objs: build 2.39x, query 1.02x, total 1.15x
224
-
225
- ### Run benchmarks
226
- To run the benchmarks yourself, first install the dependencies:
227
-
228
- ```bash
229
- pip install -r benchmarks/requirements.txt
230
- ```
231
-
232
- Then run:
233
-
234
- ```bash
235
- python benchmarks/cross_library_bench.py
236
- python benchmarks/benchmark_native_vs_shim.py
237
- ```
238
-
239
- Check the CLI arguments for the cross-library benchmark in `benchmarks/quadtree_bench/main.py`.
240
-
241
- ## Run Visualizer
242
- A visualizer is included to help you understand how the quadtree subdivides space.
243
-
244
- ```bash
245
- pip install -r interactive/requirements.txt
246
- python interactive/interactive_v2.py
247
- ```
116
+ * 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.
117
+ * Refer to the [Native vs Shim Benchmark](https://elan456.github.io/fastquadtree/benchmark/#native-vs-shim-benchmark) for overhead details.
248
118
 
249
119
  ### Pygame Ball Pit Demo
250
120
 
@@ -253,10 +123,7 @@ python interactive/interactive_v2.py
253
123
  A simple demo of moving objects with collision detection using **fastquadtree**.
254
124
  You can toggle between quadtree mode and brute-force mode to see the performance difference.
255
125
 
256
- ```bash
257
- pip install -r interactive/requirements.txt
258
- python interactive/ball_pit.py
259
- ```
126
+ See the [runnables guide](https://elan456.github.io/fastquadtree/runnables/) for setup instructions.
260
127
 
261
128
  ## FAQ
262
129
 
@@ -267,7 +134,7 @@ Allowed. For k-nearest, duplicates are de-duplicated by id. For range queries yo
267
134
  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.
268
135
 
269
136
  **Can I store rectangles or circles?**
270
- The core stores points. To index objects with extent, insert whatever representative point you choose. For rectangles you can insert centers or build an AABB tree separately.
137
+ 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.
271
138
 
272
139
  ## License
273
140
 
Binary file
@@ -7,6 +7,7 @@ import random
7
7
  import statistics as stats
8
8
  from time import perf_counter as now
9
9
 
10
+ from system_info_collector import collect_system_info, format_system_info_markdown_lite
10
11
  from tqdm import tqdm
11
12
 
12
13
  from fastquadtree import QuadTree as ShimQuadTree
@@ -55,10 +56,10 @@ def bench_shim(points, queries, *, track_objects: bool, with_objs: bool):
55
56
  )
56
57
  if with_objs:
57
58
  for i, p in enumerate(points):
58
- qt.insert(p, id=i, obj=i) # store a tiny object
59
+ qt.insert(p, id_=i, obj=i) # store a tiny object
59
60
  else:
60
61
  for i, p in enumerate(points):
61
- qt.insert(p, id=i)
62
+ qt.insert(p, id_=i)
62
63
  t_build = now() - t0
63
64
 
64
65
  t0 = now()
@@ -133,14 +134,14 @@ def main():
133
134
  return f"{x:.3f}"
134
135
 
135
136
  md = f"""
136
- ### Native vs Shim
137
+ ## Native vs Shim
137
138
 
138
- **Setup**
139
+ ### Configuration
139
140
  - Points: {args.points:,}
140
141
  - Queries: {args.queries}
141
142
  - Repeats: {args.repeats}
142
143
 
143
- **Timing (seconds)**
144
+ ### Results
144
145
 
145
146
  | Variant | Build | Query | Total |
146
147
  |---|---:|---:|---:|
@@ -148,13 +149,18 @@ def main():
148
149
  | Shim (no map) | {fmt(s_build_no_map)} | {fmt(s_query_no_map)} | {fmt(s_build_no_map + s_query_no_map)} |
149
150
  | Shim (track+objs) | {fmt(s_build_map)} | {fmt(s_query_map)} | {fmt(s_build_map + s_query_map)} |
150
151
 
151
- **Overhead vs Native**
152
+ ### Summary
152
153
 
153
- - No map: build {s_build_no_map / n_build:.2f}x, query {s_query_no_map / n_query:.2f}x, total {(s_build_no_map + s_query_no_map) / (n_build + n_query):.2f}x
154
- - Track + objs: build {s_build_map / n_build:.2f}x, query {s_query_map / n_query:.2f}x, total {(s_build_map + s_query_map) / (n_build + n_query):.2f}x
154
+ 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
+ **Total slowdown = {fmt((s_build_map + s_query_map) / (n_build + n_query))}x.**
156
+
157
+ Adding the object map only impacts the build time, not the query time.
155
158
  """
156
159
  print(md.strip())
157
160
 
161
+ info = collect_system_info()
162
+ print(format_system_info_markdown_lite(info))
163
+
158
164
 
159
165
  if __name__ == "__main__":
160
166
  main()
@@ -0,0 +1,6 @@
1
+ import system_info_collector
2
+ from quadtree_bench.main import main
3
+
4
+ main()
5
+ system_info = system_info_collector.collect_system_info()
6
+ print(system_info_collector.format_system_info_markdown_lite(system_info))
@@ -104,7 +104,7 @@ def _create_fastquadtree_engine(
104
104
 
105
105
  def build(points):
106
106
  qt = RustQuadTree(bounds, max_points, max_depth=max_depth)
107
- qt.insert_many_points(points)
107
+ qt.insert_many(points)
108
108
  return qt
109
109
 
110
110
  def query(qt, queries):
@@ -6,7 +6,6 @@ and result collection for performance analysis.
6
6
  """
7
7
 
8
8
  import gc
9
- import json
10
9
  import math
11
10
  import random
12
11
  import statistics as stats
@@ -17,7 +16,6 @@ from typing import Any, Dict, List, Tuple
17
16
  from tqdm import tqdm
18
17
 
19
18
  from .engines import Engine
20
- from .system_info_collector import collect_system_info
21
19
 
22
20
 
23
21
  @dataclass
@@ -257,8 +255,8 @@ class BenchmarkRunner:
257
255
  experiment_bar.close()
258
256
 
259
257
  # Add metadata to results
260
- results["engines"] = engines
261
- results["config"] = self.config
258
+ results["engines"] = engines # pyright: ignore[reportArgumentType]
259
+ results["config"] = self.config # pyright: ignore[reportArgumentType]
262
260
 
263
261
  return results
264
262
 
@@ -279,7 +277,6 @@ class BenchmarkRunner:
279
277
  print(
280
278
  f"- Points: **{config.experiments[i]:,}**, Queries: **{config.n_queries}**"
281
279
  )
282
- print("--------------------")
283
280
 
284
281
  # Find fastest and show key results
285
282
  ranked = sorted(
@@ -317,9 +314,3 @@ class BenchmarkRunner:
317
314
  print(f"| Max points per node | {config.max_points} |")
318
315
  print(f"| Max depth | {config.max_depth} |")
319
316
  print(f"| Queries per experiment | {config.n_queries} |")
320
-
321
- system_info = collect_system_info()
322
- print("#### System Info")
323
- print("```json")
324
- print(json.dumps(system_info, indent=2))
325
- print("```")
@@ -17,5 +17,4 @@ shapely>=2.0.0 # STRtree comparator
17
17
  # For getting system info
18
18
  psutil
19
19
  py-cpuinfo
20
- distro
21
- GPUtil
20
+ distro