1use crate::raise;
2use crate::{PebblesError, PebblesErrorDetails};
3use rand_distr::{Triangular, Distribution};
4
5#[derive(PartialEq)]
11#[derive(Debug)]
12pub struct Rect {
13 pub x:i64,
14 pub y:i64,
15 pub width:i64,
16 pub height:i64,
17}
18
19impl Rect {
20 pub fn local_point(&self) -> Result<(i64, i64), PebblesError> {
29 let mut rng = rand::thread_rng();
30 let triangular_x = Triangular::new(0.0, self.width as f64, self.width as f64 / 2.0)
31 .or_else(|error| raise!(PebblesError::MathError, format!(
32 "Error while generating Triangular range: /
33 {}", error)))?;
34 let triangular_y = Triangular::new(0.0, self.height as f64, self.height as f64 / 2.0)
35 .or_else(|error| raise!(PebblesError::MathError, format!(
36 "Error while generating Triangular range: /
37 {}", error)))?;
38 Ok((triangular_x.sample(&mut rng) as i64, triangular_y.sample(&mut rng) as i64))
39 }
40
41 pub fn world_point(&self) -> Result<(i64, i64), PebblesError> {
50 let (x, y) = self.local_point()?;
51 Ok((self.x + x, self.y + y))
52 }
53
54 pub fn overlaps(&self, other: &Rect) -> bool {
55 let self_right = self.x + self.width;
56 let self_bottom = self.y + self.height;
57 let other_right = other.x + other.width;
58 let other_bottom = other.y + other.height;
59
60 if self.x < other_right && self_right > other.x && self.y < other_bottom && self_bottom > other.y {
61 return true;
62 }
63
64 false
65 }
66
67 pub fn overlapping_rect(&self, other: &Rect) -> Option<Rect> {
71 let x = std::cmp::max(self.x, other.x);
73 let y = std::cmp::max(self.y, other.y);
74 let right = std::cmp::min(self.x + self.width, other.x + other.width);
75 let bottom = std::cmp::min(self.y + self.height, other.y + other.height);
76
77 if right > x && bottom > y {
79 Some(Rect {
81 x,
82 y,
83 width: right - x,
84 height: bottom - y,
85 })
86 } else {
87 None
89 }
90 }
91}
92
93impl std::fmt::Display for Rect {
94 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
95 write!(f, "{{x:{}, y:{}, width:{}, height:{}}}", self.x, self.y, self.width, self.height)
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_local_point() {
105 let rect = Rect {
106 x: 0,
107 y: 0,
108 width: 10,
109 height: 10,
110 };
111
112 let result = rect.local_point();
113 assert!(result.is_ok());
114
115 let point = result.unwrap();
116 assert!(point.0 >= 0 && point.0 <= 10);
117 assert!(point.1 >= 0 && point.1 <= 10);
118 }
119
120 #[test]
121 fn test_world_point() {
122 let rect = Rect {
123 x: 5,
124 y: 5,
125 width: 10,
126 height: 10,
127 };
128
129 let result = rect.world_point();
130 assert!(result.is_ok());
131
132 let point = result.unwrap();
133 assert!(point.0 >= 5 && point.0 <= 15);
134 assert!(point.1 >= 5 && point.1 <= 15);
135 }
136 #[test]
137 fn test_overlaps() {
138 let rect1 = Rect { x: 0, y: 0, width: 10, height: 10 };
139 let rect2 = Rect { x: 5, y: 5, width: 10, height: 10 };
140 let rect3 = Rect { x: 20, y: 20, width: 10, height: 10 };
141
142 assert!(rect1.overlaps(&rect2), "Rect1 should overlap with Rect2");
143 assert!(!rect1.overlaps(&rect3), "Rect1 should not overlap with Rect3");
144 }
145
146 #[test]
147 fn test_overlapping_rect() {
148 let rect1 = Rect { x: 0, y: 0, width: 10, height: 10 };
149 let rect2 = Rect { x: 5, y: 5, width: 10, height: 10 };
150 let rect3 = Rect { x: 20, y: 20, width: 10, height: 10 };
151
152 if let Some(overlap) = rect1.overlapping_rect(&rect2) {
154 assert_eq!(overlap, Rect { x: 5, y: 5, width: 5, height: 5 }, "The overlapping area of Rect1 and Rect2 is incorrect");
155 } else {
156 panic!("Rect1 should overlap with Rect2");
157 }
158
159 assert!(rect1.overlapping_rect(&rect3).is_none(), "Rect1 should not overlap with Rect3");
161 }
162}