waterui_core/animation_system/
vector_arithmetic.rs1use core::ops::{Add, Mul, Sub};
25use num_traits::ToPrimitive;
26
27pub trait VectorArithmetic:
32 Clone + Default + Send + 'static + Add<Output = Self> + Sub<Output = Self> + Mul<f64, Output = Self>
33{
34 #[must_use]
36 fn lerp(&self, other: &Self, t: f64) -> Self {
37 self.clone() + (other.clone() - self.clone()) * t
38 }
39}
40
41impl<T> VectorArithmetic for T where
43 T: Clone + Default + Send + 'static + Add<Output = T> + Sub<Output = T> + Mul<f64, Output = T>
44{
45}
46
47#[derive(Clone, Default, Debug, PartialEq, Eq)]
51pub struct AnimatablePair<A, B>(pub A, pub B);
52
53impl<A: Add<Output = A>, B: Add<Output = B>> Add for AnimatablePair<A, B> {
54 type Output = Self;
55
56 fn add(self, other: Self) -> Self {
57 Self(self.0 + other.0, self.1 + other.1)
58 }
59}
60
61impl<A: Sub<Output = A>, B: Sub<Output = B>> Sub for AnimatablePair<A, B> {
62 type Output = Self;
63
64 fn sub(self, other: Self) -> Self {
65 Self(self.0 - other.0, self.1 - other.1)
66 }
67}
68
69impl<A: Mul<f64, Output = A>, B: Mul<f64, Output = B>> Mul<f64> for AnimatablePair<A, B> {
70 type Output = Self;
71
72 fn mul(self, scalar: f64) -> Self {
73 Self(self.0 * scalar, self.1 * scalar)
74 }
75}
76
77#[derive(Clone, Copy, Default, Debug, PartialEq)]
85pub struct Point2(pub [f32; 2]);
86
87impl Point2 {
88 #[must_use]
90 pub const fn new(x: f32, y: f32) -> Self {
91 Self([x, y])
92 }
93
94 #[must_use]
96 pub const fn x(&self) -> f32 {
97 self.0[0]
98 }
99
100 #[must_use]
102 pub const fn y(&self) -> f32 {
103 self.0[1]
104 }
105}
106
107impl From<[f32; 2]> for Point2 {
108 fn from(arr: [f32; 2]) -> Self {
109 Self(arr)
110 }
111}
112
113impl From<Point2> for [f32; 2] {
114 fn from(p: Point2) -> Self {
115 p.0
116 }
117}
118
119impl Add for Point2 {
120 type Output = Self;
121
122 fn add(self, other: Self) -> Self {
123 Self([self.0[0] + other.0[0], self.0[1] + other.0[1]])
124 }
125}
126
127impl Sub for Point2 {
128 type Output = Self;
129
130 fn sub(self, other: Self) -> Self {
131 Self([self.0[0] - other.0[0], self.0[1] - other.0[1]])
132 }
133}
134
135impl Mul<f64> for Point2 {
136 type Output = Self;
137
138 fn mul(self, scalar: f64) -> Self {
139 let s = scalar
140 .to_f32()
141 .expect("Point2 scaling requires an f64 representable as f32");
142 Self([self.0[0] * s, self.0[1] * s])
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_f64_lerp() {
152 let a = 0.0_f64;
153 let b = 1.0_f64;
154 assert!((a.lerp(&b, 0.0) - 0.0).abs() < 0.001);
155 assert!((a.lerp(&b, 0.5) - 0.5).abs() < 0.001);
156 assert!((a.lerp(&b, 1.0) - 1.0).abs() < 0.001);
157 }
158
159 #[test]
160 fn test_point2_lerp() {
161 let a = Point2::new(0.0, 0.0);
162 let b = Point2::new(1.0, 2.0);
163 let mid = a.lerp(&b, 0.5);
164 assert!((mid.x() - 0.5).abs() < 0.001);
165 assert!((mid.y() - 1.0).abs() < 0.001);
166 }
167
168 #[test]
169 fn test_animatable_pair() {
170 let a = AnimatablePair(0.0_f64, Point2::new(0.0, 0.0));
171 let b = AnimatablePair(1.0_f64, Point2::new(2.0, 4.0));
172 let mid = a.lerp(&b, 0.5);
173 assert!((mid.0 - 0.5).abs() < 0.001);
174 assert!((mid.1.x() - 1.0).abs() < 0.001);
175 assert!((mid.1.y() - 2.0).abs() < 0.001);
176 }
177}