1use super::animate_multiplicative_factor;
10use super::{Animate, Procedure, ToAnimatedZero};
11use crate::derives::*;
12use crate::values::computed::transform::Rotate as ComputedRotate;
13use crate::values::computed::transform::Scale as ComputedScale;
14use crate::values::computed::transform::Transform as ComputedTransform;
15use crate::values::computed::transform::TransformOperation as ComputedTransformOperation;
16use crate::values::computed::transform::Translate as ComputedTranslate;
17use crate::values::computed::transform::{DirectionVector, Matrix, Matrix3D};
18use crate::values::computed::Angle;
19use crate::values::computed::{Length, LengthPercentage};
20use crate::values::computed::{Number, Percentage};
21use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
22use crate::values::generics::transform::{self, Transform, TransformOperation};
23use crate::values::generics::transform::{Rotate, Scale, Translate};
24use crate::values::CSSFloat;
25use crate::Zero;
26use std::cmp;
27use std::ops::Add;
28
29#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
34#[allow(missing_docs)]
35pub struct InnerMatrix2D {
40 pub m11: CSSFloat,
41 pub m12: CSSFloat,
42 pub m21: CSSFloat,
43 pub m22: CSSFloat,
44}
45
46impl Animate for InnerMatrix2D {
47 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
48 Ok(InnerMatrix2D {
49 m11: animate_multiplicative_factor(self.m11, other.m11, procedure)?,
50 m12: self.m12.animate(&other.m12, procedure)?,
51 m21: self.m21.animate(&other.m21, procedure)?,
52 m22: animate_multiplicative_factor(self.m22, other.m22, procedure)?,
53 })
54 }
55}
56
57#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
59pub struct Translate2D(f32, f32);
60
61#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
63pub struct Scale2D(f32, f32);
64
65impl Animate for Scale2D {
66 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
67 Ok(Scale2D(
68 animate_multiplicative_factor(self.0, other.0, procedure)?,
69 animate_multiplicative_factor(self.1, other.1, procedure)?,
70 ))
71 }
72}
73
74#[derive(Clone, Copy, Debug, MallocSizeOf)]
76pub struct MatrixDecomposed2D {
77 pub translate: Translate2D,
79 pub scale: Scale2D,
81 pub angle: f32,
83 pub matrix: InnerMatrix2D,
85}
86
87impl Animate for MatrixDecomposed2D {
88 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
90 let mut scale = self.scale;
93 let mut angle = self.angle;
94 let mut other_angle = other.angle;
95 if (scale.0 < 0.0 && other.scale.1 < 0.0) || (scale.1 < 0.0 && other.scale.0 < 0.0) {
96 scale.0 = -scale.0;
97 scale.1 = -scale.1;
98 angle += if angle < 0.0 { 180. } else { -180. };
99 }
100
101 if angle == 0.0 {
103 angle = 360.
104 }
105 if other_angle == 0.0 {
106 other_angle = 360.
107 }
108
109 if (angle - other_angle).abs() > 180. {
110 if angle > other_angle {
111 angle -= 360.
112 } else {
113 other_angle -= 360.
114 }
115 }
116
117 let translate = self.translate.animate(&other.translate, procedure)?;
119 let scale = scale.animate(&other.scale, procedure)?;
120 let angle = angle.animate(&other_angle, procedure)?;
121 let matrix = self.matrix.animate(&other.matrix, procedure)?;
122
123 Ok(MatrixDecomposed2D {
124 translate,
125 scale,
126 angle,
127 matrix,
128 })
129 }
130}
131
132impl ComputeSquaredDistance for MatrixDecomposed2D {
133 #[inline]
134 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
135 const RAD_PER_DEG: f64 = std::f64::consts::PI / 180.0;
137 let angle1 = self.angle as f64 * RAD_PER_DEG;
138 let angle2 = other.angle as f64 * RAD_PER_DEG;
139 Ok(self.translate.compute_squared_distance(&other.translate)?
140 + self.scale.compute_squared_distance(&other.scale)?
141 + angle1.compute_squared_distance(&angle2)?
142 + self.matrix.compute_squared_distance(&other.matrix)?)
143 }
144}
145
146impl From<Matrix3D> for MatrixDecomposed2D {
147 fn from(matrix: Matrix3D) -> MatrixDecomposed2D {
150 let mut row0x = matrix.m11;
151 let mut row0y = matrix.m12;
152 let mut row1x = matrix.m21;
153 let mut row1y = matrix.m22;
154
155 let translate = Translate2D(matrix.m41, matrix.m42);
156 let mut scale = Scale2D(
157 (row0x * row0x + row0y * row0y).sqrt(),
158 (row1x * row1x + row1y * row1y).sqrt(),
159 );
160
161 let determinant = row0x * row1y - row0y * row1x;
163 if determinant < 0. {
164 if row0x < row1y {
165 scale.0 = -scale.0;
166 } else {
167 scale.1 = -scale.1;
168 }
169 }
170
171 if scale.0 != 0.0 {
173 row0x *= 1. / scale.0;
174 row0y *= 1. / scale.0;
175 }
176 if scale.1 != 0.0 {
177 row1x *= 1. / scale.1;
178 row1y *= 1. / scale.1;
179 }
180
181 let mut angle = row0y.atan2(row0x);
183 if angle != 0.0 {
184 let sn = -row0y;
185 let cs = row0x;
186 let m11 = row0x;
187 let m12 = row0y;
188 let m21 = row1x;
189 let m22 = row1y;
190 row0x = cs * m11 + sn * m21;
191 row0y = cs * m12 + sn * m22;
192 row1x = -sn * m11 + cs * m21;
193 row1y = -sn * m12 + cs * m22;
194 }
195
196 let m = InnerMatrix2D {
197 m11: row0x,
198 m12: row0y,
199 m21: row1x,
200 m22: row1y,
201 };
202
203 angle = angle.to_degrees();
205 MatrixDecomposed2D {
206 translate,
207 scale,
208 angle,
209 matrix: m,
210 }
211 }
212}
213
214impl From<MatrixDecomposed2D> for Matrix3D {
215 fn from(decomposed: MatrixDecomposed2D) -> Matrix3D {
218 let mut computed_matrix = Matrix3D::identity();
219 computed_matrix.m11 = decomposed.matrix.m11;
220 computed_matrix.m12 = decomposed.matrix.m12;
221 computed_matrix.m21 = decomposed.matrix.m21;
222 computed_matrix.m22 = decomposed.matrix.m22;
223
224 computed_matrix.m41 = decomposed.translate.0;
226 computed_matrix.m42 = decomposed.translate.1;
227
228 let angle = decomposed.angle.to_radians();
230 let cos_angle = angle.cos();
231 let sin_angle = angle.sin();
232
233 let mut rotate_matrix = Matrix3D::identity();
234 rotate_matrix.m11 = cos_angle;
235 rotate_matrix.m12 = sin_angle;
236 rotate_matrix.m21 = -sin_angle;
237 rotate_matrix.m22 = cos_angle;
238
239 computed_matrix = rotate_matrix.multiply(&computed_matrix);
241
242 computed_matrix.m11 *= decomposed.scale.0;
244 computed_matrix.m12 *= decomposed.scale.0;
245 computed_matrix.m21 *= decomposed.scale.1;
246 computed_matrix.m22 *= decomposed.scale.1;
247 computed_matrix
248 }
249}
250
251impl Animate for Matrix {
252 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
253 let this = Matrix3D::from(*self);
254 let other = Matrix3D::from(*other);
255 let from = decompose_2d_matrix(&this)?;
256 let to = decompose_2d_matrix(&other)?;
257 Matrix3D::from(from.animate(&to, procedure)?).into_2d()
258 }
259}
260
261#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
263pub struct Translate3D(pub f32, pub f32, pub f32);
264
265#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
267pub struct Scale3D(pub f32, pub f32, pub f32);
268
269impl Scale3D {
270 fn negate(&mut self) {
272 self.0 *= -1.0;
273 self.1 *= -1.0;
274 self.2 *= -1.0;
275 }
276}
277
278impl Animate for Scale3D {
279 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
280 Ok(Scale3D(
281 animate_multiplicative_factor(self.0, other.0, procedure)?,
282 animate_multiplicative_factor(self.1, other.1, procedure)?,
283 animate_multiplicative_factor(self.2, other.2, procedure)?,
284 ))
285 }
286}
287
288#[derive(Animate, Clone, Copy, Debug, MallocSizeOf)]
290pub struct Skew(f32, f32, f32);
291
292impl ComputeSquaredDistance for Skew {
293 #[inline]
296 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
297 Ok(self.0.atan().compute_squared_distance(&other.0.atan())?
298 + self.1.atan().compute_squared_distance(&other.1.atan())?
299 + self.2.atan().compute_squared_distance(&other.2.atan())?)
300 }
301}
302
303#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
305pub struct Perspective(pub f32, pub f32, pub f32, pub f32);
306
307impl Animate for Perspective {
308 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
309 Ok(Perspective(
310 self.0.animate(&other.0, procedure)?,
311 self.1.animate(&other.1, procedure)?,
312 self.2.animate(&other.2, procedure)?,
313 animate_multiplicative_factor(self.3, other.3, procedure)?,
314 ))
315 }
316}
317
318#[derive(Clone, Copy, Debug, MallocSizeOf)]
320pub struct Quaternion(f64, f64, f64, f64);
321
322impl Quaternion {
323 #[inline]
325 fn from_direction_and_angle(vector: &DirectionVector, angle: f64) -> Self {
326 debug_assert!(
327 (vector.length() - 1.).abs() < 0.0001,
328 "Only accept an unit direction vector to create a quaternion"
329 );
330
331 let half_angle = angle
336 .abs()
337 .rem_euclid(std::f64::consts::TAU)
338 .copysign(angle)
339 / 2.;
340
341 Quaternion(
351 vector.x as f64 * half_angle.sin(),
352 vector.y as f64 * half_angle.sin(),
353 vector.z as f64 * half_angle.sin(),
354 half_angle.cos(),
355 )
356 }
357
358 #[inline]
360 fn dot(&self, other: &Self) -> f64 {
361 self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3
362 }
363
364 #[inline]
366 fn scale(&self, factor: f64) -> Self {
367 Quaternion(
368 self.0 * factor,
369 self.1 * factor,
370 self.2 * factor,
371 self.3 * factor,
372 )
373 }
374}
375
376impl Add for Quaternion {
377 type Output = Self;
378
379 fn add(self, other: Self) -> Self {
380 Self(
381 self.0 + other.0,
382 self.1 + other.1,
383 self.2 + other.2,
384 self.3 + other.3,
385 )
386 }
387}
388
389impl Animate for Quaternion {
390 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
391 let (this_weight, other_weight) = procedure.weights();
392 debug_assert!(
393 (this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON * 2.0
396 || other_weight == 1.0f64
397 || other_weight == 0.0f64,
398 "animate should only be used for interpolating or accumulating transforms"
399 );
400
401 if let Procedure::Accumulate { .. } = procedure {
404 debug_assert_eq!(other_weight, 1.0);
405 if this_weight == 0.0 {
406 return Ok(*other);
407 }
408
409 let clamped_w = self.3.min(1.0).max(-1.0);
410
411 let mut theta = clamped_w.acos();
413 let mut scale = if theta == 0.0 { 0.0 } else { 1.0 / theta.sin() };
414 theta *= this_weight;
415 scale *= theta.sin();
416
417 let mut scaled_self = *self;
419 scaled_self.0 *= scale;
420 scaled_self.1 *= scale;
421 scaled_self.2 *= scale;
422 scaled_self.3 = theta.cos();
423
424 let a = &scaled_self;
426 let b = other;
427 return Ok(Quaternion(
428 a.3 * b.0 + a.0 * b.3 + a.1 * b.2 - a.2 * b.1,
429 a.3 * b.1 - a.0 * b.2 + a.1 * b.3 + a.2 * b.0,
430 a.3 * b.2 + a.0 * b.1 - a.1 * b.0 + a.2 * b.3,
431 a.3 * b.3 - a.0 * b.0 - a.1 * b.1 - a.2 * b.2,
432 ));
433 }
434
435 let cos_half_theta =
439 (self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3)
440 .min(1.0)
441 .max(-1.0);
442
443 if cos_half_theta.abs() == 1.0 {
444 return Ok(*self);
445 }
446
447 let half_theta = cos_half_theta.acos();
448 let sin_half_theta = (1.0 - cos_half_theta * cos_half_theta).sqrt();
449
450 let right_weight = (other_weight * half_theta).sin() / sin_half_theta;
451 let left_weight = (this_weight * half_theta).sin() / sin_half_theta;
464
465 Ok(self.scale(left_weight) + other.scale(right_weight))
466 }
467}
468
469impl ComputeSquaredDistance for Quaternion {
470 #[inline]
471 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
472 let distance = self.dot(other).max(-1.0).min(1.0).acos() * 2.0;
476 Ok(SquaredDistance::from_sqrt(distance))
477 }
478}
479
480#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
482pub struct MatrixDecomposed3D {
483 pub translate: Translate3D,
485 pub scale: Scale3D,
487 pub skew: Skew,
489 pub perspective: Perspective,
491 pub quaternion: Quaternion,
493}
494
495impl From<MatrixDecomposed3D> for Matrix3D {
496 fn from(decomposed: MatrixDecomposed3D) -> Matrix3D {
499 let mut matrix = Matrix3D::identity();
500
501 matrix.set_perspective(&decomposed.perspective);
503
504 matrix.apply_translate(&decomposed.translate);
506
507 {
509 let x = decomposed.quaternion.0;
510 let y = decomposed.quaternion.1;
511 let z = decomposed.quaternion.2;
512 let w = decomposed.quaternion.3;
513
514 let mut rotation_matrix = Matrix3D::identity();
517 rotation_matrix.m11 = 1.0 - 2.0 * (y * y + z * z) as f32;
518 rotation_matrix.m12 = 2.0 * (x * y + z * w) as f32;
519 rotation_matrix.m13 = 2.0 * (x * z - y * w) as f32;
520 rotation_matrix.m21 = 2.0 * (x * y - z * w) as f32;
521 rotation_matrix.m22 = 1.0 - 2.0 * (x * x + z * z) as f32;
522 rotation_matrix.m23 = 2.0 * (y * z + x * w) as f32;
523 rotation_matrix.m31 = 2.0 * (x * z + y * w) as f32;
524 rotation_matrix.m32 = 2.0 * (y * z - x * w) as f32;
525 rotation_matrix.m33 = 1.0 - 2.0 * (x * x + y * y) as f32;
526
527 matrix = rotation_matrix.multiply(&matrix);
528 }
529
530 {
532 let mut temp = Matrix3D::identity();
533 if decomposed.skew.2 != 0.0 {
534 temp.m32 = decomposed.skew.2;
535 matrix = temp.multiply(&matrix);
536 temp.m32 = 0.0;
537 }
538
539 if decomposed.skew.1 != 0.0 {
540 temp.m31 = decomposed.skew.1;
541 matrix = temp.multiply(&matrix);
542 temp.m31 = 0.0;
543 }
544
545 if decomposed.skew.0 != 0.0 {
546 temp.m21 = decomposed.skew.0;
547 matrix = temp.multiply(&matrix);
548 }
549 }
550
551 matrix.apply_scale(&decomposed.scale);
553
554 matrix
555 }
556}
557
558fn decompose_3d_matrix(mut matrix: Matrix3D) -> Result<MatrixDecomposed3D, ()> {
562 let combine = |a: [f32; 3], b: [f32; 3], ascl: f32, bscl: f32| {
564 [
565 (ascl * a[0]) + (bscl * b[0]),
566 (ascl * a[1]) + (bscl * b[1]),
567 (ascl * a[2]) + (bscl * b[2]),
568 ]
569 };
570 let dot = |a: [f32; 3], b: [f32; 3]| a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
572 let cross = |row1: [f32; 3], row2: [f32; 3]| {
574 [
575 row1[1] * row2[2] - row1[2] * row2[1],
576 row1[2] * row2[0] - row1[0] * row2[2],
577 row1[0] * row2[1] - row1[1] * row2[0],
578 ]
579 };
580
581 if matrix.m44 == 0.0 {
582 return Err(());
583 }
584
585 let scaling_factor = matrix.m44;
586
587 matrix.scale_by_factor(1.0 / scaling_factor);
589
590 let mut perspective_matrix = matrix;
593
594 perspective_matrix.m14 = 0.0;
595 perspective_matrix.m24 = 0.0;
596 perspective_matrix.m34 = 0.0;
597 perspective_matrix.m44 = 1.0;
598
599 if perspective_matrix.determinant() == 0.0 {
600 return Err(());
601 }
602
603 let perspective = if matrix.m14 != 0.0 || matrix.m24 != 0.0 || matrix.m34 != 0.0 {
605 let right_hand_side: [f32; 4] = [matrix.m14, matrix.m24, matrix.m34, matrix.m44];
606
607 perspective_matrix = perspective_matrix.inverse().unwrap().transpose();
608 let perspective = perspective_matrix.pre_mul_point4(&right_hand_side);
609 Perspective(
613 perspective[0],
614 perspective[1],
615 perspective[2],
616 perspective[3],
617 )
618 } else {
619 Perspective(0.0, 0.0, 0.0, 1.0)
620 };
621
622 let translate = Translate3D(matrix.m41, matrix.m42, matrix.m43);
624
625 let mut row = matrix.get_matrix_3x3_part();
627
628 let row0len = (row[0][0] * row[0][0] + row[0][1] * row[0][1] + row[0][2] * row[0][2]).sqrt();
630 let mut scale = Scale3D(row0len, 0.0, 0.0);
631 row[0] = [
632 row[0][0] / row0len,
633 row[0][1] / row0len,
634 row[0][2] / row0len,
635 ];
636
637 let mut skew = Skew(dot(row[0], row[1]), 0.0, 0.0);
639 row[1] = combine(row[1], row[0], 1.0, -skew.0);
640
641 let row1len = (row[1][0] * row[1][0] + row[1][1] * row[1][1] + row[1][2] * row[1][2]).sqrt();
643 scale.1 = row1len;
644 row[1] = [
645 row[1][0] / row1len,
646 row[1][1] / row1len,
647 row[1][2] / row1len,
648 ];
649 skew.0 /= scale.1;
650
651 skew.1 = dot(row[0], row[2]);
653 row[2] = combine(row[2], row[0], 1.0, -skew.1);
654 skew.2 = dot(row[1], row[2]);
655 row[2] = combine(row[2], row[1], 1.0, -skew.2);
656
657 let row2len = (row[2][0] * row[2][0] + row[2][1] * row[2][1] + row[2][2] * row[2][2]).sqrt();
659 scale.2 = row2len;
660 row[2] = [
661 row[2][0] / row2len,
662 row[2][1] / row2len,
663 row[2][2] / row2len,
664 ];
665 skew.1 /= scale.2;
666 skew.2 /= scale.2;
667
668 if dot(row[0], cross(row[1], row[2])) < 0.0 {
672 scale.negate();
673 for i in 0..3 {
674 row[i][0] *= -1.0;
675 row[i][1] *= -1.0;
676 row[i][2] *= -1.0;
677 }
678 }
679
680 let mut quaternion = Quaternion(
682 0.5 * ((1.0 + row[0][0] - row[1][1] - row[2][2]).max(0.0) as f64).sqrt(),
683 0.5 * ((1.0 - row[0][0] + row[1][1] - row[2][2]).max(0.0) as f64).sqrt(),
684 0.5 * ((1.0 - row[0][0] - row[1][1] + row[2][2]).max(0.0) as f64).sqrt(),
685 0.5 * ((1.0 + row[0][0] + row[1][1] + row[2][2]).max(0.0) as f64).sqrt(),
686 );
687
688 if row[2][1] > row[1][2] {
689 quaternion.0 = -quaternion.0
690 }
691 if row[0][2] > row[2][0] {
692 quaternion.1 = -quaternion.1
693 }
694 if row[1][0] > row[0][1] {
695 quaternion.2 = -quaternion.2
696 }
697
698 Ok(MatrixDecomposed3D {
699 translate,
700 scale,
701 skew,
702 perspective,
703 quaternion,
704 })
705}
706
707fn decompose_2d_matrix(matrix: &Matrix3D) -> Result<MatrixDecomposed3D, ()> {
829 let (mut m11, mut m12) = (matrix.m11, matrix.m12);
835 let (mut m21, mut m22) = (matrix.m21, matrix.m22);
836 if m11 * m22 == m12 * m21 {
838 return Err(());
839 }
840
841 let mut scale_x = (m11 * m11 + m12 * m12).sqrt();
842 m11 /= scale_x;
843 m12 /= scale_x;
844
845 let mut shear_xy = m11 * m21 + m12 * m22;
846 m21 -= m11 * shear_xy;
847 m22 -= m12 * shear_xy;
848
849 let scale_y = (m21 * m21 + m22 * m22).sqrt();
850 m21 /= scale_y;
851 m22 /= scale_y;
852 shear_xy /= scale_y;
853
854 let determinant = m11 * m22 - m12 * m21;
855 if 0.99 > determinant.abs() || determinant.abs() > 1.01 {
857 return Err(());
858 }
859
860 if determinant < 0. {
861 m11 = -m11;
862 m12 = -m12;
863 shear_xy = -shear_xy;
864 scale_x = -scale_x;
865 }
866
867 Ok(MatrixDecomposed3D {
868 translate: Translate3D(matrix.m41, matrix.m42, 0.),
869 scale: Scale3D(scale_x, scale_y, 1.),
870 skew: Skew(shear_xy, 0., 0.),
871 perspective: Perspective(0., 0., 0., 1.),
872 quaternion: Quaternion::from_direction_and_angle(
873 &DirectionVector::new(0., 0., 1.),
874 m12.atan2(m11) as f64,
875 ),
876 })
877}
878
879impl Animate for Matrix3D {
880 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
881 let (from, to) = if self.is_3d() || other.is_3d() {
882 (decompose_3d_matrix(*self)?, decompose_3d_matrix(*other)?)
883 } else {
884 (decompose_2d_matrix(self)?, decompose_2d_matrix(other)?)
885 };
886 Ok(Matrix3D::from(from.animate(&to, procedure)?))
890 }
891}
892
893impl ComputeSquaredDistance for Matrix3D {
894 #[inline]
895 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
896 let (from, to) = if self.is_3d() || other.is_3d() {
897 (decompose_3d_matrix(*self)?, decompose_3d_matrix(*other)?)
898 } else {
899 (decompose_2d_matrix(self)?, decompose_2d_matrix(other)?)
900 };
901 from.compute_squared_distance(&to)
902 }
903}
904
905fn is_matched_operation(
909 first: &ComputedTransformOperation,
910 second: &ComputedTransformOperation,
911) -> bool {
912 match (first, second) {
913 (&TransformOperation::Matrix(..), &TransformOperation::Matrix(..))
914 | (&TransformOperation::Matrix3D(..), &TransformOperation::Matrix3D(..))
915 | (&TransformOperation::Skew(..), &TransformOperation::Skew(..))
916 | (&TransformOperation::SkewX(..), &TransformOperation::SkewX(..))
917 | (&TransformOperation::SkewY(..), &TransformOperation::SkewY(..))
918 | (&TransformOperation::Rotate(..), &TransformOperation::Rotate(..))
919 | (&TransformOperation::Rotate3D(..), &TransformOperation::Rotate3D(..))
920 | (&TransformOperation::RotateX(..), &TransformOperation::RotateX(..))
921 | (&TransformOperation::RotateY(..), &TransformOperation::RotateY(..))
922 | (&TransformOperation::RotateZ(..), &TransformOperation::RotateZ(..))
923 | (&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => true,
924 (a, b) if a.is_translate() && b.is_translate() => true,
926 (a, b) if a.is_scale() && b.is_scale() => true,
927 (a, b) if a.is_rotate() && b.is_rotate() => true,
928 _ => false,
930 }
931}
932
933impl Animate for ComputedTransform {
935 #[inline]
936 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
937 use std::borrow::Cow;
938
939 if procedure == Procedure::Add {
944 let result = self.0.iter().chain(&*other.0).cloned().collect();
945 return Ok(Transform(result));
946 }
947
948 let this = Cow::Borrowed(&self.0);
949 let other = Cow::Borrowed(&other.0);
950
951 let mut result = this
953 .iter()
954 .zip(other.iter())
955 .take_while(|(this, other)| is_matched_operation(this, other))
956 .map(|(this, other)| this.animate(other, procedure))
957 .collect::<Result<Vec<_>, _>>()?;
958
959 let this_remainder = if this.len() > result.len() {
961 Some(&this[result.len()..])
962 } else {
963 None
964 };
965 let other_remainder = if other.len() > result.len() {
966 Some(&other[result.len()..])
967 } else {
968 None
969 };
970
971 match (this_remainder, other_remainder) {
972 (Some(this_remainder), Some(other_remainder)) => {
975 result.push(TransformOperation::animate_mismatched_transforms(
976 this_remainder,
977 other_remainder,
978 procedure,
979 )?);
980 },
981 (Some(remainder), None) | (None, Some(remainder)) => {
985 let fill_right = this_remainder.is_some();
986 result.append(
987 &mut remainder
988 .iter()
989 .map(|transform| {
990 let identity = transform.to_animated_zero().unwrap();
991
992 match transform {
993 TransformOperation::AccumulateMatrix { .. }
994 | TransformOperation::InterpolateMatrix { .. } => {
995 let (from, to) = if fill_right {
996 (transform, &identity)
997 } else {
998 (&identity, transform)
999 };
1000
1001 TransformOperation::animate_mismatched_transforms(
1002 &[from.clone()],
1003 &[to.clone()],
1004 procedure,
1005 )
1006 },
1007 _ => {
1008 let (lhs, rhs) = if fill_right {
1009 (transform, &identity)
1010 } else {
1011 (&identity, transform)
1012 };
1013 lhs.animate(rhs, procedure)
1014 },
1015 }
1016 })
1017 .collect::<Result<Vec<_>, _>>()?,
1018 );
1019 },
1020 (None, None) => {},
1021 }
1022
1023 Ok(Transform(result.into()))
1024 }
1025}
1026
1027impl ComputeSquaredDistance for ComputedTransform {
1028 #[inline]
1029 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1030 let squared_dist = super::lists::with_zero::squared_distance(&self.0, &other.0);
1031
1032 if squared_dist.is_err() {
1038 let rect = euclid::Rect::zero();
1039 let matrix1: Matrix3D = self.to_transform_3d_matrix(Some(&rect))?.0.into();
1040 let matrix2: Matrix3D = other.to_transform_3d_matrix(Some(&rect))?.0.into();
1041 return matrix1.compute_squared_distance(&matrix2);
1042 }
1043
1044 squared_dist
1045 }
1046}
1047
1048impl Animate for ComputedTransformOperation {
1050 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1051 match (self, other) {
1052 (TransformOperation::Matrix3D(this), TransformOperation::Matrix3D(other)) => Ok(
1053 TransformOperation::Matrix3D(this.animate(other, procedure)?),
1054 ),
1055 (TransformOperation::Matrix(this), TransformOperation::Matrix(other)) => {
1056 Ok(TransformOperation::Matrix(this.animate(other, procedure)?))
1057 },
1058 (TransformOperation::Skew(fx, fy), TransformOperation::Skew(tx, ty)) => Ok(
1059 TransformOperation::Skew(fx.animate(tx, procedure)?, fy.animate(ty, procedure)?),
1060 ),
1061 (TransformOperation::SkewX(f), TransformOperation::SkewX(t)) => {
1062 Ok(TransformOperation::SkewX(f.animate(t, procedure)?))
1063 },
1064 (TransformOperation::SkewY(f), TransformOperation::SkewY(t)) => {
1065 Ok(TransformOperation::SkewY(f.animate(t, procedure)?))
1066 },
1067 (
1068 TransformOperation::Translate3D(fx, fy, fz),
1069 TransformOperation::Translate3D(tx, ty, tz),
1070 ) => Ok(TransformOperation::Translate3D(
1071 fx.animate(tx, procedure)?,
1072 fy.animate(ty, procedure)?,
1073 fz.animate(tz, procedure)?,
1074 )),
1075 (TransformOperation::Translate(fx, fy), TransformOperation::Translate(tx, ty)) => {
1076 Ok(TransformOperation::Translate(
1077 fx.animate(tx, procedure)?,
1078 fy.animate(ty, procedure)?,
1079 ))
1080 },
1081 (TransformOperation::TranslateX(f), TransformOperation::TranslateX(t)) => {
1082 Ok(TransformOperation::TranslateX(f.animate(t, procedure)?))
1083 },
1084 (TransformOperation::TranslateY(f), TransformOperation::TranslateY(t)) => {
1085 Ok(TransformOperation::TranslateY(f.animate(t, procedure)?))
1086 },
1087 (TransformOperation::TranslateZ(f), TransformOperation::TranslateZ(t)) => {
1088 Ok(TransformOperation::TranslateZ(f.animate(t, procedure)?))
1089 },
1090 (TransformOperation::Scale3D(fx, fy, fz), TransformOperation::Scale3D(tx, ty, tz)) => {
1091 Ok(TransformOperation::Scale3D(
1092 animate_multiplicative_factor(*fx, *tx, procedure)?,
1093 animate_multiplicative_factor(*fy, *ty, procedure)?,
1094 animate_multiplicative_factor(*fz, *tz, procedure)?,
1095 ))
1096 },
1097 (TransformOperation::ScaleX(f), TransformOperation::ScaleX(t)) => Ok(
1098 TransformOperation::ScaleX(animate_multiplicative_factor(*f, *t, procedure)?),
1099 ),
1100 (TransformOperation::ScaleY(f), TransformOperation::ScaleY(t)) => Ok(
1101 TransformOperation::ScaleY(animate_multiplicative_factor(*f, *t, procedure)?),
1102 ),
1103 (TransformOperation::ScaleZ(f), TransformOperation::ScaleZ(t)) => Ok(
1104 TransformOperation::ScaleZ(animate_multiplicative_factor(*f, *t, procedure)?),
1105 ),
1106 (TransformOperation::Scale(fx, fy), TransformOperation::Scale(tx, ty)) => {
1107 Ok(TransformOperation::Scale(
1108 animate_multiplicative_factor(*fx, *tx, procedure)?,
1109 animate_multiplicative_factor(*fy, *ty, procedure)?,
1110 ))
1111 },
1112 (
1113 &TransformOperation::Rotate3D(fx, fy, fz, fa),
1114 &TransformOperation::Rotate3D(tx, ty, tz, ta),
1115 ) => {
1116 let animated = Rotate::Rotate3D(fx, fy, fz, fa)
1117 .animate(&Rotate::Rotate3D(tx, ty, tz, ta), procedure)?;
1118 let (fx, fy, fz, fa) = ComputedRotate::resolve(&animated);
1119 Ok(TransformOperation::Rotate3D(fx, fy, fz, fa))
1120 },
1121 (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta)) => {
1122 Ok(TransformOperation::RotateX(fa.animate(&ta, procedure)?))
1123 },
1124 (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta)) => {
1125 Ok(TransformOperation::RotateY(fa.animate(&ta, procedure)?))
1126 },
1127 (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta)) => {
1128 Ok(TransformOperation::RotateZ(fa.animate(&ta, procedure)?))
1129 },
1130 (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {
1131 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1132 },
1133 (&TransformOperation::Rotate(fa), &TransformOperation::RotateZ(ta)) => {
1134 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1135 },
1136 (&TransformOperation::RotateZ(fa), &TransformOperation::Rotate(ta)) => {
1137 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1138 },
1139 (TransformOperation::Perspective(fd), TransformOperation::Perspective(td)) => {
1140 use crate::values::computed::CSSPixelLength;
1141 use crate::values::generics::transform::create_perspective_matrix;
1142
1143 let from = create_perspective_matrix(fd.infinity_or(|l| l.px()));
1151 let to = create_perspective_matrix(td.infinity_or(|l| l.px()));
1152
1153 let interpolated = Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?;
1154
1155 let decomposed = decompose_3d_matrix(interpolated)?;
1156 let perspective_z = decomposed.perspective.2;
1157 let used_value = if perspective_z >= 0. {
1160 transform::PerspectiveFunction::None
1161 } else {
1162 transform::PerspectiveFunction::Length(CSSPixelLength::new(
1163 if perspective_z <= -1. {
1164 1.
1165 } else {
1166 -1. / perspective_z
1167 },
1168 ))
1169 };
1170 Ok(TransformOperation::Perspective(used_value))
1171 },
1172 _ if self.is_translate() && other.is_translate() => self
1173 .to_translate_3d()
1174 .animate(&other.to_translate_3d(), procedure),
1175 _ if self.is_scale() && other.is_scale() => {
1176 self.to_scale_3d().animate(&other.to_scale_3d(), procedure)
1177 },
1178 _ if self.is_rotate() && other.is_rotate() => self
1179 .to_rotate_3d()
1180 .animate(&other.to_rotate_3d(), procedure),
1181 _ => Err(()),
1182 }
1183 }
1184}
1185
1186impl ComputedTransformOperation {
1187 fn try_animate_mismatched_transforms_in_place(
1190 left: &[Self],
1191 right: &[Self],
1192 procedure: Procedure,
1193 ) -> Result<Self, ()> {
1194 let (left, _left_3d) = Transform::components_to_transform_3d_matrix(left, None)?;
1195 let (right, _right_3d) = Transform::components_to_transform_3d_matrix(right, None)?;
1196 Ok(Self::Matrix3D(
1197 Matrix3D::from(left).animate(&Matrix3D::from(right), procedure)?,
1198 ))
1199 }
1200
1201 fn animate_mismatched_transforms(
1202 left: &[Self],
1203 right: &[Self],
1204 procedure: Procedure,
1205 ) -> Result<Self, ()> {
1206 if let Ok(op) = Self::try_animate_mismatched_transforms_in_place(left, right, procedure) {
1207 return Ok(op);
1208 }
1209 let from_list = Transform(left.to_vec().into());
1210 let to_list = Transform(right.to_vec().into());
1211 Ok(match procedure {
1212 Procedure::Add => {
1213 debug_assert!(false, "Addition should've been handled earlier");
1214 return Err(());
1215 },
1216 Procedure::Interpolate { progress } => Self::InterpolateMatrix {
1217 from_list,
1218 to_list,
1219 progress: Percentage(progress as f32),
1220 },
1221 Procedure::Accumulate { count } => Self::AccumulateMatrix {
1222 from_list,
1223 to_list,
1224 count: cmp::min(count, i32::MAX as u64) as i32,
1225 },
1226 })
1227 }
1228}
1229
1230impl ComputeSquaredDistance for ComputedTransformOperation {
1235 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1236 match (self, other) {
1237 (TransformOperation::Matrix3D(this), TransformOperation::Matrix3D(other)) => {
1238 this.compute_squared_distance(other)
1239 },
1240 (TransformOperation::Matrix(this), TransformOperation::Matrix(other)) => {
1241 let this: Matrix3D = (*this).into();
1242 let other: Matrix3D = (*other).into();
1243 this.compute_squared_distance(&other)
1244 },
1245 (TransformOperation::Skew(fx, fy), TransformOperation::Skew(tx, ty)) => {
1246 Ok(fx.compute_squared_distance(tx)? + fy.compute_squared_distance(ty)?)
1247 },
1248 (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t))
1249 | (&TransformOperation::SkewY(ref f), &TransformOperation::SkewY(ref t)) => {
1250 f.compute_squared_distance(t)
1251 },
1252 (
1253 TransformOperation::Translate3D(fx, fy, fz),
1254 TransformOperation::Translate3D(tx, ty, tz),
1255 ) => {
1256 let basis = Length::new(0.);
1263 let fx = fx.resolve(basis).px();
1264 let fy = fy.resolve(basis).px();
1265 let tx = tx.resolve(basis).px();
1266 let ty = ty.resolve(basis).px();
1267
1268 Ok(fx.compute_squared_distance(&tx)?
1269 + fy.compute_squared_distance(&ty)?
1270 + fz.compute_squared_distance(tz)?)
1271 },
1272 (TransformOperation::Scale3D(fx, fy, fz), TransformOperation::Scale3D(tx, ty, tz)) => {
1273 Ok(fx.compute_squared_distance(tx)?
1274 + fy.compute_squared_distance(ty)?
1275 + fz.compute_squared_distance(tz)?)
1276 },
1277 (
1278 &TransformOperation::Rotate3D(fx, fy, fz, fa),
1279 &TransformOperation::Rotate3D(tx, ty, tz, ta),
1280 ) => Rotate::Rotate3D(fx, fy, fz, fa)
1281 .compute_squared_distance(&Rotate::Rotate3D(tx, ty, tz, ta)),
1282 (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta))
1283 | (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta))
1284 | (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta))
1285 | (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {
1286 fa.compute_squared_distance(&ta)
1287 },
1288 (TransformOperation::Perspective(fd), TransformOperation::Perspective(td)) => fd
1289 .infinity_or(|l| l.px())
1290 .compute_squared_distance(&td.infinity_or(|l| l.px())),
1291 (&TransformOperation::Perspective(ref p), &TransformOperation::Matrix3D(ref m))
1292 | (&TransformOperation::Matrix3D(ref m), &TransformOperation::Perspective(ref p)) => {
1293 let mut p_matrix = Matrix3D::identity();
1296 let p = p.infinity_or(|p| p.px());
1297 if p >= 0. {
1298 p_matrix.m34 = -1. / p.max(1.);
1299 }
1300 p_matrix.compute_squared_distance(m)
1301 },
1302 _ if self.is_translate() && other.is_translate() => self
1306 .to_translate_3d()
1307 .compute_squared_distance(&other.to_translate_3d()),
1308 _ if self.is_scale() && other.is_scale() => self
1309 .to_scale_3d()
1310 .compute_squared_distance(&other.to_scale_3d()),
1311 _ if self.is_rotate() && other.is_rotate() => self
1312 .to_rotate_3d()
1313 .compute_squared_distance(&other.to_rotate_3d()),
1314 _ => Err(()),
1315 }
1316 }
1317}
1318
1319impl ComputedRotate {
1324 fn resolve(&self) -> (Number, Number, Number, Angle) {
1325 match *self {
1330 Rotate::None => (0., 0., 1., Angle::zero()),
1331 Rotate::Rotate3D(rx, ry, rz, angle) => (rx, ry, rz, angle),
1332 Rotate::Rotate(angle) => (0., 0., 1., angle),
1333 }
1334 }
1335}
1336
1337impl Animate for ComputedRotate {
1338 #[inline]
1339 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1340 use euclid::approxeq::ApproxEq;
1341 match (self, other) {
1342 (&Rotate::None, &Rotate::None) => Ok(Rotate::None),
1343 (&Rotate::Rotate3D(fx, fy, fz, fa), &Rotate::None) => {
1344 let (fx, fy, fz, fa) = transform::get_normalized_vector_and_angle(fx, fy, fz, fa);
1349 Ok(Rotate::Rotate3D(
1350 fx,
1351 fy,
1352 fz,
1353 fa.animate(&Angle::zero(), procedure)?,
1354 ))
1355 },
1356 (&Rotate::None, &Rotate::Rotate3D(tx, ty, tz, ta)) => {
1357 let (tx, ty, tz, ta) = transform::get_normalized_vector_and_angle(tx, ty, tz, ta);
1359 Ok(Rotate::Rotate3D(
1360 tx,
1361 ty,
1362 tz,
1363 Angle::zero().animate(&ta, procedure)?,
1364 ))
1365 },
1366 (&Rotate::Rotate3D(..), _) | (_, &Rotate::Rotate3D(..)) => {
1367 let (from, to) = (self.resolve(), other.resolve());
1370 let (fx, fy, fz, fa) =
1373 transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
1374 let (tx, ty, tz, ta) =
1375 transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
1376
1377 let fv = DirectionVector::new(fx, fy, fz);
1384 let tv = DirectionVector::new(tx, ty, tz);
1385 if fa.is_zero() || ta.is_zero() || fv.approx_eq(&tv) {
1386 let (x, y, z) = if fa.is_zero() && ta.is_zero() {
1387 (0., 0., 1.)
1388 } else if fa.is_zero() {
1389 (tx, ty, tz)
1390 } else {
1391 (fx, fy, fz)
1393 };
1394 return Ok(Rotate::Rotate3D(x, y, z, fa.animate(&ta, procedure)?));
1395 }
1396
1397 let rq = if procedure == Procedure::Add {
1407 let f = ComputedTransformOperation::Rotate3D(fx, fy, fz, fa);
1412 let t = ComputedTransformOperation::Rotate3D(tx, ty, tz, ta);
1413 let v =
1414 Transform(vec![f].into()).animate(&Transform(vec![t].into()), procedure)?;
1415 let (m, _) = v.to_transform_3d_matrix(None)?;
1416 decompose_3d_matrix(Matrix3D::from(m))?.quaternion
1418 } else {
1419 let fq = Quaternion::from_direction_and_angle(&fv, fa.radians64());
1432 let tq = Quaternion::from_direction_and_angle(&tv, ta.radians64());
1433 Quaternion::animate(&fq, &tq, procedure)?
1434 };
1435
1436 let (x, y, z, angle) = transform::get_normalized_vector_and_angle(
1437 rq.0 as f32,
1438 rq.1 as f32,
1439 rq.2 as f32,
1440 rq.3.clamp(-1.0, 1.0).acos() as f32 * 2.0,
1443 );
1444
1445 Ok(Rotate::Rotate3D(x, y, z, Angle::from_radians(angle)))
1446 },
1447 (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => {
1448 let (from, to) = (self.resolve().3, other.resolve().3);
1450 Ok(Rotate::Rotate(from.animate(&to, procedure)?))
1451 },
1452 }
1453 }
1454}
1455
1456impl ComputeSquaredDistance for ComputedRotate {
1457 #[inline]
1458 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1459 use euclid::approxeq::ApproxEq;
1460 match (self, other) {
1461 (&Rotate::None, &Rotate::None) => Ok(SquaredDistance::from_sqrt(0.)),
1462 (&Rotate::Rotate3D(_, _, _, a), &Rotate::None)
1463 | (&Rotate::None, &Rotate::Rotate3D(_, _, _, a)) => {
1464 a.compute_squared_distance(&Angle::zero())
1465 },
1466 (&Rotate::Rotate3D(..), _) | (_, &Rotate::Rotate3D(..)) => {
1467 let (from, to) = (self.resolve(), other.resolve());
1468 let (mut fx, mut fy, mut fz, angle1) =
1469 transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
1470 let (mut tx, mut ty, mut tz, angle2) =
1471 transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
1472
1473 if angle1.is_zero() && angle2.is_zero() {
1474 (fx, fy, fz) = (0., 0., 1.);
1475 (tx, ty, tz) = (0., 0., 1.);
1476 } else if angle1.is_zero() {
1477 (fx, fy, fz) = (tx, ty, tz);
1478 } else if angle2.is_zero() {
1479 (tx, ty, tz) = (fx, fy, fz);
1480 }
1481
1482 let v1 = DirectionVector::new(fx, fy, fz);
1483 let v2 = DirectionVector::new(tx, ty, tz);
1484 if v1.approx_eq(&v2) {
1485 angle1.compute_squared_distance(&angle2)
1486 } else {
1487 let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64());
1488 let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64());
1489 q1.compute_squared_distance(&q2)
1490 }
1491 },
1492 (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => self
1493 .resolve()
1494 .3
1495 .compute_squared_distance(&other.resolve().3),
1496 }
1497 }
1498}
1499
1500impl ComputedTranslate {
1502 fn resolve(&self) -> (LengthPercentage, LengthPercentage, Length) {
1503 match *self {
1508 Translate::None => (
1509 LengthPercentage::zero(),
1510 LengthPercentage::zero(),
1511 Length::zero(),
1512 ),
1513 Translate::Translate(ref tx, ref ty, ref tz) => (tx.clone(), ty.clone(), *tz),
1514 }
1515 }
1516}
1517
1518impl Animate for ComputedTranslate {
1519 #[inline]
1520 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1521 match (self, other) {
1522 (&Translate::None, &Translate::None) => Ok(Translate::None),
1523 (&Translate::Translate(..), _) | (_, &Translate::Translate(..)) => {
1524 let (from, to) = (self.resolve(), other.resolve());
1525 Ok(Translate::Translate(
1526 from.0.animate(&to.0, procedure)?,
1527 from.1.animate(&to.1, procedure)?,
1528 from.2.animate(&to.2, procedure)?,
1529 ))
1530 },
1531 }
1532 }
1533}
1534
1535impl ComputeSquaredDistance for ComputedTranslate {
1536 #[inline]
1537 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1538 let (from, to) = (self.resolve(), other.resolve());
1539 Ok(from.0.compute_squared_distance(&to.0)?
1540 + from.1.compute_squared_distance(&to.1)?
1541 + from.2.compute_squared_distance(&to.2)?)
1542 }
1543}
1544
1545impl ComputedScale {
1547 fn resolve(&self) -> (Number, Number, Number) {
1548 match *self {
1553 Scale::None => (1.0, 1.0, 1.0),
1554 Scale::Scale(sx, sy, sz) => (sx, sy, sz),
1555 }
1556 }
1557}
1558
1559impl Animate for ComputedScale {
1560 #[inline]
1561 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1562 match (self, other) {
1563 (&Scale::None, &Scale::None) => Ok(Scale::None),
1564 (&Scale::Scale(..), _) | (_, &Scale::Scale(..)) => {
1565 let (from, to) = (self.resolve(), other.resolve());
1566 if procedure == Procedure::Add {
1571 return Ok(Scale::Scale(from.0 * to.0, from.1 * to.1, from.2 * to.2));
1573 }
1574 Ok(Scale::Scale(
1575 animate_multiplicative_factor(from.0, to.0, procedure)?,
1576 animate_multiplicative_factor(from.1, to.1, procedure)?,
1577 animate_multiplicative_factor(from.2, to.2, procedure)?,
1578 ))
1579 },
1580 }
1581 }
1582}
1583
1584impl ComputeSquaredDistance for ComputedScale {
1585 #[inline]
1586 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1587 let (from, to) = (self.resolve(), other.resolve());
1588 Ok(from.0.compute_squared_distance(&to.0)?
1589 + from.1.compute_squared_distance(&to.1)?
1590 + from.2.compute_squared_distance(&to.2)?)
1591 }
1592}