irox_stats/
rects.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5use crate::points::{Double2D, Vec2D};
6
7#[allow(unused_imports)]
8use irox_tools::f64::FloatExt;
9
10#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd, Eq, Ord)]
11pub struct Rect2D {
12    pub origin: Double2D,
13    pub far_point: Double2D,
14}
15impl Rect2D {
16    pub const fn empty() -> Rect2D {
17        Self {
18            origin: Double2D::new(f64::INFINITY, f64::INFINITY),
19            far_point: Double2D::new(f64::NEG_INFINITY, f64::NEG_INFINITY),
20        }
21    }
22    #[must_use]
23    pub fn new(origin: Double2D, size: Vec2D) -> Self {
24        Self {
25            origin,
26            far_point: origin.translate(&size),
27        }
28    }
29    #[must_use]
30    pub fn origin(&self) -> Double2D {
31        self.origin
32    }
33    #[must_use]
34    pub fn size(&self) -> Vec2D {
35        self.far_point - self.origin
36    }
37
38    #[must_use]
39    pub fn far_point(&self) -> Double2D {
40        self.far_point
41    }
42
43    #[must_use]
44    pub fn far_horiz(&self) -> Double2D {
45        Double2D {
46            x: self.far_point.x,
47            y: self.origin.y,
48        }
49    }
50    #[must_use]
51    pub fn far_vert(&self) -> Double2D {
52        Double2D {
53            x: self.origin.x,
54            y: self.far_point.y,
55        }
56    }
57
58    #[must_use]
59    pub fn corners(&self) -> [Double2D; 4] {
60        [
61            self.origin,
62            self.far_horiz(),
63            self.far_point(),
64            self.far_vert(),
65        ]
66    }
67
68    #[must_use]
69    pub fn contains(&self, point: Double2D) -> bool {
70        let Double2D { x, y } = point;
71        let Double2D { x: fx, y: fy } = self.far_point();
72        x >= self.origin.x && x <= fx && y >= self.origin.y && y <= fy
73    }
74    #[must_use]
75    pub fn intersects(&self, other: &Rect2D) -> bool {
76        for mypt in self.corners() {
77            if other.contains(mypt) {
78                return true;
79            }
80        }
81        for pt in other.corners() {
82            if self.contains(pt) {
83                return true;
84            }
85        }
86        false
87    }
88
89    pub fn add_point(&mut self, x: f64, y: f64) {
90        self.origin.x = self.origin.x.min(x);
91        self.origin.y = self.origin.y.min(y);
92        self.far_point.x = self.far_point.x.max(x);
93        self.far_point.y = self.far_point.y.max(y);
94    }
95
96    #[must_use]
97    pub fn height(&self) -> f64 {
98        (self.far_point.y - self.origin.y).abs()
99    }
100    #[must_use]
101    pub fn width(&self) -> f64 {
102        (self.far_point.x - self.origin.x).abs()
103    }
104    #[must_use]
105    pub fn center(&self) -> Double2D {
106        let y = self.height() / 2. + self.origin.y;
107        let x = self.width() / 2. + self.origin.x;
108        Double2D::new(x, y)
109    }
110    #[must_use]
111    pub fn lower_left_quadrant(&self) -> Rect2D {
112        Rect2D {
113            origin: self.origin,
114            far_point: self.center(),
115        }
116    }
117    #[must_use]
118    pub fn upper_left_quadrant(&self) -> Rect2D {
119        let ctr = self.center();
120        Rect2D {
121            origin: Double2D::new(self.origin.x, ctr.y),
122            far_point: Double2D::new(ctr.x, self.far_point.y),
123        }
124    }
125    #[must_use]
126    pub fn upper_right_quadrant(&self) -> Rect2D {
127        Rect2D {
128            origin: self.center(),
129            far_point: self.far_point,
130        }
131    }
132    #[must_use]
133    pub fn lower_right_quadrant(&self) -> Rect2D {
134        let ctr = self.center();
135        Rect2D {
136            origin: Double2D::new(ctr.x, self.origin.y),
137            far_point: Double2D::new(self.far_point.x, ctr.y),
138        }
139    }
140}
141
142#[cfg(feature = "emath")]
143impl From<emath::Rect> for Rect2D {
144    fn from(rect: emath::Rect) -> Self {
145        Self {
146            origin: rect.min.into(),
147            far_point: rect.max.into(),
148        }
149    }
150}
151#[cfg(feature = "emath")]
152impl From<Rect2D> for emath::Rect {
153    fn from(rect: Rect2D) -> Self {
154        Self {
155            min: rect.origin.into(),
156            max: rect.far_point.into(),
157        }
158    }
159}