1use crate::error::{SpatialError, SpatialResult};
2use scirs2_core::ndarray::{Array1, ArrayView1};
3use std::cmp::Ordering;
4#[derive(Clone, Debug)]
8pub struct Rectangle {
9 pub(crate) min: Array1<f64>,
11 pub(crate) max: Array1<f64>,
13}
14
15impl Rectangle {
16 pub fn new(min: Array1<f64>, max: Array1<f64>) -> SpatialResult<Self> {
27 if min.len() != max.len() {
28 return Err(SpatialError::DimensionError(format!(
29 "Min and max must have the same dimensions. Got {} and {}",
30 min.len(),
31 max.len()
32 )));
33 }
34
35 for i in 0..min.len() {
37 if min[i] > max[i] {
38 return Err(SpatialError::ValueError(
39 format!("Min must be less than or equal to max in all dimensions. Dimension {}: {} > {}",
40 i, min[i], max[i])
41 ));
42 }
43 }
44
45 Ok(Rectangle { min, max })
46 }
47
48 pub fn from_point(point: &ArrayView1<f64>) -> Self {
58 Rectangle {
59 min: point.to_owned(),
60 max: point.to_owned(),
61 }
62 }
63
64 pub fn ndim(&self) -> usize {
66 self.min.len()
67 }
68
69 pub fn area(&self) -> f64 {
71 let mut area = 1.0;
72 for i in 0..self.ndim() {
73 area *= self.max[i] - self.min[i];
74 }
75 area
76 }
77
78 pub fn margin(&self) -> f64 {
80 let mut margin = 0.0;
81 for i in 0..self.ndim() {
82 margin += self.max[i] - self.min[i];
83 }
84 2.0 * margin
85 }
86
87 pub fn contains_point(&self, point: &ArrayView1<f64>) -> SpatialResult<bool> {
97 if point.len() != self.ndim() {
98 return Err(SpatialError::DimensionError(format!(
99 "Point dimension {} does not match rectangle dimension {}",
100 point.len(),
101 self.ndim()
102 )));
103 }
104
105 for i in 0..self.ndim() {
106 if point[i] < self.min[i] || point[i] > self.max[i] {
107 return Ok(false);
108 }
109 }
110
111 Ok(true)
112 }
113
114 pub fn contains_rectangle(&self, other: &Rectangle) -> SpatialResult<bool> {
124 if other.ndim() != self.ndim() {
125 return Err(SpatialError::DimensionError(format!(
126 "Rectangle dimensions do not match: {} and {}",
127 self.ndim(),
128 other.ndim()
129 )));
130 }
131
132 for i in 0..self.ndim() {
133 if other.min[i] < self.min[i] || other.max[i] > self.max[i] {
134 return Ok(false);
135 }
136 }
137
138 Ok(true)
139 }
140
141 pub fn intersects(&self, other: &Rectangle) -> SpatialResult<bool> {
151 if other.ndim() != self.ndim() {
152 return Err(SpatialError::DimensionError(format!(
153 "Rectangle dimensions do not match: {} and {}",
154 self.ndim(),
155 other.ndim()
156 )));
157 }
158
159 for i in 0..self.ndim() {
160 if self.max[i] < other.min[i] || self.min[i] > other.max[i] {
161 return Ok(false);
162 }
163 }
164
165 Ok(true)
166 }
167
168 pub fn intersection(&self, other: &Rectangle) -> SpatialResult<Rectangle> {
179 if other.ndim() != self.ndim() {
180 return Err(SpatialError::DimensionError(format!(
181 "Rectangle dimensions do not match: {} and {}",
182 self.ndim(),
183 other.ndim()
184 )));
185 }
186
187 let intersects = self.intersects(other)?;
188 if !intersects {
189 return Err(SpatialError::ValueError(
190 "Rectangles do not intersect".into(),
191 ));
192 }
193
194 let mut min = Array1::zeros(self.ndim());
195 let mut max = Array1::zeros(self.ndim());
196
197 for i in 0..self.ndim() {
198 min[i] = f64::max(self.min[i], other.min[i]);
199 max[i] = f64::min(self.max[i], other.max[i]);
200 }
201
202 Rectangle::new(min, max)
203 }
204
205 pub fn enlarge(&self, other: &Rectangle) -> SpatialResult<Rectangle> {
216 if other.ndim() != self.ndim() {
217 return Err(SpatialError::DimensionError(format!(
218 "Rectangle dimensions do not match: {} and {}",
219 self.ndim(),
220 other.ndim()
221 )));
222 }
223
224 let mut min = Array1::zeros(self.ndim());
225 let mut max = Array1::zeros(self.ndim());
226
227 for i in 0..self.ndim() {
228 min[i] = f64::min(self.min[i], other.min[i]);
229 max[i] = f64::max(self.max[i], other.max[i]);
230 }
231
232 Rectangle::new(min, max)
233 }
234
235 pub fn enlargement_area(&self, other: &Rectangle) -> SpatialResult<f64> {
246 let enlarged = self.enlarge(other)?;
247 Ok(enlarged.area() - self.area())
248 }
249
250 pub fn min_distance_to_point(&self, point: &ArrayView1<f64>) -> SpatialResult<f64> {
261 if point.len() != self.ndim() {
262 return Err(SpatialError::DimensionError(format!(
263 "Point dimension {} does not match rectangle dimension {}",
264 point.len(),
265 self.ndim()
266 )));
267 }
268
269 let mut distance_sq = 0.0;
270
271 for i in 0..self.ndim() {
272 if point[i] < self.min[i] {
273 distance_sq += (point[i] - self.min[i]).powi(2);
274 } else if point[i] > self.max[i] {
275 distance_sq += (point[i] - self.max[i]).powi(2);
276 }
277 }
278
279 Ok(distance_sq.sqrt())
280 }
281
282 pub fn min_distance_to_rectangle(&self, other: &Rectangle) -> SpatialResult<f64> {
293 if other.ndim() != self.ndim() {
294 return Err(SpatialError::DimensionError(format!(
295 "Rectangle dimensions do not match: {} and {}",
296 self.ndim(),
297 other.ndim()
298 )));
299 }
300
301 if self.intersects(other)? {
303 return Ok(0.0);
304 }
305
306 let mut distance_sq = 0.0;
307
308 for i in 0..self.ndim() {
309 if self.max[i] < other.min[i] {
310 distance_sq += (other.min[i] - self.max[i]).powi(2);
311 } else if self.min[i] > other.max[i] {
312 distance_sq += (self.min[i] - other.max[i]).powi(2);
313 }
314 }
315
316 Ok(distance_sq.sqrt())
317 }
318}
319
320#[derive(Clone, Debug)]
322pub(crate) enum Entry<T>
323where
324 T: Clone,
325{
326 Leaf {
328 mbr: Rectangle,
330 data: T,
332 index: usize,
334 },
335
336 NonLeaf {
338 mbr: Rectangle,
340 child: Box<Node<T>>,
342 },
343}
344
345impl<T: Clone> Entry<T> {
346 pub(crate) fn mbr(&self) -> &Rectangle {
348 match self {
349 Entry::Leaf { mbr, .. } => mbr,
350 Entry::NonLeaf { mbr, .. } => mbr,
351 }
352 }
353}
354
355#[derive(Clone, Debug)]
357pub(crate) struct Node<T>
358where
359 T: Clone,
360{
361 pub entries: Vec<Entry<T>>,
363 pub _isleaf: bool,
365 pub level: usize,
367}
368
369impl<T: Clone> Default for Node<T> {
370 fn default() -> Self {
371 Self {
372 entries: Vec::new(),
373 _isleaf: true,
374 level: 0,
375 }
376 }
377}
378
379impl<T: Clone> Node<T> {
380 pub fn new(_isleaf: bool, level: usize) -> Self {
382 Node {
383 entries: Vec::new(),
384 _isleaf,
385 level,
386 }
387 }
388
389 pub fn size(&self) -> usize {
391 self.entries.len()
392 }
393
394 pub fn mbr(&self) -> SpatialResult<Option<Rectangle>> {
396 if self.entries.is_empty() {
397 return Ok(None);
398 }
399
400 let mut result = self.entries[0].mbr().clone();
401
402 for i in 1..self.size() {
403 result = result.enlarge(self.entries[i].mbr())?;
404 }
405
406 Ok(Some(result))
407 }
408}
409
410#[derive(Clone, Debug)]
412pub(crate) struct EntryWithDistance<T>
413where
414 T: Clone,
415{
416 pub entry: Entry<T>,
418 pub distance: f64,
420}
421
422impl<T: Clone> PartialEq for EntryWithDistance<T> {
423 fn eq(&self, other: &Self) -> bool {
424 self.distance == other.distance
425 }
426}
427
428impl<T: Clone> Eq for EntryWithDistance<T> {}
429
430impl<T: Clone> PartialOrd for EntryWithDistance<T> {
431 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
432 Some(self.cmp(other))
433 }
434}
435
436impl<T: Clone> Ord for EntryWithDistance<T> {
437 fn cmp(&self, other: &Self) -> Ordering {
438 other
440 .distance
441 .partial_cmp(&self.distance)
442 .unwrap_or(Ordering::Equal)
443 }
444}
445
446#[derive(Clone, Debug)]
488pub struct RTree<T>
489where
490 T: Clone,
491{
492 pub(crate) root: Node<T>,
494 ndim: usize,
496 pub(crate) min_entries: usize,
498 pub(crate) maxentries: usize,
500 size: usize,
502 height: usize,
504}
505
506impl<T: Clone> RTree<T> {
507 pub fn new(ndim: usize, min_entries: usize, maxentries: usize) -> SpatialResult<Self> {
519 if ndim == 0 {
520 return Err(SpatialError::ValueError(
521 "Number of dimensions must be positive".into(),
522 ));
523 }
524
525 if min_entries < 1 || min_entries > maxentries / 2 {
526 return Err(SpatialError::ValueError(format!(
527 "min_entries must be between 1 and maxentries/2, got: {min_entries}"
528 )));
529 }
530
531 if maxentries < 2 {
532 return Err(SpatialError::ValueError(format!(
533 "maxentries must be at least 2, got: {maxentries}"
534 )));
535 }
536
537 Ok(RTree {
538 root: Node::new(true, 0),
539 ndim,
540 min_entries,
541 maxentries,
542 size: 0,
543 height: 1,
544 })
545 }
546
547 pub fn ndim(&self) -> usize {
549 self.ndim
550 }
551
552 pub fn size(&self) -> usize {
554 self.size
555 }
556
557 pub fn height(&self) -> usize {
559 self.height
560 }
561
562 pub fn is_empty(&self) -> bool {
564 self.size == 0
565 }
566
567 pub fn clear(&mut self) {
569 self.root = Node::new(true, 0);
570 self.size = 0;
571 self.height = 1;
572 }
573
574 pub(crate) fn increment_size(&mut self) {
576 self.size += 1;
577 }
578
579 pub(crate) fn decrement_size(&mut self) {
581 if self.size > 0 {
582 self.size -= 1;
583 }
584 }
585
586 pub(crate) fn increment_height(&mut self) {
588 self.height += 1;
589 }
590
591 #[allow(dead_code)]
593 pub(crate) fn decrement_height(&mut self) {
594 if self.height > 0 {
595 self.height -= 1;
596 }
597 }
598}
599
600#[cfg(test)]
601mod tests {
602 use super::*;
603 use approx::assert_relative_eq;
604 use scirs2_core::ndarray::array;
605
606 #[test]
607 fn test_rectangle_creation() {
608 let min = array![0.0, 0.0, 0.0];
609 let max = array![1.0, 2.0, 3.0];
610
611 let rect = Rectangle::new(min.clone(), max.clone()).unwrap();
612
613 assert_eq!(rect.ndim(), 3);
614 assert_eq!(rect.min, min);
615 assert_eq!(rect.max, max);
616
617 let min_invalid = array![0.0, 3.0, 0.0];
619 let max_invalid = array![1.0, 2.0, 3.0];
620
621 let result = Rectangle::new(min_invalid, max_invalid);
622 assert!(result.is_err());
623
624 let min_dim_mismatch = array![0.0, 0.0];
626 let max_dim_mismatch = array![1.0, 2.0, 3.0];
627
628 let result = Rectangle::new(min_dim_mismatch, max_dim_mismatch);
629 assert!(result.is_err());
630 }
631
632 #[test]
633 fn test_rectangle_area_and_margin() {
634 let min = array![0.0, 0.0, 0.0];
635 let max = array![1.0, 2.0, 3.0];
636
637 let rect = Rectangle::new(min, max).unwrap();
638
639 assert_relative_eq!(rect.area(), 6.0);
640 assert_relative_eq!(rect.margin(), 12.0);
641 }
642
643 #[test]
644 fn test_rectangle_contains_point() {
645 let min = array![0.0, 0.0];
646 let max = array![1.0, 1.0];
647
648 let rect = Rectangle::new(min, max).unwrap();
649
650 let point_inside = array![0.5, 0.5];
652 assert!(rect.contains_point(&point_inside.view()).unwrap());
653
654 let point_boundary = array![0.0, 0.5];
656 assert!(rect.contains_point(&point_boundary.view()).unwrap());
657
658 let point_outside = array![2.0, 0.5];
660 assert!(!rect.contains_point(&point_outside.view()).unwrap());
661
662 let point_dim_mismatch = array![0.5, 0.5, 0.5];
664 assert!(rect.contains_point(&point_dim_mismatch.view()).is_err());
665 }
666
667 #[test]
668 fn test_rectangle_intersects() {
669 let rect1 = Rectangle::new(array![0.0, 0.0], array![2.0, 2.0]).unwrap();
670
671 let rect2 = Rectangle::new(array![1.0, 1.0], array![3.0, 3.0]).unwrap();
673 assert!(rect1.intersects(&rect2).unwrap());
674
675 let rect3 = Rectangle::new(array![2.0, 0.0], array![3.0, 2.0]).unwrap();
677 assert!(rect1.intersects(&rect3).unwrap());
678
679 let rect4 = Rectangle::new(array![3.0, 3.0], array![4.0, 4.0]).unwrap();
681 assert!(!rect1.intersects(&rect4).unwrap());
682
683 let rect5 = Rectangle::new(array![0.0, 0.0, 0.0], array![1.0, 1.0, 1.0]).unwrap();
685 assert!(rect1.intersects(&rect5).is_err());
686 }
687
688 #[test]
689 fn test_rectangle_enlarge() {
690 let rect1 = Rectangle::new(array![1.0, 1.0], array![2.0, 2.0]).unwrap();
691 let rect2 = Rectangle::new(array![0.0, 0.0], array![3.0, 1.5]).unwrap();
692
693 let enlarged = rect1.enlarge(&rect2).unwrap();
694
695 assert_eq!(enlarged.min, array![0.0, 0.0]);
696 assert_eq!(enlarged.max, array![3.0, 2.0]);
697
698 let enlargement = rect1.enlargement_area(&rect2).unwrap();
700 let original_area = rect1.area();
701 let enlarged_area = enlarged.area();
702
703 assert_relative_eq!(enlargement, enlarged_area - original_area);
704 }
705
706 #[test]
707 fn test_rectangle_distance() {
708 let rect = Rectangle::new(array![1.0, 1.0], array![3.0, 3.0]).unwrap();
709
710 let point_inside = array![2.0, 2.0];
712 assert_relative_eq!(
713 rect.min_distance_to_point(&point_inside.view()).unwrap(),
714 0.0
715 );
716
717 let point_outside = array![0.0, 0.0];
719 assert_relative_eq!(
720 rect.min_distance_to_point(&point_outside.view()).unwrap(),
721 2.0_f64.sqrt()
722 );
723
724 let rect2 = Rectangle::new(array![4.0, 4.0], array![5.0, 5.0]).unwrap();
726 assert_relative_eq!(
727 rect.min_distance_to_rectangle(&rect2).unwrap(),
728 2.0_f64.sqrt()
729 );
730
731 let rect3 = Rectangle::new(array![2.0, 2.0], array![4.0, 4.0]).unwrap();
733 assert_relative_eq!(rect.min_distance_to_rectangle(&rect3).unwrap(), 0.0);
734 }
735
736 #[test]
737 fn test_rtree_creation() {
738 let rtree: RTree<usize> = RTree::new(2, 2, 5).unwrap();
740 assert_eq!(rtree.ndim(), 2);
741 assert_eq!(rtree.size(), 0);
742 assert_eq!(rtree.height(), 1);
743 assert!(rtree.is_empty());
744
745 assert!(RTree::<usize>::new(0, 2, 5).is_err()); assert!(RTree::<usize>::new(2, 0, 5).is_err()); assert!(RTree::<usize>::new(2, 3, 5).is_err()); assert!(RTree::<usize>::new(2, 2, 1).is_err()); }
751}