1#[cfg(test)]
4mod tests {
5 use crate::graph::NodeBuilder;
6 use crate::types::{Position, Rect, Viewport};
7
8 use crate::spatial::SpatialIndex;
9 use crate::spatial::SpatialQuery;
10
11 #[test]
12 fn test_spatial_index_creation() {
13 let index = SpatialIndex::new();
14 assert!(index.is_empty());
15 assert_eq!(index.len(), 0);
16 }
17
18 #[test]
19 fn test_insert_and_query() {
20 let mut index = SpatialIndex::new();
21
22 let node = NodeBuilder::<()>::new("test-node")
23 .position(10.0, 10.0)
24 .size(50.0, 30.0)
25 .build();
26
27 index.insert(&node).unwrap();
28 assert_eq!(index.len(), 1);
29
30 let query_bounds = Rect::new(0.0, 0.0, 50.0, 50.0);
32 let results = index.query_rect(&query_bounds);
33 assert_eq!(results.len(), 1);
34 assert_eq!(results[0], node.id.clone());
35 }
36
37 #[test]
38 fn test_remove_node() {
39 let mut index = SpatialIndex::new();
40
41 let node = NodeBuilder::<()>::new("test-node")
42 .position(10.0, 10.0)
43 .size(20.0, 20.0)
44 .build();
45
46 index.insert(&node).unwrap();
47 assert_eq!(index.len(), 1);
48
49 let removed = index.remove(&node.id);
51 assert!(removed);
52 assert_eq!(index.len(), 0);
53
54 let query_bounds = Rect::new(0.0, 0.0, 50.0, 50.0);
56 let results = index.query_rect(&query_bounds);
57 assert_eq!(results.len(), 0);
58 }
59
60 #[test]
61 fn test_update_node() {
62 let mut index = SpatialIndex::new();
63
64 let mut node = NodeBuilder::<()>::new("test-node")
65 .position(10.0, 10.0)
66 .size(20.0, 20.0)
67 .build();
68
69 index.insert(&node).unwrap();
70
71 node.set_position(Position::new(100.0, 100.0));
73 index.update(&node).unwrap();
74
75 let old_query = Rect::new(0.0, 0.0, 50.0, 50.0);
77 let results = index.query_rect(&old_query);
78 assert_eq!(results.len(), 0);
79
80 let new_query = Rect::new(90.0, 90.0, 50.0, 50.0);
82 let results = index.query_rect(&new_query);
83 assert_eq!(results.len(), 1);
84 }
85
86 #[test]
87 fn test_bulk_load() {
88 let mut index = SpatialIndex::new();
89
90 let nodes = vec![
91 NodeBuilder::<()>::new("node1")
92 .position(10.0, 10.0)
93 .size(20.0, 20.0)
94 .build(),
95 NodeBuilder::<()>::new("node2")
96 .position(50.0, 50.0)
97 .size(20.0, 20.0)
98 .build(),
99 NodeBuilder::<()>::new("node3")
100 .position(90.0, 90.0)
101 .size(20.0, 20.0)
102 .build(),
103 ];
104
105 index.bulk_load(&nodes).unwrap();
106 assert_eq!(index.len(), 3);
107
108 let query_bounds = Rect::new(0.0, 0.0, 120.0, 120.0);
110 let results = index.query_rect(&query_bounds);
111 assert_eq!(results.len(), 3);
112 }
113
114 #[test]
115 fn test_spatial_query_builder() {
116 let mut index = SpatialIndex::new();
117
118 let nodes = vec![
119 NodeBuilder::<()>::new("node1")
120 .position(10.0, 10.0)
121 .size(20.0, 20.0)
122 .build(),
123 NodeBuilder::<()>::new("node2")
124 .position(50.0, 50.0)
125 .size(20.0, 20.0)
126 .build(),
127 NodeBuilder::<()>::new("node3")
128 .position(90.0, 90.0)
129 .size(20.0, 20.0)
130 .build(),
131 ];
132
133 index.bulk_load(&nodes).unwrap();
134
135 let results = SpatialQuery::new(&index)
137 .bounds(Rect::new(0.0, 0.0, 120.0, 120.0))
138 .limit(2)
139 .execute();
140
141 assert_eq!(results.len(), 2);
142
143 let results = SpatialQuery::new(&index)
145 .radius(Position::new(60.0, 60.0), 30.0)
146 .execute();
147
148 assert!(!results.is_empty());
149 }
150
151 #[test]
152 fn test_grid_cells() {
153 let index = SpatialIndex::with_cell_size(50.0);
154
155 let bounds = Rect::new(10.0, 10.0, 20.0, 20.0);
157 let cells = index.get_grid_cells_for_bounds(&bounds);
158 assert_eq!(cells.len(), 1);
159
160 let bounds = Rect::new(10.0, 10.0, 100.0, 100.0);
162 let cells = index.get_grid_cells_for_bounds(&bounds);
163 assert_eq!(cells.len(), 9); }
165
166 #[test]
167 fn test_viewport_query() {
168 let mut index = SpatialIndex::new();
169
170 let node = NodeBuilder::<()>::new("test-node")
171 .position(50.0, 50.0)
172 .size(20.0, 20.0)
173 .build();
174
175 index.insert(&node).unwrap();
176
177 let viewport = Viewport::with_size(0.0, 0.0, 100.0, 100.0);
179 let results = index.query_viewport(&viewport);
180 assert_eq!(results.len(), 1);
181
182 let viewport = Viewport::with_size(200.0, 200.0, 50.0, 50.0);
184 let results = index.query_viewport(&viewport);
185 assert_eq!(results.len(), 0);
186 }
187
188 #[test]
189 fn test_radius_query() {
190 let mut index = SpatialIndex::new();
191
192 let node = NodeBuilder::<()>::new("test-node")
193 .position(50.0, 50.0)
194 .size(20.0, 20.0)
195 .build();
196
197 index.insert(&node).unwrap();
198
199 let results = index.query_radius(Position::new(60.0, 60.0), 20.0);
201 assert_eq!(results.len(), 1);
202
203 let results = index.query_radius(Position::new(100.0, 100.0), 10.0);
205 assert_eq!(results.len(), 0);
206 }
207
208 #[test]
209 fn test_nearest_query() {
210 let mut index = SpatialIndex::new();
211
212 let node1 = NodeBuilder::<()>::new("node1")
213 .position(10.0, 10.0)
214 .size(20.0, 20.0)
215 .build();
216
217 let node2 = NodeBuilder::<()>::new("node2")
218 .position(100.0, 100.0)
219 .size(20.0, 20.0)
220 .build();
221
222 index.insert(&node1).unwrap();
223 index.insert(&node2).unwrap();
224
225 let nearest = index.nearest(Position::new(20.0, 20.0));
227 assert_eq!(nearest, Some(node1.id.clone()));
228
229 let nearest = index.nearest(Position::new(110.0, 110.0));
231 assert_eq!(nearest, Some(node2.id.clone()));
232 }
233
234 #[test]
235 fn test_bounds_calculation() {
236 let mut index = SpatialIndex::new();
237
238 assert_eq!(index.bounds(), None);
240
241 let node1 = NodeBuilder::<()>::new("node1")
242 .position(10.0, 10.0)
243 .size(20.0, 20.0)
244 .build();
245
246 let node2 = NodeBuilder::<()>::new("node2")
247 .position(50.0, 50.0)
248 .size(30.0, 30.0)
249 .build();
250
251 index.insert(&node1).unwrap();
252 index.insert(&node2).unwrap();
253
254 let bounds = index.bounds().unwrap();
255 assert_eq!(bounds.x, 10.0);
256 assert_eq!(bounds.y, 10.0);
257 assert_eq!(bounds.width, 70.0); assert_eq!(bounds.height, 70.0); }
260
261 #[test]
262 fn test_custom_cell_size() {
263 let index = SpatialIndex::with_cell_size(25.0);
264 assert_eq!(index.cell_size(), 25.0);
265 }
266
267 #[test]
268 fn test_default_implementation() {
269 let index = SpatialIndex::default();
270 assert!(index.is_empty());
271 assert_eq!(index.len(), 0);
272 assert_eq!(index.cell_size(), 100.0);
273 }
274
275 #[test]
276 fn test_spatial_index_nan_infinity_handling() {
277 let index = SpatialIndex::new();
278
279 let nan_bounds = Rect::new(f64::NAN, 10.0, 50.0, 50.0);
281 let cells_nan = index.get_grid_cells_for_bounds(&nan_bounds);
282 assert_eq!(cells_nan.len(), 0);
283
284 let inf_bounds = Rect::new(f64::INFINITY, 10.0, 50.0, 50.0);
286 let cells_inf = index.get_grid_cells_for_bounds(&inf_bounds);
287 assert_eq!(cells_inf.len(), 0);
288
289 let neg_inf_bounds = Rect::new(f64::NEG_INFINITY, 10.0, 50.0, 50.0);
291 let cells_neg_inf = index.get_grid_cells_for_bounds(&neg_inf_bounds);
292 assert_eq!(cells_neg_inf.len(), 0);
293
294 let nan_pos = Position::new(f64::NAN, 10.0);
296 let nan_results = index.query_radius(nan_pos, 50.0);
297 assert_eq!(nan_results.len(), 0);
298 }
299
300 #[test]
301 fn test_spatial_index_viewport_query_edge_cases() {
302 let mut index = SpatialIndex::new();
303
304 let node = NodeBuilder::<()>::new("test-node")
305 .position(100.0, 100.0)
306 .size(50.0, 50.0)
307 .build();
308
309 index.insert(&node).unwrap();
310
311 let viewport1 = Viewport::with_size(0.0, 0.0, 200.0, 200.0);
313 let results1 = index.query_viewport(&viewport1);
314 assert_eq!(results1.len(), 1);
315
316 let viewport2 = Viewport::with_size(200.0, 200.0, 100.0, 100.0);
318 let results2 = index.query_viewport(&viewport2);
319 assert_eq!(results2.len(), 0);
320
321 let viewport3 = Viewport::with_size(125.0, 125.0, 1.0, 1.0);
323 let results3 = index.query_viewport(&viewport3);
324 assert_eq!(results3.len(), 1);
325 }
326
327 #[test]
328 fn test_spatial_query_builder_edge_cases() {
329 let mut index = SpatialIndex::new();
330
331 let nodes = vec![
332 NodeBuilder::<()>::new("node1")
333 .position(10.0, 10.0)
334 .size(20.0, 20.0)
335 .build(),
336 NodeBuilder::<()>::new("node2")
337 .position(50.0, 50.0)
338 .size(20.0, 20.0)
339 .build(),
340 NodeBuilder::<()>::new("node3")
341 .position(90.0, 90.0)
342 .size(20.0, 20.0)
343 .build(),
344 ];
345
346 index.bulk_load(&nodes).unwrap();
347
348 let results = SpatialQuery::new(&index).execute();
350 assert_eq!(results.len(), 3);
351
352 let results = SpatialQuery::new(&index).limit(10).execute();
354 assert_eq!(results.len(), 3);
355
356 let results = SpatialQuery::new(&index).limit(0).execute();
358 assert_eq!(results.len(), 0);
359
360 let results = SpatialQuery::new(&index)
362 .bounds(Rect::new(0.0, 0.0, 40.0, 40.0))
363 .radius(Position::new(60.0, 60.0), 100.0)
364 .execute();
365 assert_eq!(results.len(), 1); }
367
368 #[test]
369 fn test_spatial_index_zero_negative_size_bounds() {
370 let index = SpatialIndex::new();
371
372 let neg_width_bounds = Rect::new(50.0, 50.0, -20.0, 20.0);
374 let cells_neg_width = index.get_grid_cells_for_bounds(&neg_width_bounds);
375 assert_eq!(cells_neg_width.len(), 0);
376
377 let neg_height_bounds = Rect::new(50.0, 50.0, 20.0, -20.0);
379 let cells_neg_height = index.get_grid_cells_for_bounds(&neg_height_bounds);
380 assert_eq!(cells_neg_height.len(), 0);
381
382 let neg_both_bounds = Rect::new(50.0, 50.0, -20.0, -20.0);
384 let cells_neg_both = index.get_grid_cells_for_bounds(&neg_both_bounds);
385 assert_eq!(cells_neg_both.len(), 0);
386 }
387
388 #[test]
389 fn test_spatial_index_clear_functionality() {
390 let mut index = SpatialIndex::new();
391
392 let node1 = NodeBuilder::<()>::new("node1")
393 .position(10.0, 10.0)
394 .size(20.0, 20.0)
395 .build();
396
397 let node2 = NodeBuilder::<()>::new("node2")
398 .position(50.0, 50.0)
399 .size(20.0, 20.0)
400 .build();
401
402 index.insert(&node1).unwrap();
403 index.insert(&node2).unwrap();
404 assert_eq!(index.len(), 2);
405
406 index.clear();
408 assert_eq!(index.len(), 0);
409 assert!(index.is_empty());
410
411 let query_bounds = Rect::new(0.0, 0.0, 100.0, 100.0);
413 let results = index.query_rect(&query_bounds);
414 assert_eq!(results.len(), 0);
415
416 let bounds = index.bounds();
418 assert_eq!(bounds, None);
419 }
420}