Skip to content

Commit 38ab9ae

Browse files
committed
Removed sorting in query_circled_points
1 parent 1f34224 commit 38ab9ae

File tree

8 files changed

+96
-28
lines changed

8 files changed

+96
-28
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## [0.7.0] - 2025-11-15
4+
- `query_circle_points` returns unsorted points
5+
- Fix documentation
6+
37
## [0.6.13] - 2025-11-08
48
- Fix documentation
59

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22

33
name = "aabb"
4-
version = "0.6.14"
4+
version = "0.7.0"
55
description = "Static AABB spatial index for 2D queries"
66
rust-version = "1.88"
77
edition = "2024"

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
55

66

7-
A Rust library providing a simple and efficient Hilbert R-tree implementation for spatial queries on axis-aligned bounding boxes (AABBs).
7+
A Rust library providing efficient Hilbert R-tree implementation for spatial queries on axis-aligned bounding boxes (AABBs).
88

99
## Features
1010

@@ -20,7 +20,7 @@ Add this to your `Cargo.toml`:
2020

2121
```toml
2222
[dependencies]
23-
aabb = "0.6"
23+
aabb = "0.7"
2424
```
2525

2626
### Basic Example
@@ -71,7 +71,6 @@ fn main() {
7171
tree.query_circle_points(0.0, 0.0, 2.5, &mut results);
7272

7373
println!("Found {} points within radius 2.5", results.len());
74-
// Results are sorted by distance (closest first)
7574

7675
// Find K nearest points
7776
let mut results = Vec::new();
@@ -122,10 +121,10 @@ The curve preserves spatial locality - points close to each other in 2D space te
122121
- `query_circle(center_x, center_y, radius, results)` `(f64)` - Find boxes intersecting a circular region
123122

124123
#### Point-Specific Optimized Queries
125-
- `query_nearest_k_points(x, y, k, results)` `(f64)` - **Optimized** - Find K nearest points (stored as (x, x, y, y)) - ~30% faster than `query_nearest_k` for point clouds
126-
- `query_circle_points(center_x, center_y, radius, results)` `(f64)` - **Optimized** - Find points within a circular region with distance-sorted results - ~30% faster than `query_circle` for point clouds
124+
- `query_nearest_k_points(x, y, k, results)` `(f64)` - Find K nearest points (stored as (x, x, y, y)), sorted by distance
125+
- `query_circle_points(center_x, center_y, radius, results)` `(f64)` - Find points within a circular region (optimized for point data)
127126

128-
**Note:** Point-specific methods assume all items in the tree are stored as degenerate boxes (points) where `min_x == max_x` and `min_y == max_y`. For mixed data (both points and boxes), use the general methods instead. Results from point-specific queries are automatically sorted by distance (closest first).
127+
**Note:** Point-specific methods assume all items in the tree are stored as degenerate boxes (points) where `min_x == max_x` and `min_y == max_y`. For mixed data (both points and boxes), use the general methods instead.
129128

130129
#### Directional Queries
131130
- `query_in_direction(rect_min_x, rect_min_y, rect_max_x, rect_max_y, direction_x, direction_y, distance, results)` `(f64)` - Find boxes intersecting a rectangle's movement path

benches/profile_bench.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,69 @@ fn main() {
273273
results.len() as f64
274274
);
275275

276+
// Build a tree with point data for point-specific queries
277+
println!("\n\nPoint Cloud Queries");
278+
println!("====================");
279+
println!("\nBuilding point cloud index {} points...", num_items);
280+
281+
let mut point_tree = HilbertRTree::with_capacity(num_items);
282+
for _ in 0..num_items {
283+
let x = rng.random_range(0.0..100.0);
284+
let y = rng.random_range(0.0..100.0);
285+
point_tree.add_point(x, y);
286+
}
287+
let build_point_start = Instant::now();
288+
point_tree.build();
289+
let build_point_time = build_point_start.elapsed();
290+
println!(" Built in {:.2}ms\n", build_point_time.as_secs_f64() * 1000.0);
291+
292+
// Profile query_circle_points
293+
println!("Profiling query_circle_points:");
294+
println!("{}", "-".repeat(40));
295+
296+
let query_start = Instant::now();
297+
for i in 0..num_tests {
298+
results.clear();
299+
let x = coords[4 * i];
300+
let y = coords[4 * i + 1];
301+
point_tree.query_circle_points(x, y, 5.0, &mut results);
302+
}
303+
let elapsed = query_start.elapsed();
304+
println!(
305+
" {} queries (radius=5): {:.2}ms ({:.3}µs/query, avg {:.1} results)",
306+
num_tests,
307+
elapsed.as_secs_f64() * 1000.0,
308+
elapsed.as_secs_f64() * 1_000_000.0 / num_tests as f64,
309+
results.len() as f64
310+
);
311+
312+
// Profile query_nearest_k_points with different K values
313+
println!("\nProfiling query_nearest_k_points:");
314+
println!("{}", "-".repeat(40));
315+
316+
let k_values = vec![1, 10, 100, 1000];
317+
318+
for k in k_values {
319+
let num_queries = if k == 1000 { 100 } else { num_tests };
320+
321+
let query_start = Instant::now();
322+
for i in 0..num_queries {
323+
let x = coords[4 * i];
324+
let y = coords[4 * i + 1];
325+
point_tree.query_nearest_k_points(x, y, k, &mut results);
326+
}
327+
let elapsed = query_start.elapsed();
328+
329+
println!(
330+
" {} queries k={}: {:.2}ms ({:.3}µs/query, avg {:.1} results)",
331+
num_queries,
332+
k,
333+
elapsed.as_secs_f64() * 1000.0,
334+
elapsed.as_secs_f64() * 1_000_000.0 / num_queries as f64,
335+
results.len() as f64
336+
);
337+
}
338+
276339
println!("\n{}", "=".repeat(50));
277340
println!("\nSummary:");
278341
println!("--------");
@@ -395,6 +458,7 @@ Summary:
395458
--------
396459
Build dominates: 21.1%
397460
Queries (total): 356.81ms
461+
___________________________________________________________________________________
398462
399463
400464
*/

src/hilbert_rtree.rs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,8 +1497,9 @@ impl HilbertRTree {
14971497
///
14981498
/// This is an optimized version of `query_circle()` specifically for point data.
14991499
/// For point items where min_x==max_x and min_y==max_y, this method computes
1500-
/// distances directly without the axis_distance() helper function, providing
1501-
/// approximately 30% faster queries on point clouds.
1500+
/// distances directly without the axis_distance() helper function for leaf nodes.
1501+
/// For parent nodes, axis_distance is used to maintain correctness.
1502+
/// This optimization provides approximately 30% faster queries on point clouds.
15021503
///
15031504
/// **Important:** This method assumes all items in the tree are stored as points.
15041505
/// If the tree contains mixed data (both points and boxes), use `query_circle()` instead
@@ -1509,7 +1510,7 @@ impl HilbertRTree {
15091510
/// * `center_y` - Y coordinate of circle center
15101511
/// * `radius` - Radius of the circular region
15111512
/// * `results` - Output vector; will be cleared and populated with indices of all point items
1512-
/// within the circular region, sorted by distance (closest first)
1513+
/// within the circular region
15131514
///
15141515
/// # Example
15151516
/// ```
@@ -1522,7 +1523,7 @@ impl HilbertRTree {
15221523
///
15231524
/// let mut results = Vec::new();
15241525
/// tree.query_circle_points(0.0, 0.0, 1.5, &mut results);
1525-
/// // Results include points 0 and 1 (within radius), ordered by distance
1526+
/// // Results include points 0 and 1 (within radius)
15261527
/// ```
15271528
pub fn query_circle_points(&self, center_x: f64, center_y: f64, radius: f64, results: &mut Vec<usize>) {
15281529
if self.num_items == 0 || self.level_bounds.is_empty() || radius < 0.0 {
@@ -1533,8 +1534,6 @@ impl HilbertRTree {
15331534
results.clear();
15341535
let radius_sq = radius * radius;
15351536
let mut queue = VecDeque::new();
1536-
let mut candidates: Vec<(f64, usize)> = Vec::new();
1537-
15381537
let mut node_index = self.total_nodes - 1;
15391538

15401539
loop {
@@ -1563,7 +1562,7 @@ impl HilbertRTree {
15631562
if pos >= self.num_items {
15641563
queue.push_back((index >> 2) as usize);
15651564
} else {
1566-
candidates.push((dist_sq, index as usize));
1565+
results.push(index as usize);
15671566
}
15681567
}
15691568
}
@@ -1574,12 +1573,6 @@ impl HilbertRTree {
15741573

15751574
node_index = queue.pop_front().unwrap();
15761575
}
1577-
1578-
// Sort by distance ascending
1579-
candidates.sort_unstable_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
1580-
for (_, idx) in candidates {
1581-
results.push(idx);
1582-
}
15831576
}
15841577

15851578
/// Finds all boxes that intersect with a rectangle's directional movement path.

src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@
9696
//! - [`query_nearest_k`] `(f64)` - Find K nearest boxes to a point (use k=1 for single nearest)
9797
//! - [`query_circle`] `(f64)` - Find boxes intersecting a circular region
9898
//!
99+
//! ### Point-Specific Optimized Queries
100+
//! - [`query_nearest_k_points`] `(f64)` - Find K nearest points (stored as (x, x, y, y)), sorted by distance
101+
//! - [`query_circle_points`] `(f64)` - Find points within a circular region (optimized for point data)
102+
//!
103+
//! **Note:** Point-specific methods assume all items in the tree are stored as degenerate boxes (points)
104+
//! where `min_x == max_x` and `min_y == max_y`. For mixed data (both points and boxes), use the general methods instead.
105+
//!
99106
//! ### Directional Queries
100107
//! - [`query_in_direction`] `(f64)` - Find boxes intersecting a rectangle's movement path
101108
//! - [`query_in_direction_k`] `(f64)` - Find K nearest boxes intersecting a rectangle's movement path
@@ -108,6 +115,8 @@
108115
//! [`query_contained_within`]: HilbertRTree::query_contained_within
109116
//! [`query_nearest_k`]: HilbertRTree::query_nearest_k
110117
//! [`query_circle`]: HilbertRTree::query_circle
118+
//! [`query_nearest_k_points`]: HilbertRTree::query_nearest_k_points
119+
//! [`query_circle_points`]: HilbertRTree::query_circle_points
111120
//! [`query_in_direction`]: HilbertRTree::query_in_direction
112121
//! [`query_in_direction_k`]: HilbertRTree::query_in_direction_k
113122
//!

src/test_point_optimizations.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,13 @@ mod test_point_optimizations {
162162
let mut results = Vec::new();
163163
tree.query_circle_points(0.0, 0.0, 6.0, &mut results);
164164

165-
// All 4 points within radius 6
165+
// All 4 points within radius 6 (order depends on tree traversal)
166166
assert_eq!(results.len(), 4);
167-
// Should be sorted by distance
168-
// Point 3 (dist 0), Point 1 (dist 1), Point 2 (dist 2), Point 0 (dist 5)
169-
assert_eq!(results[0], 3);
170-
assert_eq!(results[1], 1);
171-
assert_eq!(results[2], 2);
172-
assert_eq!(results[3], 0);
167+
// Verify all expected points are in results (unordered)
168+
assert!(results.contains(&0));
169+
assert!(results.contains(&1));
170+
assert!(results.contains(&2));
171+
assert!(results.contains(&3));
173172
}
174173

175174
/// Test query_nearest_k_points with negative coordinates

0 commit comments

Comments
 (0)