1use num_traits::{Float, Num, NumAssignOps, NumOps};
2
3use crate::area::Area;
4use crate::aspect_ratio::AspectRatio;
5use crate::axis::{Axis, SizeForAxis};
6use crate::component::Component;
7use crate::dividing::VerticalDividingHelper;
8use crate::point::{Edge, Point};
9use crate::rectangle::{Rectangle, RectangleSize};
10use crate::rotate::QuarterRotation;
11
12#[derive(Debug, PartialEq, Clone)]
14pub struct AxisAlignedRectangle<T>
15where
16 T: Copy + Num + NumAssignOps + NumOps,
17{
18 pub point: Point<T>,
19 pub rectangle: Rectangle<T>,
20}
21
22impl<T> AxisAlignedRectangle<T>
23where
24 T: Copy + Num + NumAssignOps + NumOps + PartialOrd + Float,
25{
26 pub fn round(&self) -> Self {
27 let p1 = self.edge_left_top().round(Edge::RightBottom);
28 let p2 = self.edge_right_bottom().round(Edge::LeftTop);
29 let width = p2.x() - p1.x();
30 let height = p2.y() - p1.y();
31 let rect = Rectangle::new(width, height);
32 Self::new(&p1, &rect)
33 }
34}
35
36impl<T> SizeForAxis<T> for AxisAlignedRectangle<T>
37where
38 T: Copy + Num + NumAssignOps,
39{
40 fn size_for_axis(&self, axis: Axis) -> T {
41 self.rectangle.size_for_axis(axis)
42 }
43}
44
45impl<T> RectangleSize<T> for AxisAlignedRectangle<T>
47where
48 T: Copy + Num + NumAssignOps + NumOps,
49{
50 fn width(&self) -> T {
51 self.rectangle.width()
52 }
53 fn height(&self) -> T {
54 self.rectangle.height()
55 }
56}
57
58impl<T> Component<T> for AxisAlignedRectangle<T>
59where
60 T: Copy + Num + NumAssignOps + NumOps,
61{
62 fn x(&self) -> T {
63 self.point.x()
64 }
65
66 fn y(&self) -> T {
67 self.point.y()
68 }
69}
70
71impl<T> AxisAlignedRectangle<T>
72where
73 T: Copy + Num + NumAssignOps + NumOps,
74{
75 pub fn new(point: &Point<T>, rectangle: &Rectangle<T>) -> Self {
77 Self {
78 point: *point,
79 rectangle: *rectangle,
80 }
81 }
82
83 pub fn rect(&self) -> Rectangle<T> {
84 self.rectangle
85 }
86
87 pub fn origin(&self) -> Point<T> {
88 self.point
89 }
90}
91
92impl<T> AxisAlignedRectangle<T>
93where
94 T: Copy + Num + NumAssignOps + NumOps + Float,
95{
96 pub fn from_two_point(p1: &Point<T>, p2: &Point<T>) -> Self {
97 let vec = *p1 - *p2;
98 let width = vec.x().abs();
99 let height = vec.y().abs();
100 let rect = Rectangle::new(width, height);
101
102 Self::new(p1, &rect)
103 }
104}
105
106impl<T> AspectRatio<T> for AxisAlignedRectangle<T>
107where
108 T: Copy + Num + NumAssignOps + NumOps,
109{
110 fn aspect_ratio(&self) -> T {
111 self.rectangle.aspect_ratio()
112 }
113}
114
115impl<T> AxisAlignedRectangle<T>
116where
117 T: Copy + Num + NumAssignOps + NumOps + PartialOrd,
118{
119 pub(crate) fn edge_left_top(&self) -> Point<T> {
120 self.point
121 }
122 pub(crate) fn edge_right_top(&self) -> Point<T> {
123 Point::new(self.point.x() + self.rectangle.width(), self.point.y())
124 }
125 pub(crate) fn edge_left_bottom(&self) -> Point<T> {
126 Point::new(self.point.x(), self.point.y() + self.rectangle.height())
127 }
128 pub(crate) fn edge_right_bottom(&self) -> Point<T> {
129 Point::new(
130 self.point.x() + self.rectangle.width(),
131 self.point.y() + self.rectangle.height(),
132 )
133 }
134
135 #[allow(dead_code)]
136 pub(crate) fn edges(&self) -> Vec<Point<T>> {
137 vec![
138 self.edge_left_top(),
139 self.edge_right_top(),
140 self.edge_right_bottom(),
141 self.edge_left_bottom(),
142 ]
143 }
144
145 #[allow(dead_code)]
146 pub(crate) fn includes(&self, p: &Point<T>) -> bool {
147 p.x() > self.point.x()
148 && p.x() < self.point.x() + self.rectangle.width()
149 && p.y() > self.point.y()
150 && p.y() < self.point.y() + self.rectangle.height()
151 }
152
153 #[allow(dead_code)]
154 pub(crate) fn includes_or_on_the_boundary(&self, p: &Point<T>) -> bool {
155 p.x() >= self.point.x()
156 && p.x() <= self.point.x() + self.rectangle.width()
157 && p.y() >= self.point.y()
158 && p.y() <= self.point.y() + self.rectangle.height()
159 }
160
161 #[allow(dead_code)]
162 pub(crate) fn overlaps(&self, other: &Self) -> bool {
163 other.edges().iter().any(|p| self.includes(p))
165 }
166
167 #[allow(dead_code)]
168 pub(crate) fn enclodes(&self, other: &Self) -> bool {
169 other
171 .edges()
172 .iter()
173 .all(|p| self.includes_or_on_the_boundary(p))
174 }
175}
176
177impl<T> Area<T> for AxisAlignedRectangle<T>
179where
180 T: Copy + Num + NumAssignOps,
181{
182 fn area(&self) -> T {
183 self.rectangle.area()
184 }
185}
186
187impl<T> QuarterRotation for AxisAlignedRectangle<T>
189where
190 T: Copy + Num + NumAssignOps,
191{
192 fn rotate_clockwise(&self) -> Self {
193 Self::new(
194 &Point::new(self.y(), self.x()),
195 &Rectangle::new(self.height(), self.width()),
196 )
197 }
198}
199
200impl<T> VerticalDividingHelper<T> for AxisAlignedRectangle<T>
201where
202 T: Copy + Num + NumAssignOps + NumOps,
203{
204 fn divide_vertical_helper(&self, x: T) -> (AxisAlignedRectangle<T>, AxisAlignedRectangle<T>) {
206 (
207 Self::new(
208 &Point::new(self.x(), self.y()),
209 &Rectangle::new(x, self.height()),
210 ),
211 Self::new(
212 &Point::new(self.x() + x, self.y()),
213 &Rectangle::new(self.width() - x, self.height()),
214 ),
215 )
216 }
217}
218
219#[cfg(test)]
220mod tests {
221
222 use super::*;
223
224 #[test]
225 fn test_new() {
226 let point = Point::new(2, 3);
227 let rect = Rectangle::new(4, 5);
228 let result = AxisAlignedRectangle::new(&point, &rect);
229 assert_eq!(result.origin(), point);
230 assert_eq!(result.rect(), rect);
231 assert_eq!(result.x(), 2);
232 assert_eq!(result.y(), 3);
233 assert_eq!(result.width(), 4);
234 assert_eq!(result.height(), 5);
235 }
236
237 #[test]
238 fn test_rotate() {
239 let point = Point::new(2, 3);
240 let rect = Rectangle::new(4, 5);
241 let result = AxisAlignedRectangle::new(&point, &rect).rotate_clockwise();
242 assert_eq!(result.origin(), Point::new(3, 2));
243 assert_eq!(result.rect(), Rectangle::new(5, 4));
244 }
245
246 #[test]
247 fn test_area() {
248 let point = Point::new(2, 3);
249 let rect = Rectangle::new(4, 5);
250 let result = AxisAlignedRectangle::new(&point, &rect).area();
251 assert_eq!(result, 20);
252 }
253
254 #[test]
255 fn test_edges() {
256 let point = Point::new(2, 3);
257 let rect = Rectangle::new(4, 5);
258 let result = AxisAlignedRectangle::new(&point, &rect).edges();
259 assert_eq!(result.len(), 4);
260 assert_eq!(result[0], point);
261 assert_eq!(result[1], Point::new(6, 3));
262 assert_eq!(result[2], Point::new(6, 8));
263 assert_eq!(result[3], Point::new(2, 8));
264 }
265
266 #[test]
267 fn test_include() {
268 let point = Point::new(2, 3);
269 let rect = Rectangle::new(4, 5);
270 let a_rect = AxisAlignedRectangle::new(&point, &rect);
271 assert!(!a_rect.includes(&Point::new(1, 3)));
272 assert!(!a_rect.includes(&Point::new(7, 3)));
273 assert!(!a_rect.includes(&Point::new(6, 2)));
274 assert!(!a_rect.includes(&Point::new(6, 9)));
275 }
276
277 #[test]
278 fn test_overlaps() {
279 let point = Point::new(2, 3);
280 let rect = Rectangle::new(4, 5);
281 let a_rect = AxisAlignedRectangle::new(&point, &rect);
282 assert!(a_rect.overlaps(&AxisAlignedRectangle::new(
283 &Point::new(1, 2),
284 &Rectangle::new(4, 5)
285 )));
286 assert!(a_rect.overlaps(&AxisAlignedRectangle::new(
287 &Point::new(1, 4),
288 &Rectangle::new(4, 5)
289 )));
290 assert!(a_rect.overlaps(&AxisAlignedRectangle::new(
291 &Point::new(1, 5),
292 &Rectangle::new(4, 5)
293 )));
294 assert!(a_rect.overlaps(&AxisAlignedRectangle::new(
295 &Point::new(1, 6),
296 &Rectangle::new(4, 5)
297 )));
298 }
299}