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