rust_rectangle_dividing/
rectangle.rs

1use crate::area::Area;
2use crate::aspect_ratio::AspectRatio;
3use crate::axis::{Axis, SizeForAxis};
4use crate::dividing::VerticalDividingHelper;
5use crate::rotate::QuarterRotation;
6use num_traits::{Float, Num, NumAssignOps, NumOps};
7/// rectangle in 2D space with a width and height
8
9#[derive(Debug, PartialEq, Clone, Copy)]
10pub struct Rectangle<T>
11where
12    T: Copy + Num + NumAssignOps + NumOps,
13{
14    width: T,
15    height: T,
16}
17
18impl<T> Rectangle<T>
19where
20    T: Copy + Num + NumAssignOps + NumOps + Float,
21{
22    pub fn round(&self) -> Self {
23        Self {
24            width: self.width.round(),
25            height: self.height.round(),
26        }
27    }
28}
29
30impl<T> SizeForAxis<T> for Rectangle<T>
31where
32    T: Copy + Num + NumAssignOps + NumOps,
33{
34    fn size_for_axis(&self, axis: Axis) -> T {
35        match axis {
36            Axis::Vertical => self.width,
37            Axis::Horizontal => self.height,
38        }
39    }
40}
41
42pub trait RectangleSize<T>
43where
44    T: Copy + Num + NumAssignOps + NumOps,
45{
46    fn width(&self) -> T;
47    fn height(&self) -> T;
48}
49
50/// A rectangle in 2D space with a width and height
51impl<T> RectangleSize<T> for Rectangle<T>
52where
53    T: Copy + Num + NumAssignOps + NumOps,
54{
55    fn width(&self) -> T {
56        self.width
57    }
58
59    fn height(&self) -> T {
60        self.height
61    }
62}
63
64impl<T> Area<T> for Rectangle<T>
65where
66    T: Copy + Num + NumAssignOps + NumOps,
67{
68    fn area(&self) -> T {
69        self.width * self.height
70    }
71}
72
73/// A rectangle in 2D space constructor
74impl<T> Rectangle<T>
75where
76    T: Copy + Num + NumAssignOps + NumOps,
77{
78    pub fn new(width: T, height: T) -> Self {
79        Self { width, height }
80    }
81}
82
83/// Rotate a rectangle by 90 degrees
84impl<T> QuarterRotation for Rectangle<T>
85where
86    T: Copy + Num + NumAssignOps + NumOps,
87{
88    fn rotate_clockwise(&self) -> Self {
89        Self {
90            width: self.height,
91            height: self.width,
92        }
93    }
94}
95
96impl<T> VerticalDividingHelper<T> for Rectangle<T>
97where
98    T: Copy + Num + NumAssignOps + NumOps,
99{
100    /// dividing a rectangle into two rectangles (vertical)
101    fn divide_vertical_helper(&self, x: T) -> (Rectangle<T>, Rectangle<T>) {
102        (
103            Self::new(x, self.height),
104            Self::new(self.width - x, self.height),
105        )
106    }
107}
108
109impl<T> AspectRatio<T> for Rectangle<T>
110where
111    T: Copy + Num + NumAssignOps + NumOps,
112{
113    fn aspect_ratio(&self) -> T {
114        self.width / self.height
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use crate::axis::Axis;
121    use crate::dividing::Dividing;
122
123    use super::*;
124
125    #[test]
126    fn test_new() {
127        let result = Rectangle::new(2, 3);
128        assert_eq!(result.width, 2);
129        assert_eq!(result.height, 3);
130    }
131
132    #[test]
133    fn test_identity() {
134        // identity: a rectangle is equal to itself
135        let rect = Rectangle::new(2, 3);
136        assert_rect_eq(&rect, &rect);
137    }
138
139    #[test]
140    fn test_rotate() {
141        assert_rotate_twice_is_same_as_original(&Rectangle::new(2, 3));
142    }
143
144    #[test]
145    fn test_area() {
146        let result = Rectangle::new(2, 3).area();
147        assert_eq!(result, 6);
148    }
149
150    #[test]
151    fn test_aspect_ratio() {
152        let result = Rectangle::new(16.0, 9.0).aspect_ratio();
153        assert_eq!(result, 1.7777777777777777);
154        let result = Rectangle::new(1920.0, 1080.0).aspect_ratio();
155        assert_eq!(result, 1.7777777777777777);
156    }
157
158    #[test]
159    fn test_divide_vertical() {
160        let (rect_a, rect_b) = Rectangle::new(4, 2).divide_vertical(1);
161        assert_rect_eq(&rect_a, &Rectangle::new(1, 2));
162        assert_rect_eq(&rect_b, &Rectangle::new(3, 2));
163    }
164
165    #[test]
166    fn test_divide_horizontal() {
167        let (rect_a, rect_b) = Rectangle::new(2, 4).divide_horizontal(1);
168        assert_rect_eq(&rect_a, &Rectangle::new(2, 1));
169        assert_rect_eq(&rect_b, &Rectangle::new(2, 3));
170    }
171
172    #[test]
173    fn test_divide() {
174        let (rect_a, rect_b) = Rectangle::new(4, 2).divide(1, Axis::Vertical);
175        assert_rect_eq(&rect_a, &Rectangle::new(1, 2));
176        assert_rect_eq(&rect_b, &Rectangle::new(3, 2));
177
178        let (rect_a, rect_b) = Rectangle::new(2, 4).divide(1, Axis::Horizontal);
179        assert_rect_eq(&rect_a, &Rectangle::new(2, 1));
180        assert_rect_eq(&rect_b, &Rectangle::new(2, 3));
181    }
182
183    #[test]
184    fn test_divide_nth() {
185        let rect = Rectangle::new(6, 2);
186        let divided = rect.divide_by_values_and_axis(&vec![1, 2], Axis::Vertical);
187        assert_rect_eq(&divided[0], &Rectangle::new(1, 2));
188        assert_rect_eq(&divided[1], &Rectangle::new(2, 2));
189        assert_rect_eq(&divided[2], &Rectangle::new(3, 2));
190
191        let rect = Rectangle::new(2, 6);
192        let divided = rect.divide_by_values_and_axis(&vec![3, 2], Axis::Horizontal);
193        assert_rect_eq(&divided[0], &Rectangle::new(2, 3));
194        assert_rect_eq(&divided[1], &Rectangle::new(2, 2));
195        assert_rect_eq(&divided[2], &Rectangle::new(2, 1));
196    }
197
198    #[test]
199    fn test_divide_by_weights() {
200        let rect = Rectangle::new(6.0, 2.0);
201        // values
202        let divided1 = rect.divide_by_values_and_axis(&vec![1.0, 2.0], Axis::Vertical);
203
204        let rect = Rectangle::new(6.0, 2.0);
205        let divided2 = rect.divide_by_weights_and_axis(&vec![2.0, 4.0, 6.0], Axis::Vertical);
206        assert_eq!(divided1, divided2);
207    }
208
209    /// Helper function to assert that two rectangles are equal
210    fn assert_rect_eq(rect1: &Rectangle<i32>, rect2: &Rectangle<i32>) {
211        assert_rect_has_same_component_is_equal(rect1, rect2);
212    }
213
214    /// Assert that two rectangles have the same width and height are equal
215    fn assert_rect_has_same_component_is_equal<T>(rect1: &Rectangle<T>, rect2: &Rectangle<T>)
216    where
217        T: Copy + PartialEq + std::fmt::Debug + Num + NumAssignOps,
218    {
219        assert_eq!(rect1.width, rect2.width);
220        assert_eq!(rect1.height, rect2.height);
221        assert_eq!(rect1, rect2);
222    }
223
224    /// Rotate a rectangle twice is the same as the original
225    fn assert_rotate_twice_is_same_as_original<T>(rect: &Rectangle<T>)
226    where
227        T: Copy + PartialEq + std::fmt::Debug + Num + NumAssignOps,
228    {
229        let rotated_twice = rect.rotate_clockwise().rotate_clockwise();
230        assert_rect_has_same_component_is_equal(rect, &rotated_twice);
231        assert_eq!(rotated_twice, *rect);
232    }
233}