1use crate::Interpolatable;
2
3use super::{Generic, Point2, Space, Vector2};
4use std::{f64::consts::PI, marker::PhantomData, ops::Mul};
5
6impl<W: Space, T: Space> Interpolatable for Transform2<W, T> {
15 fn interpolate(&self, other: &Self, t: f64) -> Self {
16 let parts1: TransformParts = (*self).into();
17 let parts2: TransformParts = (*other).into();
18
19 let origin = parts1.origin + (parts2.origin - parts1.origin) * t;
21
22 let scale = parts1.scale + (parts2.scale - parts1.scale) * t;
24
25 let skew = parts1.skew + (parts2.skew - parts1.skew) * t;
27
28 let mut rotation = parts1.rotation + (parts2.rotation - parts1.rotation) * t;
30
31 if (parts2.rotation - parts1.rotation).abs() > PI {
33 if parts2.rotation > parts1.rotation {
34 rotation += 2.0 * PI * (1.0 - t);
35 } else {
36 rotation -= 2.0 * PI * (1.0 - t);
37 }
38 }
39
40 TransformParts {
42 origin,
43 scale,
44 skew,
45 rotation,
46 }
47 .into()
48 }
49}
50
51pub struct Transform2<WFrom = Generic, WTo = WFrom> {
52 pub m: [f64; 6],
53 _panthom_from: PhantomData<WFrom>,
54 _panthom_to: PhantomData<WTo>,
55}
56
57impl<F, T> Clone for Transform2<F, T> {
61 fn clone(&self) -> Self {
62 Self {
63 m: self.m,
64 _panthom_from: PhantomData,
65 _panthom_to: PhantomData,
66 }
67 }
68}
69
70impl<F, T> std::fmt::Debug for Transform2<F, T> {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 writeln!(f, "{} {} {}", self.m[0], self.m[2], self.m[4])?;
73 write!(f, "{} {} {}", self.m[1], self.m[3], self.m[5])
74 }
75}
76
77impl<F, T> PartialEq for Transform2<F, T> {
78 fn eq(&self, other: &Self) -> bool {
79 self.m == other.m
80 }
81}
82
83impl<F, T> Copy for Transform2<F, T> {}
84
85impl<F: Space, T: Space> Default for Transform2<F, T> {
86 fn default() -> Self {
87 Self::identity()
88 }
89}
90
91impl<WFrom: Space, WTo: Space> Transform2<WFrom, WTo> {
92 pub fn new(m: [f64; 6]) -> Self {
93 Self {
94 m,
95 _panthom_from: PhantomData,
96 _panthom_to: PhantomData,
97 }
98 }
99
100 pub fn identity() -> Self {
101 Self::new([1.0, 0.0, 0.0, 1.0, 0.0, 0.0])
102 }
103
104 pub fn scale(s: f64) -> Self {
105 Self::scale_sep(Vector2::new(s, s))
106 }
107
108 pub fn scale_sep(s: Vector2<WTo>) -> Self {
109 Self::new([s.x, 0.0, 0.0, s.y, 0.0, 0.0])
110 }
111
112 pub fn skew(k: Vector2<WTo>) -> Self {
113 Self::new([1.0, k.y, k.x, 1.0, 0.0, 0.0])
114 }
115
116 pub fn rotate(th: f64) -> Self {
117 let (s, c) = th.sin_cos();
118 Self::new([c, s, -s, c, 0.0, 0.0])
119 }
120
121 pub fn translate(p: Vector2<WTo>) -> Self {
122 Self::new([1.0, 0.0, 0.0, 1.0, p.x, p.y])
123 }
124
125 pub fn determinant(self) -> f64 {
126 self.m[0] * self.m[3] - self.m[1] * self.m[2]
127 }
128
129 pub fn coeffs(&self) -> [f64; 6] {
130 self.m
131 }
132
133 pub fn get_translation(self) -> Vector2<WFrom> {
134 (self * Point2::<WFrom>::default()).cast_space().to_vector()
135 }
136
137 pub fn get_scale(self) -> Vector2<WTo> {
138 self * Vector2::<WFrom>::new(1.0, 1.0)
139 }
140
141 pub fn cast_spaces<W: Space, T: Space>(self) -> Transform2<W, T> {
142 Transform2::new(self.m)
143 }
144
145 pub fn inverse(self) -> Transform2<WTo, WFrom> {
147 let inv_det = self.determinant().recip();
148 Transform2::<WTo, WFrom>::new([
149 inv_det * self.m[3],
150 -inv_det * self.m[1],
151 -inv_det * self.m[2],
152 inv_det * self.m[0],
153 inv_det * (self.m[2] * self.m[5] - self.m[3] * self.m[4]),
154 inv_det * (self.m[1] * self.m[4] - self.m[0] * self.m[5]),
155 ])
156 }
157
158 pub fn compose(p: Point2<WTo>, vx: Vector2<WTo>, vy: Vector2<WTo>) -> Self {
159 Self::new([vx.x, vx.y, vy.x, vy.y, p.x, p.y])
160 }
161
162 pub fn decompose(&self) -> (Point2<WTo>, Vector2<WTo>, Vector2<WTo>) {
165 let [v1x, v1y, v2x, v2y, px, py] = self.m;
166 (
167 Point2::new(px, py),
168 Vector2::new(v1x, v1y),
169 Vector2::new(v2x, v2y),
170 )
171 }
172
173 pub fn contains_point(&self, point: Point2<WTo>) -> bool {
174 let unit = self.inverse() * point;
175 unit.x > 0.0 && unit.y > 0.0 && unit.x < 1.0 && unit.y < 1.0
176 }
177}
178
179#[derive(PartialEq, Clone)]
180pub struct TransformParts {
181 pub origin: Vector2,
182 pub scale: Vector2,
183 pub skew: Vector2,
184 pub rotation: f64,
185}
186
187impl<F: Space, W: Space> Into<Transform2<F, W>> for TransformParts {
188 fn into(self) -> Transform2<F, W> {
189 (Transform2::<Generic>::translate(self.origin)
190 * Transform2::rotate(self.rotation)
191 * Transform2::<Generic>::scale_sep(self.scale)
192 * Transform2::<Generic>::skew(self.skew))
193 .cast_spaces()
194 }
195}
196
197impl<F: Space, T: Space> Into<TransformParts> for Transform2<F, T> {
199 fn into(self) -> TransformParts {
200 let [a, b, c, d, e, f] = self.m;
201 let angle = f64::atan2(b, a);
202 let denom = a.powi(2) + b.powi(2);
203 let scale_x = f64::sqrt(denom);
204 let scale_y = (a * d - c * b) / scale_x;
205 let skew_x = (a * c + b * d) / denom;
206
207 TransformParts {
208 origin: Vector2::new(e, f),
209 scale: Vector2::new(scale_x, scale_y),
210 skew: Vector2::new(skew_x, 0.0),
211 rotation: angle,
212 }
213 }
214}
215
216impl std::fmt::Debug for TransformParts {
217 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 f.debug_struct("Parts")
219 .field("origin", &self.origin)
220 .field("scale", &self.scale)
221 .field("skew", &self.skew)
222 .field("rotation", &self.rotation)
223 .finish()
224 }
225}
226
227impl<W1: Space, W2: Space, W3: Space> Mul<Transform2<W1, W2>> for Transform2<W2, W3> {
228 type Output = Transform2<W1, W3>;
229
230 fn mul(self, rhs: Transform2<W1, W2>) -> Self::Output {
231 Self::Output::new([
232 self.m[0] * rhs.m[0] + self.m[2] * rhs.m[1],
233 self.m[1] * rhs.m[0] + self.m[3] * rhs.m[1],
234 self.m[0] * rhs.m[2] + self.m[2] * rhs.m[3],
235 self.m[1] * rhs.m[2] + self.m[3] * rhs.m[3],
236 self.m[0] * rhs.m[4] + self.m[2] * rhs.m[5] + self.m[4],
237 self.m[1] * rhs.m[4] + self.m[3] * rhs.m[5] + self.m[5],
238 ])
239 }
240}
241
242impl<F: Space, T: Space> Mul<Point2<F>> for Transform2<F, T> {
243 type Output = Point2<T>;
244
245 fn mul(self, other: Point2<F>) -> Self::Output {
246 Self::Output::new(
247 self.m[0] * other.x + self.m[2] * other.y + self.m[4],
248 self.m[1] * other.x + self.m[3] * other.y + self.m[5],
249 )
250 }
251}
252
253impl<F: Space, T: Space> Mul<Vector2<F>> for Transform2<F, T> {
254 type Output = Vector2<T>;
255
256 fn mul(self, other: Vector2<F>) -> Self::Output {
257 Self::Output::new(
258 self.m[0] * other.x + self.m[2] * other.y,
259 self.m[1] * other.x + self.m[3] * other.y,
260 )
261 }
262}
263
264impl<T: Space, F: Space> From<Transform2<T, F>> for kurbo::Affine {
265 fn from(value: Transform2<T, F>) -> Self {
266 Self::new(value.m)
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use std::f64::consts::PI;
273
274 use crate::math::{Generic, Vector2};
275
276 use super::{Transform2, TransformParts};
277
278 #[test]
279 fn from_to_parts() {
280 for origin in [
282 Vector2::new(0.0, 0.0),
283 Vector2::new(11.2, 10.5),
284 Vector2::new(-9.2, 0.98),
285 Vector2::new(14.2, -3.2),
286 Vector2::new(-5.0, -5.1),
287 ] {
288 for scale in [
290 Vector2::new(1.0, 1.0),
291 Vector2::new(1.2, 1.5),
292 Vector2::new(0.2, 0.4395),
293 ] {
294 for rotation in [0.0, 1.0, 1.2940, -0.495, 5.0, -40.1] {
296 for skew in [
298 Vector2::new(0.0, 0.0),
299 Vector2::new(1.0, 0.0),
300 Vector2::new(0.13904, 0.0),
301 Vector2::new(-0.55, 0.0),
302 Vector2::new(100.0, 0.0),
303 ] {
304 let parts = TransformParts {
305 origin,
306 scale,
307 rotation,
308 skew,
309 };
310 let transform: Transform2 = parts.clone().into();
311 let new_parts: TransformParts = transform.into();
312 assert!((parts.skew - new_parts.skew).length() < 1e-3);
313 assert!((parts.origin - new_parts.origin).length() < 1e-3);
314 assert!((parts.scale - new_parts.scale).length() < 1e-3);
315 assert!(
317 ((parts.rotation - new_parts.rotation).rem_euclid(2.0 * PI)).abs()
318 < 1e-3
319 );
320 }
321 }
322 }
323 }
324 }
325
326 #[test]
327 fn test_scale_and_resize() {
328 let transform = Transform2::<Generic>::new([1.0, 0.4, 0.8, 0.2, 0.1, 0.5]);
329 let mut parts: TransformParts = transform.into();
330 parts.scale.x = 2.6 * parts.scale.x;
331 parts.scale.y = 1.8 * parts.scale.y;
332 let res_transform: Transform2<Generic> = parts.into();
333 let res2_transform = transform * Transform2::<Generic>::scale_sep(Vector2::new(2.6, 1.8));
334 assert!(
335 !(res_transform
336 .coeffs()
337 .into_iter()
338 .zip(res2_transform.coeffs())
339 .map(|(a, b)| (a - b).abs())
340 .sum::<f64>()
341 < 1e-3)
342 );
343 }
344}