1use std::ops::{Add, Div, Mul, Neg, Sub};
4
5pub mod bounds;
6pub mod flatten;
7mod transform;
8
9pub use transform::{Transform, TransformComponents};
10
11#[derive(Clone, Copy, Debug, Default, PartialEq)]
13pub struct Point {
14 pub x: f64,
16 pub y: f64,
18}
19
20impl Point {
21 pub const ORIGIN: Self = Self::new(0.0, 0.0);
23
24 pub const fn new(horizontal: f64, vertical: f64) -> Self {
26 Self {
27 x: horizontal,
28 y: vertical,
29 }
30 }
31
32 pub fn distance(self, other: Self) -> f64 {
34 (self - other).length()
35 }
36
37 pub fn interpolate(&self, target: &Self, progress: f64) -> Self {
39 Self::new(
40 self.x * (1.0 - progress) + target.x * progress,
41 self.y * (1.0 - progress) + target.y * progress,
42 )
43 }
44}
45
46impl Add<Vector2> for Point {
47 type Output = Self;
48
49 fn add(self, displacement: Vector2) -> Self {
50 Self::new(self.x + displacement.x, self.y + displacement.y)
51 }
52}
53
54impl Sub<Vector2> for Point {
55 type Output = Self;
56
57 fn sub(self, displacement: Vector2) -> Self {
58 Self::new(self.x - displacement.x, self.y - displacement.y)
59 }
60}
61
62impl Sub for Point {
63 type Output = Vector2;
64
65 fn sub(self, origin: Self) -> Vector2 {
66 Vector2::new(self.x - origin.x, self.y - origin.y)
67 }
68}
69
70#[derive(Clone, Copy, Debug, Default, PartialEq)]
72pub struct Vector2 {
73 pub x: f64,
75 pub y: f64,
77}
78
79impl Vector2 {
80 pub const ZERO: Self = Self::new(0.0, 0.0);
82
83 pub const fn new(horizontal: f64, vertical: f64) -> Self {
85 Self {
86 x: horizontal,
87 y: vertical,
88 }
89 }
90
91 pub fn from_angle(radians: f64) -> Self {
94 if radians == 0.0 {
95 Self::new(1.0, 0.0)
96 } else if radians.abs() == std::f64::consts::PI {
97 Self::new(-1.0, 0.0)
98 } else if radians == std::f64::consts::FRAC_PI_2 {
99 Self::new(0.0, 1.0)
100 } else if radians == -std::f64::consts::FRAC_PI_2 {
101 Self::new(0.0, -1.0)
102 } else {
103 Self::new(libm::cos(radians), libm::sin(radians))
104 }
105 }
106
107 pub fn length(self) -> f64 {
109 libm::hypot(self.x, self.y)
110 }
111
112 pub fn normalize(self) -> Self {
116 if self.x == 0.0 && self.y == 0.0 {
117 return Self::ZERO;
118 }
119 let magnitude = self.x.abs().max(self.y.abs());
120 let scaled = self / magnitude;
121 scaled / scaled.length()
122 }
123
124 pub fn dot(self, other: Self) -> f64 {
126 self.x * other.x + self.y * other.y
127 }
128
129 pub fn cross(self, other: Self) -> f64 {
131 self.x * other.y - self.y * other.x
132 }
133
134 pub fn rotate(self, radians: f64) -> Self {
136 let direction = Self::from_angle(radians);
137 Self::new(
138 direction.x * self.x - direction.y * self.y,
139 direction.y * self.x + direction.x * self.y,
140 )
141 }
142
143 pub fn interpolate(&self, target: &Self, progress: f64) -> Self {
145 Self::new(
146 self.x * (1.0 - progress) + target.x * progress,
147 self.y * (1.0 - progress) + target.y * progress,
148 )
149 }
150}
151
152impl Add for Vector2 {
153 type Output = Self;
154
155 fn add(self, other: Self) -> Self {
156 Self::new(self.x + other.x, self.y + other.y)
157 }
158}
159
160impl Sub for Vector2 {
161 type Output = Self;
162
163 fn sub(self, other: Self) -> Self {
164 Self::new(self.x - other.x, self.y - other.y)
165 }
166}
167
168impl Neg for Vector2 {
169 type Output = Self;
170
171 fn neg(self) -> Self {
172 Self::new(-self.x, -self.y)
173 }
174}
175
176impl Mul<f64> for Vector2 {
177 type Output = Self;
178
179 fn mul(self, factor: f64) -> Self {
180 Self::new(self.x * factor, self.y * factor)
181 }
182}
183
184impl Mul<Vector2> for f64 {
185 type Output = Vector2;
186
187 fn mul(self, vector: Vector2) -> Vector2 {
188 vector * self
189 }
190}
191
192impl Div<f64> for Vector2 {
193 type Output = Self;
194
195 fn div(self, divisor: f64) -> Self {
196 Self::new(self.x / divisor, self.y / divisor)
197 }
198}
199
200pub trait AngleLiteral {
203 fn degrees(self) -> f64;
205
206 fn turns(self) -> f64;
208}
209
210impl AngleLiteral for f64 {
211 fn degrees(self) -> f64 {
212 self.to_radians()
213 }
214
215 fn turns(self) -> f64 {
216 self * std::f64::consts::TAU
217 }
218}
219
220impl AngleLiteral for i32 {
221 fn degrees(self) -> f64 {
222 f64::from(self).degrees()
223 }
224
225 fn turns(self) -> f64 {
226 f64::from(self).turns()
227 }
228}
229
230impl AngleLiteral for i64 {
231 fn degrees(self) -> f64 {
232 (self as f64).degrees()
233 }
234
235 fn turns(self) -> f64 {
236 (self as f64).turns()
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 #[test]
243 fn design_points_preserve_precision_and_distinguish_displacements() {
244 use super::{Point, Vector2};
245
246 let origin = Point::new(16_777_217.125, -14.5);
247 let displacement = Vector2::new(3.0, 4.0);
248 let target = origin + displacement;
249 assert_eq!(target - origin, displacement);
250 assert_eq!(target - displacement, origin);
251 assert_eq!(origin.distance(target), 5.0);
252 assert_ne!(origin.x, f64::from(origin.x as f32));
253 let progress = 1.0 / 3.0;
254 assert_eq!(
255 Point::ORIGIN.interpolate(&Point::new(1.0, 1.0), progress).x,
256 progress
257 );
258 assert_eq!(
259 Point::ORIGIN.interpolate(&Point::new(2.0, 4.0), 1.5),
260 Point::new(3.0, 6.0)
261 );
262 }
263
264 #[test]
265 fn design_vectors_normalize_extreme_lengths_and_rotate_clockwise() {
266 use super::{AngleLiteral, Vector2};
267
268 assert_eq!(Vector2::ZERO.normalize(), Vector2::ZERO);
269 for component in [f64::MAX, 1e-300, f64::from_bits(1)] {
270 let unit = Vector2::new(component, component).normalize();
271 assert!((unit.length() - 1.0).abs() < 1e-15);
272 assert!((unit.x - std::f64::consts::FRAC_1_SQRT_2).abs() < 1e-15);
273 }
274 for component in [f64::NAN, f64::INFINITY] {
275 let invalid = Vector2::new(component, 0.0).normalize();
276 assert!(!invalid.x.is_finite() || !invalid.y.is_finite());
277 }
278 let rotated = Vector2::new(2.0, 0.0).rotate(90.degrees());
279 assert!(rotated.x.abs() < 1e-14);
280 assert!((rotated.y - 2.0).abs() < 1e-14);
281 assert_eq!(Vector2::from_angle(180.degrees()), Vector2::new(-1.0, 0.0));
282 assert_eq!(Vector2::from_angle(-90.degrees()), Vector2::new(0.0, -1.0));
283 assert_eq!(Vector2::new(1.0, 0.0).cross(Vector2::new(0.0, 1.0)), 1.0);
284 assert_eq!(Vector2::new(3.0, 4.0).dot(Vector2::new(-4.0, 3.0)), 0.0);
285 assert_eq!(0.5.turns(), 180.degrees());
286 assert_eq!(2_i64.turns(), 720.degrees());
287 assert_eq!(2.0 * Vector2::new(3.0, 4.0) / 2.0, Vector2::new(3.0, 4.0));
288 }
289}