1use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
2
3use super::{Offset, Rect};
4
5impl Neg for Offset {
6 type Output = Self;
7
8 fn neg(self) -> Self {
14 Self {
15 x: self.x.neg(),
16 y: self.y.neg(),
17 }
18 }
19}
20
21impl Add<Offset> for Rect {
22 type Output = Self;
23
24 fn add(self, offset: Offset) -> Self {
29 let max_x = i32::from(u16::MAX - self.width);
30 let max_y = i32::from(u16::MAX - self.height);
31 let x = i32::from(self.x).saturating_add(offset.x).clamp(0, max_x) as u16;
32 let y = i32::from(self.y).saturating_add(offset.y).clamp(0, max_y) as u16;
33 Self { x, y, ..self }
34 }
35}
36
37impl Add<Rect> for Offset {
38 type Output = Rect;
39
40 fn add(self, rect: Rect) -> Rect {
45 rect + self
46 }
47}
48
49impl Sub<Offset> for Rect {
50 type Output = Self;
51
52 fn sub(self, offset: Offset) -> Self {
57 let max_x = i32::from(u16::MAX - self.width);
59 let max_y = i32::from(u16::MAX - self.height);
60 let x = i32::from(self.x).saturating_sub(offset.x).clamp(0, max_x) as u16;
61 let y = i32::from(self.y).saturating_sub(offset.y).clamp(0, max_y) as u16;
62 Self { x, y, ..self }
63 }
64}
65
66impl AddAssign<Offset> for Rect {
67 fn add_assign(&mut self, offset: Offset) {
72 *self = *self + offset;
73 }
74}
75
76impl SubAssign<Offset> for Rect {
77 fn sub_assign(&mut self, offset: Offset) {
82 *self = *self - offset;
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use rstest::rstest;
89
90 use super::*;
91
92 #[rstest]
93 #[case::zero(Rect::new(3, 4, 5, 6), Offset::ZERO, Rect::new(3, 4, 5, 6))]
94 #[case::positive(Rect::new(3, 4, 5, 6), Offset::new(1, 2), Rect::new(4, 6, 5, 6))]
95 #[case::negative(Rect::new(3, 4, 5, 6), Offset::new(-1, -2), Rect::new(2, 2, 5, 6))]
96 #[case::saturate_negative(Rect::new(3, 4, 5, 6), Offset::MIN, Rect::new(0, 0, 5, 6))]
97 #[case::saturate_positive(Rect::new(3, 4, 5, 6), Offset::MAX, Rect::new(u16::MAX- 5, u16::MAX - 6, 5, 6))]
98 fn add_offset(#[case] rect: Rect, #[case] offset: Offset, #[case] expected: Rect) {
99 assert_eq!(rect + offset, expected);
100 assert_eq!(offset + rect, expected);
101 }
102
103 #[rstest]
104 #[case::zero(Rect::new(3, 4, 5, 6), Offset::ZERO, Rect::new(3, 4, 5, 6))]
105 #[case::positive(Rect::new(3, 4, 5, 6), Offset::new(1, 2), Rect::new(2, 2, 5, 6))]
106 #[case::negative(Rect::new(3, 4, 5, 6), Offset::new(-1, -2), Rect::new(4, 6, 5, 6))]
107 #[case::saturate_negative(Rect::new(3, 4, 5, 6), Offset::MAX, Rect::new(0, 0, 5, 6))]
108 #[case::saturate_positive(Rect::new(3, 4, 5, 6), -Offset::MAX, Rect::new(u16::MAX - 5, u16::MAX - 6, 5, 6))]
109 fn sub_offset(#[case] rect: Rect, #[case] offset: Offset, #[case] expected: Rect) {
110 assert_eq!(rect - offset, expected);
111 }
112
113 #[rstest]
114 #[case::zero(Rect::new(3, 4, 5, 6), Offset::ZERO, Rect::new(3, 4, 5, 6))]
115 #[case::positive(Rect::new(3, 4, 5, 6), Offset::new(1, 2), Rect::new(4, 6, 5, 6))]
116 #[case::negative(Rect::new(3, 4, 5, 6), Offset::new(-1, -2), Rect::new(2, 2, 5, 6))]
117 #[case::saturate_negative(Rect::new(3, 4, 5, 6), Offset::MIN, Rect::new(0, 0, 5, 6))]
118 #[case::saturate_positive(Rect::new(3, 4, 5, 6), Offset::MAX, Rect::new(u16::MAX - 5, u16::MAX - 6, 5, 6))]
119 fn add_assign_offset(#[case] rect: Rect, #[case] offset: Offset, #[case] expected: Rect) {
120 let mut rect = rect;
121 rect += offset;
122 assert_eq!(rect, expected);
123 }
124
125 #[rstest]
126 #[case::zero(Rect::new(3, 4, 5, 6), Offset::ZERO, Rect::new(3, 4, 5, 6))]
127 #[case::positive(Rect::new(3, 4, 5, 6), Offset::new(1, 2), Rect::new(2, 2, 5, 6))]
128 #[case::negative(Rect::new(3, 4, 5, 6), Offset::new(-1, -2), Rect::new(4, 6, 5, 6))]
129 #[case::saturate_negative(Rect::new(3, 4, 5, 6), Offset::MAX, Rect::new(0, 0, 5, 6))]
130 #[case::saturate_positive(Rect::new(3, 4, 5, 6), -Offset::MAX, Rect::new(u16::MAX - 5, u16::MAX - 6, 5, 6))]
131 fn sub_assign_offset(#[case] rect: Rect, #[case] offset: Offset, #[case] expected: Rect) {
132 let mut rect = rect;
133 rect -= offset;
134 assert_eq!(rect, expected);
135 }
136}