ferroui_base/
pixel_vector.rs1use std::fmt;
4use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
5
6use crate::PixelPoint;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
10pub struct PixelVector {
11 pub x: i32,
13 pub y: i32,
15}
16
17impl PixelVector {
18 #[inline]
20 pub const fn new(x: i32, y: i32) -> Self {
21 Self { x, y }
22 }
23
24 #[inline]
27 pub fn length(&self) -> f64 {
28 (self
29 .x
30 .wrapping_mul(self.x)
31 .wrapping_add(self.y.wrapping_mul(self.y)) as f64)
32 .sqrt()
33 }
34
35 #[inline]
37 pub const fn equals(&self, other: PixelVector) -> bool {
38 self.x == other.x && self.y == other.y
39 }
40
41 pub fn nearly_equals(&self, other: PixelVector) -> bool {
48 const TOLERANCE: f32 = f32::from_bits(1);
50 const OVERFLOW: &str = "Negating the minimum value of a twos complement number is invalid.";
51
52 let dx = self.x.wrapping_sub(other.x).checked_abs().expect(OVERFLOW);
53 let dy = self.y.wrapping_sub(other.y).checked_abs().expect(OVERFLOW);
54 (dx as f32) < TOLERANCE && (dy as f32) < TOLERANCE
55 }
56
57 #[inline]
59 pub const fn with_x(&self, x: i32) -> PixelVector {
60 PixelVector::new(x, self.y)
61 }
62
63 #[inline]
65 pub const fn with_y(&self, y: i32) -> PixelVector {
66 PixelVector::new(self.x, y)
67 }
68}
69
70impl From<PixelVector> for PixelPoint {
73 #[inline]
74 fn from(a: PixelVector) -> PixelPoint {
75 PixelPoint::new(a.x, a.y)
76 }
77}
78
79impl Mul for PixelVector {
81 type Output = i32;
82 #[inline]
83 fn mul(self, b: PixelVector) -> i32 {
84 self.x
85 .wrapping_mul(b.x)
86 .wrapping_add(self.y.wrapping_mul(b.y))
87 }
88}
89
90impl Mul<i32> for PixelVector {
92 type Output = PixelVector;
93 #[inline]
94 fn mul(self, scale: i32) -> PixelVector {
95 PixelVector::new(self.x.wrapping_mul(scale), self.y.wrapping_mul(scale))
96 }
97}
98
99impl Div<i32> for PixelVector {
104 type Output = PixelVector;
105 #[inline]
106 fn div(self, scale: i32) -> PixelVector {
107 PixelVector::new(self.x / scale, self.y / scale)
108 }
109}
110
111impl Neg for PixelVector {
112 type Output = PixelVector;
113 #[inline]
114 fn neg(self) -> PixelVector {
115 PixelVector::new(self.x.wrapping_neg(), self.y.wrapping_neg())
116 }
117}
118
119impl Add for PixelVector {
120 type Output = PixelVector;
121 #[inline]
122 fn add(self, b: PixelVector) -> PixelVector {
123 PixelVector::new(self.x.wrapping_add(b.x), self.y.wrapping_add(b.y))
124 }
125}
126
127impl Sub for PixelVector {
128 type Output = PixelVector;
129 #[inline]
130 fn sub(self, b: PixelVector) -> PixelVector {
131 PixelVector::new(self.x.wrapping_sub(b.x), self.y.wrapping_sub(b.y))
132 }
133}
134
135impl AddAssign for PixelVector {
136 #[inline]
137 fn add_assign(&mut self, b: PixelVector) {
138 *self = *self + b;
139 }
140}
141
142impl SubAssign for PixelVector {
143 #[inline]
144 fn sub_assign(&mut self, b: PixelVector) {
145 *self = *self - b;
146 }
147}
148
149impl MulAssign<i32> for PixelVector {
150 #[inline]
151 fn mul_assign(&mut self, scale: i32) {
152 *self = *self * scale;
153 }
154}
155
156impl DivAssign<i32> for PixelVector {
157 #[inline]
158 fn div_assign(&mut self, scale: i32) {
159 *self = *self / scale;
160 }
161}
162
163impl fmt::Display for PixelVector {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 write!(f, "{}, {}", self.x, self.y)
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
173
174 #[test]
175 fn arithmetic_and_display() {
176 let a = PixelVector::new(3, 4);
177 let b = PixelVector::new(1, 2);
178 assert_eq!(a * b, 11);
179 assert_eq!(a * 2, PixelVector::new(6, 8));
180 assert_eq!(a / 2, PixelVector::new(1, 2));
181 assert_eq!(-a, PixelVector::new(-3, -4));
182 assert_eq!(a + b, PixelVector::new(4, 6));
183 assert_eq!(a - b, PixelVector::new(2, 2));
184 assert_eq!(a.length(), 5.0);
185 assert_eq!(a.to_string(), "3, 4");
186 assert_eq!(PixelPoint::from(a), PixelPoint::new(3, 4));
187 assert!(a.nearly_equals(a));
188 assert!(!a.nearly_equals(b));
189 assert_eq!(a.with_x(9).with_y(8), PixelVector::new(9, 8));
190 }
191
192 #[test]
193 #[should_panic]
194 fn division_by_zero_panics() {
195 let _ = PixelVector::new(1, 1) / 0;
196 }
197}