1use super::animate_multiplicative_factor;
10use super::{Animate, Procedure, ToAnimatedZero};
11use crate::Zero;
12use crate::derives::*;
13use crate::values::CSSFloat;
14use crate::values::computed::Angle;
15use crate::values::computed::transform::Rotate as ComputedRotate;
16use crate::values::computed::transform::Scale as ComputedScale;
17use crate::values::computed::transform::Transform as ComputedTransform;
18use crate::values::computed::transform::TransformOperation as ComputedTransformOperation;
19use crate::values::computed::transform::Translate as ComputedTranslate;
20use crate::values::computed::transform::{DirectionVector, Matrix, Matrix3D};
21use crate::values::computed::{Length, LengthPercentage};
22use crate::values::computed::{Number, Percentage};
23use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
24use crate::values::generics::transform::{self, Transform, TransformOperation};
25use crate::values::generics::transform::{Rotate, Scale, Translate};
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, ()> {
827 let (mut m11, mut m12) = (matrix.m11, matrix.m12);
833 let (mut m21, mut m22) = (matrix.m21, matrix.m22);
834 if m11 * m22 == m12 * m21 {
836 return Err(());
837 }
838
839 let mut scale_x = (m11 * m11 + m12 * m12).sqrt();
840 m11 /= scale_x;
841 m12 /= scale_x;
842
843 let mut shear_xy = m11 * m21 + m12 * m22;
844 m21 -= m11 * shear_xy;
845 m22 -= m12 * shear_xy;
846
847 let scale_y = (m21 * m21 + m22 * m22).sqrt();
848 m21 /= scale_y;
849 m22 /= scale_y;
850 shear_xy /= scale_y;
851
852 let determinant = m11 * m22 - m12 * m21;
853 if 0.99 > determinant.abs() || determinant.abs() > 1.01 {
855 return Err(());
856 }
857
858 if determinant < 0. {
859 m11 = -m11;
860 m12 = -m12;
861 shear_xy = -shear_xy;
862 scale_x = -scale_x;
863 }
864
865 Ok(MatrixDecomposed3D {
866 translate: Translate3D(matrix.m41, matrix.m42, 0.),
867 scale: Scale3D(scale_x, scale_y, 1.),
868 skew: Skew(shear_xy, 0., 0.),
869 perspective: Perspective(0., 0., 0., 1.),
870 quaternion: Quaternion::from_direction_and_angle(
871 &DirectionVector::new(0., 0., 1.),
872 m12.atan2(m11) as f64,
873 ),
874 })
875}
876
877impl Animate for Matrix3D {
878 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
879 let (from, to) = if self.is_3d() || other.is_3d() {
880 (decompose_3d_matrix(*self)?, decompose_3d_matrix(*other)?)
881 } else {
882 (decompose_2d_matrix(self)?, decompose_2d_matrix(other)?)
883 };
884 Ok(Matrix3D::from(from.animate(&to, procedure)?))
888 }
889}
890
891impl ComputeSquaredDistance for Matrix3D {
892 #[inline]
893 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
894 let (from, to) = if self.is_3d() || other.is_3d() {
895 (decompose_3d_matrix(*self)?, decompose_3d_matrix(*other)?)
896 } else {
897 (decompose_2d_matrix(self)?, decompose_2d_matrix(other)?)
898 };
899 from.compute_squared_distance(&to)
900 }
901}
902
903fn is_matched_operation(
907 first: &ComputedTransformOperation,
908 second: &ComputedTransformOperation,
909) -> bool {
910 match (first, second) {
911 (&TransformOperation::Matrix(..), &TransformOperation::Matrix(..))
912 | (&TransformOperation::Matrix3D(..), &TransformOperation::Matrix3D(..))
913 | (&TransformOperation::Skew(..), &TransformOperation::Skew(..))
914 | (&TransformOperation::SkewX(..), &TransformOperation::SkewX(..))
915 | (&TransformOperation::SkewY(..), &TransformOperation::SkewY(..))
916 | (&TransformOperation::Rotate(..), &TransformOperation::Rotate(..))
917 | (&TransformOperation::Rotate3D(..), &TransformOperation::Rotate3D(..))
918 | (&TransformOperation::RotateX(..), &TransformOperation::RotateX(..))
919 | (&TransformOperation::RotateY(..), &TransformOperation::RotateY(..))
920 | (&TransformOperation::RotateZ(..), &TransformOperation::RotateZ(..))
921 | (&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => true,
922 (a, b) if a.is_translate() && b.is_translate() => true,
924 (a, b) if a.is_scale() && b.is_scale() => true,
925 (a, b) if a.is_rotate() && b.is_rotate() => true,
926 _ => false,
928 }
929}
930
931impl Animate for ComputedTransform {
933 #[inline]
934 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
935 use std::borrow::Cow;
936
937 if procedure == Procedure::Add {
942 let result = self.0.iter().chain(&*other.0).cloned().collect();
943 return Ok(Transform(result));
944 }
945
946 let this = Cow::Borrowed(&self.0);
947 let other = Cow::Borrowed(&other.0);
948
949 let mut result = this
951 .iter()
952 .zip(other.iter())
953 .take_while(|(this, other)| is_matched_operation(this, other))
954 .map(|(this, other)| this.animate(other, procedure))
955 .collect::<Result<Vec<_>, _>>()?;
956
957 let this_remainder = if this.len() > result.len() {
959 Some(&this[result.len()..])
960 } else {
961 None
962 };
963 let other_remainder = if other.len() > result.len() {
964 Some(&other[result.len()..])
965 } else {
966 None
967 };
968
969 match (this_remainder, other_remainder) {
970 (Some(this_remainder), Some(other_remainder)) => {
973 result.push(TransformOperation::animate_mismatched_transforms(
974 this_remainder,
975 other_remainder,
976 procedure,
977 )?);
978 },
979 (Some(remainder), None) | (None, Some(remainder)) => {
983 let fill_right = this_remainder.is_some();
984 result.append(
985 &mut remainder
986 .iter()
987 .map(|transform| {
988 let identity = transform.to_animated_zero().unwrap();
989
990 match transform {
991 TransformOperation::AccumulateMatrix { .. }
992 | TransformOperation::InterpolateMatrix { .. } => {
993 let (from, to) = if fill_right {
994 (transform, &identity)
995 } else {
996 (&identity, transform)
997 };
998
999 TransformOperation::animate_mismatched_transforms(
1000 std::slice::from_ref(from),
1001 std::slice::from_ref(to),
1002 procedure,
1003 )
1004 },
1005 _ => {
1006 let (lhs, rhs) = if fill_right {
1007 (transform, &identity)
1008 } else {
1009 (&identity, transform)
1010 };
1011 lhs.animate(rhs, procedure)
1012 },
1013 }
1014 })
1015 .collect::<Result<Vec<_>, _>>()?,
1016 );
1017 },
1018 (None, None) => {},
1019 }
1020
1021 Ok(Transform(result.into()))
1022 }
1023}
1024
1025impl ComputeSquaredDistance for ComputedTransform {
1026 #[inline]
1027 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1028 let squared_dist = super::lists::with_zero::squared_distance(&self.0, &other.0);
1029
1030 if squared_dist.is_err() {
1036 let rect = euclid::Rect::zero();
1037 let matrix1: Matrix3D = self.to_transform_3d_matrix(Some(&rect))?.0.into();
1038 let matrix2: Matrix3D = other.to_transform_3d_matrix(Some(&rect))?.0.into();
1039 return matrix1.compute_squared_distance(&matrix2);
1040 }
1041
1042 squared_dist
1043 }
1044}
1045
1046impl Animate for ComputedTransformOperation {
1048 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1049 match (self, other) {
1050 (TransformOperation::Matrix3D(this), TransformOperation::Matrix3D(other)) => Ok(
1051 TransformOperation::Matrix3D(this.animate(other, procedure)?),
1052 ),
1053 (TransformOperation::Matrix(this), TransformOperation::Matrix(other)) => {
1054 Ok(TransformOperation::Matrix(this.animate(other, procedure)?))
1055 },
1056 (TransformOperation::Skew(fx, fy), TransformOperation::Skew(tx, ty)) => Ok(
1057 TransformOperation::Skew(fx.animate(tx, procedure)?, fy.animate(ty, procedure)?),
1058 ),
1059 (TransformOperation::SkewX(f), TransformOperation::SkewX(t)) => {
1060 Ok(TransformOperation::SkewX(f.animate(t, procedure)?))
1061 },
1062 (TransformOperation::SkewY(f), TransformOperation::SkewY(t)) => {
1063 Ok(TransformOperation::SkewY(f.animate(t, procedure)?))
1064 },
1065 (
1066 TransformOperation::Translate3D(fx, fy, fz),
1067 TransformOperation::Translate3D(tx, ty, tz),
1068 ) => Ok(TransformOperation::Translate3D(
1069 fx.animate(tx, procedure)?,
1070 fy.animate(ty, procedure)?,
1071 fz.animate(tz, procedure)?,
1072 )),
1073 (TransformOperation::Translate(fx, fy), TransformOperation::Translate(tx, ty)) => {
1074 Ok(TransformOperation::Translate(
1075 fx.animate(tx, procedure)?,
1076 fy.animate(ty, procedure)?,
1077 ))
1078 },
1079 (TransformOperation::TranslateX(f), TransformOperation::TranslateX(t)) => {
1080 Ok(TransformOperation::TranslateX(f.animate(t, procedure)?))
1081 },
1082 (TransformOperation::TranslateY(f), TransformOperation::TranslateY(t)) => {
1083 Ok(TransformOperation::TranslateY(f.animate(t, procedure)?))
1084 },
1085 (TransformOperation::TranslateZ(f), TransformOperation::TranslateZ(t)) => {
1086 Ok(TransformOperation::TranslateZ(f.animate(t, procedure)?))
1087 },
1088 (TransformOperation::Scale3D(fx, fy, fz), TransformOperation::Scale3D(tx, ty, tz)) => {
1089 Ok(TransformOperation::Scale3D(
1090 animate_multiplicative_factor(*fx, *tx, procedure)?,
1091 animate_multiplicative_factor(*fy, *ty, procedure)?,
1092 animate_multiplicative_factor(*fz, *tz, procedure)?,
1093 ))
1094 },
1095 (TransformOperation::ScaleX(f), TransformOperation::ScaleX(t)) => Ok(
1096 TransformOperation::ScaleX(animate_multiplicative_factor(*f, *t, procedure)?),
1097 ),
1098 (TransformOperation::ScaleY(f), TransformOperation::ScaleY(t)) => Ok(
1099 TransformOperation::ScaleY(animate_multiplicative_factor(*f, *t, procedure)?),
1100 ),
1101 (TransformOperation::ScaleZ(f), TransformOperation::ScaleZ(t)) => Ok(
1102 TransformOperation::ScaleZ(animate_multiplicative_factor(*f, *t, procedure)?),
1103 ),
1104 (TransformOperation::Scale(fx, fy), TransformOperation::Scale(tx, ty)) => {
1105 Ok(TransformOperation::Scale(
1106 animate_multiplicative_factor(*fx, *tx, procedure)?,
1107 animate_multiplicative_factor(*fy, *ty, procedure)?,
1108 ))
1109 },
1110 (
1111 &TransformOperation::Rotate3D(fx, fy, fz, fa),
1112 &TransformOperation::Rotate3D(tx, ty, tz, ta),
1113 ) => {
1114 let animated = Rotate::Rotate3D(fx, fy, fz, fa)
1115 .animate(&Rotate::Rotate3D(tx, ty, tz, ta), procedure)?;
1116 let (fx, fy, fz, fa) = ComputedRotate::resolve(&animated);
1117 Ok(TransformOperation::Rotate3D(fx, fy, fz, fa))
1118 },
1119 (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta)) => {
1120 Ok(TransformOperation::RotateX(fa.animate(&ta, procedure)?))
1121 },
1122 (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta)) => {
1123 Ok(TransformOperation::RotateY(fa.animate(&ta, procedure)?))
1124 },
1125 (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta)) => {
1126 Ok(TransformOperation::RotateZ(fa.animate(&ta, procedure)?))
1127 },
1128 (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {
1129 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1130 },
1131 (&TransformOperation::Rotate(fa), &TransformOperation::RotateZ(ta)) => {
1132 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1133 },
1134 (&TransformOperation::RotateZ(fa), &TransformOperation::Rotate(ta)) => {
1135 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1136 },
1137 (TransformOperation::Perspective(fd), TransformOperation::Perspective(td)) => {
1138 use crate::values::computed::CSSPixelLength;
1139 use crate::values::generics::transform::create_perspective_matrix;
1140
1141 let from = create_perspective_matrix(fd.infinity_or(|l| l.px()));
1149 let to = create_perspective_matrix(td.infinity_or(|l| l.px()));
1150
1151 let interpolated = Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?;
1152
1153 let decomposed = decompose_3d_matrix(interpolated)?;
1154 let perspective_z = decomposed.perspective.2;
1155 let used_value = if perspective_z >= 0. {
1158 transform::PerspectiveFunction::None
1159 } else {
1160 transform::PerspectiveFunction::Length(CSSPixelLength::new(
1161 if perspective_z <= -1. {
1162 1.
1163 } else {
1164 -1. / perspective_z
1165 },
1166 ))
1167 };
1168 Ok(TransformOperation::Perspective(used_value))
1169 },
1170 _ if self.is_translate() && other.is_translate() => self
1171 .to_translate_3d()
1172 .animate(&other.to_translate_3d(), procedure),
1173 _ if self.is_scale() && other.is_scale() => {
1174 self.to_scale_3d().animate(&other.to_scale_3d(), procedure)
1175 },
1176 _ if self.is_rotate() && other.is_rotate() => self
1177 .to_rotate_3d()
1178 .animate(&other.to_rotate_3d(), procedure),
1179 _ => Err(()),
1180 }
1181 }
1182}
1183
1184impl ComputedTransformOperation {
1185 fn try_animate_mismatched_transforms_in_place(
1188 left: &[Self],
1189 right: &[Self],
1190 procedure: Procedure,
1191 ) -> Result<Self, ()> {
1192 let (left, _left_3d) = Transform::components_to_transform_3d_matrix(left, None)?;
1193 let (right, _right_3d) = Transform::components_to_transform_3d_matrix(right, None)?;
1194 Ok(Self::Matrix3D(
1195 Matrix3D::from(left).animate(&Matrix3D::from(right), procedure)?,
1196 ))
1197 }
1198
1199 fn animate_mismatched_transforms(
1200 left: &[Self],
1201 right: &[Self],
1202 procedure: Procedure,
1203 ) -> Result<Self, ()> {
1204 if let Ok(op) = Self::try_animate_mismatched_transforms_in_place(left, right, procedure) {
1205 return Ok(op);
1206 }
1207 let from_list = Transform(left.to_vec().into());
1208 let to_list = Transform(right.to_vec().into());
1209 Ok(match procedure {
1210 Procedure::Add => {
1211 debug_assert!(false, "Addition should've been handled earlier");
1212 return Err(());
1213 },
1214 Procedure::Interpolate { progress } => Self::InterpolateMatrix {
1215 from_list,
1216 to_list,
1217 progress: Percentage(progress as f32),
1218 },
1219 Procedure::Accumulate { count } => Self::AccumulateMatrix {
1220 from_list,
1221 to_list,
1222 count: cmp::min(count, i32::MAX as u64) as i32,
1223 },
1224 })
1225 }
1226}
1227
1228impl ComputeSquaredDistance for ComputedTransformOperation {
1233 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1234 match (self, other) {
1235 (TransformOperation::Matrix3D(this), TransformOperation::Matrix3D(other)) => {
1236 this.compute_squared_distance(other)
1237 },
1238 (TransformOperation::Matrix(this), TransformOperation::Matrix(other)) => {
1239 let this: Matrix3D = (*this).into();
1240 let other: Matrix3D = (*other).into();
1241 this.compute_squared_distance(&other)
1242 },
1243 (TransformOperation::Skew(fx, fy), TransformOperation::Skew(tx, ty)) => {
1244 Ok(fx.compute_squared_distance(tx)? + fy.compute_squared_distance(ty)?)
1245 },
1246 (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t))
1247 | (&TransformOperation::SkewY(ref f), &TransformOperation::SkewY(ref t)) => {
1248 f.compute_squared_distance(t)
1249 },
1250 (
1251 TransformOperation::Translate3D(fx, fy, fz),
1252 TransformOperation::Translate3D(tx, ty, tz),
1253 ) => {
1254 let basis = Length::new(0.);
1261 let fx = fx.resolve(basis).px();
1262 let fy = fy.resolve(basis).px();
1263 let tx = tx.resolve(basis).px();
1264 let ty = ty.resolve(basis).px();
1265
1266 Ok(fx.compute_squared_distance(&tx)?
1267 + fy.compute_squared_distance(&ty)?
1268 + fz.compute_squared_distance(tz)?)
1269 },
1270 (TransformOperation::Scale3D(fx, fy, fz), TransformOperation::Scale3D(tx, ty, tz)) => {
1271 Ok(fx.compute_squared_distance(tx)?
1272 + fy.compute_squared_distance(ty)?
1273 + fz.compute_squared_distance(tz)?)
1274 },
1275 (
1276 &TransformOperation::Rotate3D(fx, fy, fz, fa),
1277 &TransformOperation::Rotate3D(tx, ty, tz, ta),
1278 ) => Rotate::Rotate3D(fx, fy, fz, fa)
1279 .compute_squared_distance(&Rotate::Rotate3D(tx, ty, tz, ta)),
1280 (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta))
1281 | (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta))
1282 | (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta))
1283 | (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {
1284 fa.compute_squared_distance(&ta)
1285 },
1286 (TransformOperation::Perspective(fd), TransformOperation::Perspective(td)) => fd
1287 .infinity_or(|l| l.px())
1288 .compute_squared_distance(&td.infinity_or(|l| l.px())),
1289 (&TransformOperation::Perspective(ref p), &TransformOperation::Matrix3D(ref m))
1290 | (&TransformOperation::Matrix3D(ref m), &TransformOperation::Perspective(ref p)) => {
1291 let mut p_matrix = Matrix3D::identity();
1294 let p = p.infinity_or(|p| p.px());
1295 if p >= 0. {
1296 p_matrix.m34 = -1. / p.max(1.);
1297 }
1298 p_matrix.compute_squared_distance(m)
1299 },
1300 _ if self.is_translate() && other.is_translate() => self
1304 .to_translate_3d()
1305 .compute_squared_distance(&other.to_translate_3d()),
1306 _ if self.is_scale() && other.is_scale() => self
1307 .to_scale_3d()
1308 .compute_squared_distance(&other.to_scale_3d()),
1309 _ if self.is_rotate() && other.is_rotate() => self
1310 .to_rotate_3d()
1311 .compute_squared_distance(&other.to_rotate_3d()),
1312 _ => Err(()),
1313 }
1314 }
1315}
1316
1317impl ComputedRotate {
1322 fn resolve(&self) -> (Number, Number, Number, Angle) {
1323 match *self {
1328 Rotate::None => (0., 0., 1., Angle::zero()),
1329 Rotate::Rotate3D(rx, ry, rz, angle) => (rx, ry, rz, angle),
1330 Rotate::Rotate(angle) => (0., 0., 1., angle),
1331 }
1332 }
1333}
1334
1335impl Animate for ComputedRotate {
1336 #[inline]
1337 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1338 use euclid::approxeq::ApproxEq;
1339 match (self, other) {
1340 (&Rotate::None, &Rotate::None) => Ok(Rotate::None),
1341 (&Rotate::Rotate3D(fx, fy, fz, fa), &Rotate::None) => {
1342 let (fx, fy, fz, fa) = transform::get_normalized_vector_and_angle(fx, fy, fz, fa);
1347 Ok(Rotate::Rotate3D(
1348 fx,
1349 fy,
1350 fz,
1351 fa.animate(&Angle::zero(), procedure)?,
1352 ))
1353 },
1354 (&Rotate::None, &Rotate::Rotate3D(tx, ty, tz, ta)) => {
1355 let (tx, ty, tz, ta) = transform::get_normalized_vector_and_angle(tx, ty, tz, ta);
1357 Ok(Rotate::Rotate3D(
1358 tx,
1359 ty,
1360 tz,
1361 Angle::zero().animate(&ta, procedure)?,
1362 ))
1363 },
1364 (&Rotate::Rotate3D(..), _) | (_, &Rotate::Rotate3D(..)) => {
1365 let (from, to) = (self.resolve(), other.resolve());
1368 let (fx, fy, fz, fa) =
1371 transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
1372 let (tx, ty, tz, ta) =
1373 transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
1374
1375 let fv = DirectionVector::new(fx, fy, fz);
1382 let tv = DirectionVector::new(tx, ty, tz);
1383 if fa.is_zero() || ta.is_zero() || fv.approx_eq(&tv) {
1384 let (x, y, z) = if fa.is_zero() && ta.is_zero() {
1385 (0., 0., 1.)
1386 } else if fa.is_zero() {
1387 (tx, ty, tz)
1388 } else {
1389 (fx, fy, fz)
1391 };
1392 return Ok(Rotate::Rotate3D(x, y, z, fa.animate(&ta, procedure)?));
1393 }
1394
1395 let rq = if procedure == Procedure::Add {
1405 let f = ComputedTransformOperation::Rotate3D(fx, fy, fz, fa);
1410 let t = ComputedTransformOperation::Rotate3D(tx, ty, tz, ta);
1411 let v =
1412 Transform(vec![f].into()).animate(&Transform(vec![t].into()), procedure)?;
1413 let (m, _) = v.to_transform_3d_matrix(None)?;
1414 decompose_3d_matrix(Matrix3D::from(m))?.quaternion
1416 } else {
1417 let fq = Quaternion::from_direction_and_angle(&fv, fa.radians64());
1430 let tq = Quaternion::from_direction_and_angle(&tv, ta.radians64());
1431 Quaternion::animate(&fq, &tq, procedure)?
1432 };
1433
1434 let (x, y, z, angle) = transform::get_normalized_vector_and_angle(
1435 rq.0 as f32,
1436 rq.1 as f32,
1437 rq.2 as f32,
1438 rq.3.clamp(-1.0, 1.0).acos() as f32 * 2.0,
1441 );
1442
1443 Ok(Rotate::Rotate3D(x, y, z, Angle::from_radians(angle)))
1444 },
1445 (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => {
1446 let (from, to) = (self.resolve().3, other.resolve().3);
1448 Ok(Rotate::Rotate(from.animate(&to, procedure)?))
1449 },
1450 }
1451 }
1452}
1453
1454impl ComputeSquaredDistance for ComputedRotate {
1455 #[inline]
1456 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1457 use euclid::approxeq::ApproxEq;
1458 match (self, other) {
1459 (&Rotate::None, &Rotate::None) => Ok(SquaredDistance::from_sqrt(0.)),
1460 (&Rotate::Rotate3D(_, _, _, a), &Rotate::None)
1461 | (&Rotate::None, &Rotate::Rotate3D(_, _, _, a)) => {
1462 a.compute_squared_distance(&Angle::zero())
1463 },
1464 (&Rotate::Rotate3D(..), _) | (_, &Rotate::Rotate3D(..)) => {
1465 let (from, to) = (self.resolve(), other.resolve());
1466 let (mut fx, mut fy, mut fz, angle1) =
1467 transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
1468 let (mut tx, mut ty, mut tz, angle2) =
1469 transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
1470
1471 if angle1.is_zero() && angle2.is_zero() {
1472 (fx, fy, fz) = (0., 0., 1.);
1473 (tx, ty, tz) = (0., 0., 1.);
1474 } else if angle1.is_zero() {
1475 (fx, fy, fz) = (tx, ty, tz);
1476 } else if angle2.is_zero() {
1477 (tx, ty, tz) = (fx, fy, fz);
1478 }
1479
1480 let v1 = DirectionVector::new(fx, fy, fz);
1481 let v2 = DirectionVector::new(tx, ty, tz);
1482 if v1.approx_eq(&v2) {
1483 angle1.compute_squared_distance(&angle2)
1484 } else {
1485 let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64());
1486 let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64());
1487 q1.compute_squared_distance(&q2)
1488 }
1489 },
1490 (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => self
1491 .resolve()
1492 .3
1493 .compute_squared_distance(&other.resolve().3),
1494 }
1495 }
1496}
1497
1498impl ComputedTranslate {
1500 fn resolve(&self) -> (LengthPercentage, LengthPercentage, Length) {
1501 match *self {
1506 Translate::None => (
1507 LengthPercentage::zero(),
1508 LengthPercentage::zero(),
1509 Length::zero(),
1510 ),
1511 Translate::Translate(ref tx, ref ty, ref tz) => (tx.clone(), ty.clone(), *tz),
1512 }
1513 }
1514}
1515
1516impl Animate for ComputedTranslate {
1517 #[inline]
1518 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1519 match (self, other) {
1520 (&Translate::None, &Translate::None) => Ok(Translate::None),
1521 (&Translate::Translate(..), _) | (_, &Translate::Translate(..)) => {
1522 let (from, to) = (self.resolve(), other.resolve());
1523 Ok(Translate::Translate(
1524 from.0.animate(&to.0, procedure)?,
1525 from.1.animate(&to.1, procedure)?,
1526 from.2.animate(&to.2, procedure)?,
1527 ))
1528 },
1529 }
1530 }
1531}
1532
1533impl ComputeSquaredDistance for ComputedTranslate {
1534 #[inline]
1535 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1536 let (from, to) = (self.resolve(), other.resolve());
1537 Ok(from.0.compute_squared_distance(&to.0)?
1538 + from.1.compute_squared_distance(&to.1)?
1539 + from.2.compute_squared_distance(&to.2)?)
1540 }
1541}
1542
1543impl ComputedScale {
1545 fn resolve(&self) -> (Number, Number, Number) {
1546 match *self {
1551 Scale::None => (1.0, 1.0, 1.0),
1552 Scale::Scale(sx, sy, sz) => (sx, sy, sz),
1553 }
1554 }
1555}
1556
1557impl Animate for ComputedScale {
1558 #[inline]
1559 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1560 match (self, other) {
1561 (&Scale::None, &Scale::None) => Ok(Scale::None),
1562 (&Scale::Scale(..), _) | (_, &Scale::Scale(..)) => {
1563 let (from, to) = (self.resolve(), other.resolve());
1564 if procedure == Procedure::Add {
1569 return Ok(Scale::Scale(from.0 * to.0, from.1 * to.1, from.2 * to.2));
1571 }
1572 Ok(Scale::Scale(
1573 animate_multiplicative_factor(from.0, to.0, procedure)?,
1574 animate_multiplicative_factor(from.1, to.1, procedure)?,
1575 animate_multiplicative_factor(from.2, to.2, procedure)?,
1576 ))
1577 },
1578 }
1579 }
1580}
1581
1582impl ComputeSquaredDistance for ComputedScale {
1583 #[inline]
1584 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1585 let (from, to) = (self.resolve(), other.resolve());
1586 Ok(from.0.compute_squared_distance(&to.0)?
1587 + from.1.compute_squared_distance(&to.1)?
1588 + from.2.compute_squared_distance(&to.2)?)
1589 }
1590}