opencv_core/
rect.rs

1//! Rectangle structures for OpenCV
2
3use crate::{Point, Size};
4use std::fmt;
5use serde::{Serialize, Deserialize};
6
7/// Rectangle with integer coordinates
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10pub struct Rect {
11    pub x: i32,
12    pub y: i32,
13    pub width: i32,
14    pub height: i32,
15}
16
17impl Rect {
18    /// Create a new rectangle
19    pub fn new(x: i32, y: i32, width: i32, height: i32) -> Self {
20        Self { x, y, width, height }
21    }
22
23    /// Create rectangle from top-left and bottom-right points
24    pub fn from_points(tl: Point, br: Point) -> Self {
25        Self::new(
26            tl.x,
27            tl.y,
28            br.x - tl.x,
29            br.y - tl.y,
30        )
31    }
32
33    /// Create rectangle from center point and size
34    pub fn from_center_size(center: Point, size: Size) -> Self {
35        let half_width = size.width / 2;
36        let half_height = size.height / 2;
37        Self::new(
38            center.x - half_width,
39            center.y - half_height,
40            size.width,
41            size.height,
42        )
43    }
44
45    /// Get top-left corner
46    pub fn tl(&self) -> Point {
47        Point::new(self.x, self.y)
48    }
49
50    /// Get bottom-right corner
51    pub fn br(&self) -> Point {
52        Point::new(self.x + self.width, self.y + self.height)
53    }
54
55    /// Get center point
56    pub fn center(&self) -> Point {
57        Point::new(
58            self.x + self.width / 2,
59            self.y + self.height / 2,
60        )
61    }
62
63    /// Get size
64    pub fn size(&self) -> Size {
65        Size::new(self.width, self.height)
66    }
67
68    /// Calculate area
69    pub fn area(&self) -> i32 {
70        self.width * self.height
71    }
72
73    /// Check if rectangle is empty (width or height <= 0)
74    pub fn is_empty(&self) -> bool {
75        self.width <= 0 || self.height <= 0
76    }
77
78    /// Check if point is inside rectangle
79    pub fn contains(&self, point: Point) -> bool {
80        point.x >= self.x &&
81        point.y >= self.y &&
82        point.x < self.x + self.width &&
83        point.y < self.y + self.height
84    }
85
86    /// Check if another rectangle is completely inside this one
87    pub fn contains_rect(&self, other: &Rect) -> bool {
88        self.x <= other.x &&
89        self.y <= other.y &&
90        self.x + self.width >= other.x + other.width &&
91        self.y + self.height >= other.y + other.height
92    }
93
94    /// Get intersection with another rectangle
95    pub fn intersect(&self, other: &Rect) -> Option<Rect> {
96        let x1 = self.x.max(other.x);
97        let y1 = self.y.max(other.y);
98        let x2 = (self.x + self.width).min(other.x + other.width);
99        let y2 = (self.y + self.height).min(other.y + other.height);
100
101        if x1 < x2 && y1 < y2 {
102            Some(Rect::new(x1, y1, x2 - x1, y2 - y1))
103        } else {
104            None
105        }
106    }
107
108    /// Get union with another rectangle
109    pub fn union(&self, other: &Rect) -> Rect {
110        let x1 = self.x.min(other.x);
111        let y1 = self.y.min(other.y);
112        let x2 = (self.x + self.width).max(other.x + other.width);
113        let y2 = (self.y + self.height).max(other.y + other.height);
114
115        Rect::new(x1, y1, x2 - x1, y2 - y1)
116    }
117
118    /// Inflate rectangle by given amounts
119    pub fn inflate(&self, dx: i32, dy: i32) -> Rect {
120        Rect::new(
121            self.x - dx,
122            self.y - dy,
123            self.width + 2 * dx,
124            self.height + 2 * dy,
125        )
126    }
127
128    /// Translate rectangle by given offset
129    pub fn translate(&self, dx: i32, dy: i32) -> Rect {
130        Rect::new(
131            self.x + dx,
132            self.y + dy,
133            self.width,
134            self.height,
135        )
136    }
137}
138
139impl Default for Rect {
140    fn default() -> Self {
141        Self::new(0, 0, 0, 0)
142    }
143}
144
145impl fmt::Display for Rect {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        write!(f, "[{}, {}, {}x{}]", self.x, self.y, self.width, self.height)
148    }
149}
150
151impl From<(i32, i32, i32, i32)> for Rect {
152    fn from((x, y, width, height): (i32, i32, i32, i32)) -> Self {
153        Self::new(x, y, width, height)
154    }
155}
156
157impl From<Rect> for (i32, i32, i32, i32) {
158    fn from(rect: Rect) -> Self {
159        (rect.x, rect.y, rect.width, rect.height)
160    }
161}
162
163/// Rectangle with single-precision floating-point coordinates
164#[derive(Debug, Clone, Copy, PartialEq)]
165#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
166pub struct Rect2f {
167    pub x: f32,
168    pub y: f32,
169    pub width: f32,
170    pub height: f32,
171}
172
173impl Rect2f {
174    /// Create a new rectangle
175    pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
176        Self { x, y, width, height }
177    }
178
179    /// Calculate area
180    pub fn area(&self) -> f32 {
181        self.width * self.height
182    }
183
184    /// Check if rectangle is empty
185    pub fn is_empty(&self) -> bool {
186        self.width <= 0.0 || self.height <= 0.0
187    }
188
189    /// Check if point is inside rectangle
190    pub fn contains(&self, x: f32, y: f32) -> bool {
191        x >= self.x &&
192        y >= self.y &&
193        x < self.x + self.width &&
194        y < self.y + self.height
195    }
196
197    /// Get intersection with another rectangle
198    pub fn intersect(&self, other: &Rect2f) -> Option<Rect2f> {
199        let x1 = self.x.max(other.x);
200        let y1 = self.y.max(other.y);
201        let x2 = (self.x + self.width).min(other.x + other.width);
202        let y2 = (self.y + self.height).min(other.y + other.height);
203
204        if x1 < x2 && y1 < y2 {
205            Some(Rect2f::new(x1, y1, x2 - x1, y2 - y1))
206        } else {
207            None
208        }
209    }
210}
211
212impl Default for Rect2f {
213    fn default() -> Self {
214        Self::new(0.0, 0.0, 0.0, 0.0)
215    }
216}
217
218impl fmt::Display for Rect2f {
219    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220        write!(f, "[{:.2}, {:.2}, {:.2}x{:.2}]", self.x, self.y, self.width, self.height)
221    }
222}
223
224impl From<Rect> for Rect2f {
225    fn from(rect: Rect) -> Self {
226        Self::new(
227            rect.x as f32,
228            rect.y as f32,
229            rect.width as f32,
230            rect.height as f32,
231        )
232    }
233}
234
235/// Rectangle with double-precision floating-point coordinates
236#[derive(Debug, Clone, Copy, PartialEq)]
237#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
238pub struct Rect2d {
239    pub x: f64,
240    pub y: f64,
241    pub width: f64,
242    pub height: f64,
243}
244
245impl Rect2d {
246    /// Create a new rectangle
247    pub fn new(x: f64, y: f64, width: f64, height: f64) -> Self {
248        Self { x, y, width, height }
249    }
250
251    /// Calculate area
252    pub fn area(&self) -> f64 {
253        self.width * self.height
254    }
255
256    /// Check if rectangle is empty
257    pub fn is_empty(&self) -> bool {
258        self.width <= 0.0 || self.height <= 0.0
259    }
260
261    /// Check if point is inside rectangle
262    pub fn contains(&self, x: f64, y: f64) -> bool {
263        x >= self.x &&
264        y >= self.y &&
265        x < self.x + self.width &&
266        y < self.y + self.height
267    }
268}
269
270impl Default for Rect2d {
271    fn default() -> Self {
272        Self::new(0.0, 0.0, 0.0, 0.0)
273    }
274}
275
276impl From<Rect2f> for Rect2d {
277    fn from(rect: Rect2f) -> Self {
278        Self::new(
279            rect.x as f64,
280            rect.y as f64,
281            rect.width as f64,
282            rect.height as f64,
283        )
284    }
285}
286
287#[cfg(test)]
288mod tests {
289    use super::*;
290
291    #[test]
292    fn test_rect_creation() {
293        let rect = Rect::new(10, 20, 100, 200);
294        assert_eq!(rect.x, 10);
295        assert_eq!(rect.y, 20);
296        assert_eq!(rect.width, 100);
297        assert_eq!(rect.height, 200);
298        assert_eq!(rect.area(), 20000);
299    }
300
301    #[test]
302    fn test_rect_points() {
303        let rect = Rect::new(10, 20, 100, 200);
304        assert_eq!(rect.tl(), Point::new(10, 20));
305        assert_eq!(rect.br(), Point::new(110, 220));
306        assert_eq!(rect.center(), Point::new(60, 120));
307    }
308
309    #[test]
310    fn test_rect_contains() {
311        let rect = Rect::new(10, 20, 100, 200);
312        assert!(rect.contains(Point::new(50, 50)));
313        assert!(!rect.contains(Point::new(5, 5)));
314        assert!(!rect.contains(Point::new(150, 150)));
315    }
316
317    #[test]
318    fn test_rect_intersection() {
319        let rect1 = Rect::new(0, 0, 100, 100);
320        let rect2 = Rect::new(50, 50, 100, 100);
321        
322        let intersection = rect1.intersect(&rect2).unwrap();
323        assert_eq!(intersection, Rect::new(50, 50, 50, 50));
324    }
325
326    #[test]
327    fn test_rect_union() {
328        let rect1 = Rect::new(0, 0, 100, 100);
329        let rect2 = Rect::new(50, 50, 100, 100);
330        
331        let union = rect1.union(&rect2);
332        assert_eq!(union, Rect::new(0, 0, 150, 150));
333    }
334
335    #[test]
336    fn test_rect_inflate() {
337        let rect = Rect::new(10, 10, 100, 100);
338        let inflated = rect.inflate(5, 5);
339        assert_eq!(inflated, Rect::new(5, 5, 110, 110));
340    }
341
342    #[test]
343    fn test_rect_translate() {
344        let rect = Rect::new(10, 10, 100, 100);
345        let translated = rect.translate(5, 5);
346        assert_eq!(translated, Rect::new(15, 15, 100, 100));
347    }
348}