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: translate,
207 scale: scale,
208 angle: 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(ref this), &TransformOperation::Matrix3D(ref other)) => {
1053 Ok(TransformOperation::Matrix3D(
1054 this.animate(other, procedure)?,
1055 ))
1056 },
1057 (&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => {
1058 Ok(TransformOperation::Matrix(this.animate(other, procedure)?))
1059 },
1060 (
1061 &TransformOperation::Skew(ref fx, ref fy),
1062 &TransformOperation::Skew(ref tx, ref ty),
1063 ) => Ok(TransformOperation::Skew(
1064 fx.animate(tx, procedure)?,
1065 fy.animate(ty, procedure)?,
1066 )),
1067 (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t)) => {
1068 Ok(TransformOperation::SkewX(f.animate(t, procedure)?))
1069 },
1070 (&TransformOperation::SkewY(ref f), &TransformOperation::SkewY(ref t)) => {
1071 Ok(TransformOperation::SkewY(f.animate(t, procedure)?))
1072 },
1073 (
1074 &TransformOperation::Translate3D(ref fx, ref fy, ref fz),
1075 &TransformOperation::Translate3D(ref tx, ref ty, ref tz),
1076 ) => Ok(TransformOperation::Translate3D(
1077 fx.animate(tx, procedure)?,
1078 fy.animate(ty, procedure)?,
1079 fz.animate(tz, procedure)?,
1080 )),
1081 (
1082 &TransformOperation::Translate(ref fx, ref fy),
1083 &TransformOperation::Translate(ref tx, ref ty),
1084 ) => Ok(TransformOperation::Translate(
1085 fx.animate(tx, procedure)?,
1086 fy.animate(ty, procedure)?,
1087 )),
1088 (&TransformOperation::TranslateX(ref f), &TransformOperation::TranslateX(ref t)) => {
1089 Ok(TransformOperation::TranslateX(f.animate(t, procedure)?))
1090 },
1091 (&TransformOperation::TranslateY(ref f), &TransformOperation::TranslateY(ref t)) => {
1092 Ok(TransformOperation::TranslateY(f.animate(t, procedure)?))
1093 },
1094 (&TransformOperation::TranslateZ(ref f), &TransformOperation::TranslateZ(ref t)) => {
1095 Ok(TransformOperation::TranslateZ(f.animate(t, procedure)?))
1096 },
1097 (
1098 &TransformOperation::Scale3D(ref fx, ref fy, ref fz),
1099 &TransformOperation::Scale3D(ref tx, ref ty, ref tz),
1100 ) => Ok(TransformOperation::Scale3D(
1101 animate_multiplicative_factor(*fx, *tx, procedure)?,
1102 animate_multiplicative_factor(*fy, *ty, procedure)?,
1103 animate_multiplicative_factor(*fz, *tz, procedure)?,
1104 )),
1105 (&TransformOperation::ScaleX(ref f), &TransformOperation::ScaleX(ref t)) => Ok(
1106 TransformOperation::ScaleX(animate_multiplicative_factor(*f, *t, procedure)?),
1107 ),
1108 (&TransformOperation::ScaleY(ref f), &TransformOperation::ScaleY(ref t)) => Ok(
1109 TransformOperation::ScaleY(animate_multiplicative_factor(*f, *t, procedure)?),
1110 ),
1111 (&TransformOperation::ScaleZ(ref f), &TransformOperation::ScaleZ(ref t)) => Ok(
1112 TransformOperation::ScaleZ(animate_multiplicative_factor(*f, *t, procedure)?),
1113 ),
1114 (
1115 &TransformOperation::Scale(ref fx, ref fy),
1116 &TransformOperation::Scale(ref tx, ref ty),
1117 ) => Ok(TransformOperation::Scale(
1118 animate_multiplicative_factor(*fx, *tx, procedure)?,
1119 animate_multiplicative_factor(*fy, *ty, procedure)?,
1120 )),
1121 (
1122 &TransformOperation::Rotate3D(fx, fy, fz, fa),
1123 &TransformOperation::Rotate3D(tx, ty, tz, ta),
1124 ) => {
1125 let animated = Rotate::Rotate3D(fx, fy, fz, fa)
1126 .animate(&Rotate::Rotate3D(tx, ty, tz, ta), procedure)?;
1127 let (fx, fy, fz, fa) = ComputedRotate::resolve(&animated);
1128 Ok(TransformOperation::Rotate3D(fx, fy, fz, fa))
1129 },
1130 (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta)) => {
1131 Ok(TransformOperation::RotateX(fa.animate(&ta, procedure)?))
1132 },
1133 (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta)) => {
1134 Ok(TransformOperation::RotateY(fa.animate(&ta, procedure)?))
1135 },
1136 (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta)) => {
1137 Ok(TransformOperation::RotateZ(fa.animate(&ta, procedure)?))
1138 },
1139 (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {
1140 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1141 },
1142 (&TransformOperation::Rotate(fa), &TransformOperation::RotateZ(ta)) => {
1143 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1144 },
1145 (&TransformOperation::RotateZ(fa), &TransformOperation::Rotate(ta)) => {
1146 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1147 },
1148 (
1149 &TransformOperation::Perspective(ref fd),
1150 &TransformOperation::Perspective(ref td),
1151 ) => {
1152 use crate::values::computed::CSSPixelLength;
1153 use crate::values::generics::transform::create_perspective_matrix;
1154
1155 let from = create_perspective_matrix(fd.infinity_or(|l| l.px()));
1163 let to = create_perspective_matrix(td.infinity_or(|l| l.px()));
1164
1165 let interpolated = Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?;
1166
1167 let decomposed = decompose_3d_matrix(interpolated)?;
1168 let perspective_z = decomposed.perspective.2;
1169 let used_value = if perspective_z >= 0. {
1172 transform::PerspectiveFunction::None
1173 } else {
1174 transform::PerspectiveFunction::Length(CSSPixelLength::new(
1175 if perspective_z <= -1. {
1176 1.
1177 } else {
1178 -1. / perspective_z
1179 },
1180 ))
1181 };
1182 Ok(TransformOperation::Perspective(used_value))
1183 },
1184 _ if self.is_translate() && other.is_translate() => self
1185 .to_translate_3d()
1186 .animate(&other.to_translate_3d(), procedure),
1187 _ if self.is_scale() && other.is_scale() => {
1188 self.to_scale_3d().animate(&other.to_scale_3d(), procedure)
1189 },
1190 _ if self.is_rotate() && other.is_rotate() => self
1191 .to_rotate_3d()
1192 .animate(&other.to_rotate_3d(), procedure),
1193 _ => Err(()),
1194 }
1195 }
1196}
1197
1198impl ComputedTransformOperation {
1199 fn try_animate_mismatched_transforms_in_place(
1202 left: &[Self],
1203 right: &[Self],
1204 procedure: Procedure,
1205 ) -> Result<Self, ()> {
1206 let (left, _left_3d) = Transform::components_to_transform_3d_matrix(left, None)?;
1207 let (right, _right_3d) = Transform::components_to_transform_3d_matrix(right, None)?;
1208 Ok(Self::Matrix3D(
1209 Matrix3D::from(left).animate(&Matrix3D::from(right), procedure)?,
1210 ))
1211 }
1212
1213 fn animate_mismatched_transforms(
1214 left: &[Self],
1215 right: &[Self],
1216 procedure: Procedure,
1217 ) -> Result<Self, ()> {
1218 if let Ok(op) = Self::try_animate_mismatched_transforms_in_place(left, right, procedure) {
1219 return Ok(op);
1220 }
1221 let from_list = Transform(left.to_vec().into());
1222 let to_list = Transform(right.to_vec().into());
1223 Ok(match procedure {
1224 Procedure::Add => {
1225 debug_assert!(false, "Addition should've been handled earlier");
1226 return Err(());
1227 },
1228 Procedure::Interpolate { progress } => Self::InterpolateMatrix {
1229 from_list,
1230 to_list,
1231 progress: Percentage(progress as f32),
1232 },
1233 Procedure::Accumulate { count } => Self::AccumulateMatrix {
1234 from_list,
1235 to_list,
1236 count: cmp::min(count, i32::max_value() as u64) as i32,
1237 },
1238 })
1239 }
1240}
1241
1242impl ComputeSquaredDistance for ComputedTransformOperation {
1247 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1248 match (self, other) {
1249 (&TransformOperation::Matrix3D(ref this), &TransformOperation::Matrix3D(ref other)) => {
1250 this.compute_squared_distance(other)
1251 },
1252 (&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => {
1253 let this: Matrix3D = (*this).into();
1254 let other: Matrix3D = (*other).into();
1255 this.compute_squared_distance(&other)
1256 },
1257 (
1258 &TransformOperation::Skew(ref fx, ref fy),
1259 &TransformOperation::Skew(ref tx, ref ty),
1260 ) => Ok(fx.compute_squared_distance(&tx)? + fy.compute_squared_distance(&ty)?),
1261 (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t))
1262 | (&TransformOperation::SkewY(ref f), &TransformOperation::SkewY(ref t)) => {
1263 f.compute_squared_distance(&t)
1264 },
1265 (
1266 &TransformOperation::Translate3D(ref fx, ref fy, ref fz),
1267 &TransformOperation::Translate3D(ref tx, ref ty, ref tz),
1268 ) => {
1269 let basis = Length::new(0.);
1276 let fx = fx.resolve(basis).px();
1277 let fy = fy.resolve(basis).px();
1278 let tx = tx.resolve(basis).px();
1279 let ty = ty.resolve(basis).px();
1280
1281 Ok(fx.compute_squared_distance(&tx)?
1282 + fy.compute_squared_distance(&ty)?
1283 + fz.compute_squared_distance(&tz)?)
1284 },
1285 (
1286 &TransformOperation::Scale3D(ref fx, ref fy, ref fz),
1287 &TransformOperation::Scale3D(ref tx, ref ty, ref tz),
1288 ) => Ok(fx.compute_squared_distance(&tx)?
1289 + fy.compute_squared_distance(&ty)?
1290 + fz.compute_squared_distance(&tz)?),
1291 (
1292 &TransformOperation::Rotate3D(fx, fy, fz, fa),
1293 &TransformOperation::Rotate3D(tx, ty, tz, ta),
1294 ) => Rotate::Rotate3D(fx, fy, fz, fa)
1295 .compute_squared_distance(&Rotate::Rotate3D(tx, ty, tz, ta)),
1296 (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta))
1297 | (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta))
1298 | (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta))
1299 | (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {
1300 fa.compute_squared_distance(&ta)
1301 },
1302 (
1303 &TransformOperation::Perspective(ref fd),
1304 &TransformOperation::Perspective(ref td),
1305 ) => fd
1306 .infinity_or(|l| l.px())
1307 .compute_squared_distance(&td.infinity_or(|l| l.px())),
1308 (&TransformOperation::Perspective(ref p), &TransformOperation::Matrix3D(ref m))
1309 | (&TransformOperation::Matrix3D(ref m), &TransformOperation::Perspective(ref p)) => {
1310 let mut p_matrix = Matrix3D::identity();
1313 let p = p.infinity_or(|p| p.px());
1314 if p >= 0. {
1315 p_matrix.m34 = -1. / p.max(1.);
1316 }
1317 p_matrix.compute_squared_distance(&m)
1318 },
1319 _ if self.is_translate() && other.is_translate() => self
1323 .to_translate_3d()
1324 .compute_squared_distance(&other.to_translate_3d()),
1325 _ if self.is_scale() && other.is_scale() => self
1326 .to_scale_3d()
1327 .compute_squared_distance(&other.to_scale_3d()),
1328 _ if self.is_rotate() && other.is_rotate() => self
1329 .to_rotate_3d()
1330 .compute_squared_distance(&other.to_rotate_3d()),
1331 _ => Err(()),
1332 }
1333 }
1334}
1335
1336impl ComputedRotate {
1341 fn resolve(&self) -> (Number, Number, Number, Angle) {
1342 match *self {
1347 Rotate::None => (0., 0., 1., Angle::zero()),
1348 Rotate::Rotate3D(rx, ry, rz, angle) => (rx, ry, rz, angle),
1349 Rotate::Rotate(angle) => (0., 0., 1., angle),
1350 }
1351 }
1352}
1353
1354impl Animate for ComputedRotate {
1355 #[inline]
1356 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1357 use euclid::approxeq::ApproxEq;
1358 match (self, other) {
1359 (&Rotate::None, &Rotate::None) => Ok(Rotate::None),
1360 (&Rotate::Rotate3D(fx, fy, fz, fa), &Rotate::None) => {
1361 let (fx, fy, fz, fa) = transform::get_normalized_vector_and_angle(fx, fy, fz, fa);
1366 Ok(Rotate::Rotate3D(
1367 fx,
1368 fy,
1369 fz,
1370 fa.animate(&Angle::zero(), procedure)?,
1371 ))
1372 },
1373 (&Rotate::None, &Rotate::Rotate3D(tx, ty, tz, ta)) => {
1374 let (tx, ty, tz, ta) = transform::get_normalized_vector_and_angle(tx, ty, tz, ta);
1376 Ok(Rotate::Rotate3D(
1377 tx,
1378 ty,
1379 tz,
1380 Angle::zero().animate(&ta, procedure)?,
1381 ))
1382 },
1383 (&Rotate::Rotate3D(_, ..), _) | (_, &Rotate::Rotate3D(_, ..)) => {
1384 let (from, to) = (self.resolve(), other.resolve());
1387 let (fx, fy, fz, fa) =
1390 transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
1391 let (tx, ty, tz, ta) =
1392 transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
1393
1394 let fv = DirectionVector::new(fx, fy, fz);
1401 let tv = DirectionVector::new(tx, ty, tz);
1402 if fa.is_zero() || ta.is_zero() || fv.approx_eq(&tv) {
1403 let (x, y, z) = if fa.is_zero() && ta.is_zero() {
1404 (0., 0., 1.)
1405 } else if fa.is_zero() {
1406 (tx, ty, tz)
1407 } else {
1408 (fx, fy, fz)
1410 };
1411 return Ok(Rotate::Rotate3D(x, y, z, fa.animate(&ta, procedure)?));
1412 }
1413
1414 let rq = if procedure == Procedure::Add {
1424 let f = ComputedTransformOperation::Rotate3D(fx, fy, fz, fa);
1429 let t = ComputedTransformOperation::Rotate3D(tx, ty, tz, ta);
1430 let v =
1431 Transform(vec![f].into()).animate(&Transform(vec![t].into()), procedure)?;
1432 let (m, _) = v.to_transform_3d_matrix(None)?;
1433 decompose_3d_matrix(Matrix3D::from(m))?.quaternion
1435 } else {
1436 let fq = Quaternion::from_direction_and_angle(&fv, fa.radians64());
1449 let tq = Quaternion::from_direction_and_angle(&tv, ta.radians64());
1450 Quaternion::animate(&fq, &tq, procedure)?
1451 };
1452
1453 let (x, y, z, angle) = transform::get_normalized_vector_and_angle(
1454 rq.0 as f32,
1455 rq.1 as f32,
1456 rq.2 as f32,
1457 rq.3.clamp(-1.0, 1.0).acos() as f32 * 2.0,
1460 );
1461
1462 Ok(Rotate::Rotate3D(x, y, z, Angle::from_radians(angle)))
1463 },
1464 (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => {
1465 let (from, to) = (self.resolve().3, other.resolve().3);
1467 Ok(Rotate::Rotate(from.animate(&to, procedure)?))
1468 },
1469 }
1470 }
1471}
1472
1473impl ComputeSquaredDistance for ComputedRotate {
1474 #[inline]
1475 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1476 use euclid::approxeq::ApproxEq;
1477 match (self, other) {
1478 (&Rotate::None, &Rotate::None) => Ok(SquaredDistance::from_sqrt(0.)),
1479 (&Rotate::Rotate3D(_, _, _, a), &Rotate::None)
1480 | (&Rotate::None, &Rotate::Rotate3D(_, _, _, a)) => {
1481 a.compute_squared_distance(&Angle::zero())
1482 },
1483 (&Rotate::Rotate3D(_, ..), _) | (_, &Rotate::Rotate3D(_, ..)) => {
1484 let (from, to) = (self.resolve(), other.resolve());
1485 let (mut fx, mut fy, mut fz, angle1) =
1486 transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
1487 let (mut tx, mut ty, mut tz, angle2) =
1488 transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
1489
1490 if angle1.is_zero() && angle2.is_zero() {
1491 (fx, fy, fz) = (0., 0., 1.);
1492 (tx, ty, tz) = (0., 0., 1.);
1493 } else if angle1.is_zero() {
1494 (fx, fy, fz) = (tx, ty, tz);
1495 } else if angle2.is_zero() {
1496 (tx, ty, tz) = (fx, fy, fz);
1497 }
1498
1499 let v1 = DirectionVector::new(fx, fy, fz);
1500 let v2 = DirectionVector::new(tx, ty, tz);
1501 if v1.approx_eq(&v2) {
1502 angle1.compute_squared_distance(&angle2)
1503 } else {
1504 let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64());
1505 let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64());
1506 q1.compute_squared_distance(&q2)
1507 }
1508 },
1509 (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => self
1510 .resolve()
1511 .3
1512 .compute_squared_distance(&other.resolve().3),
1513 }
1514 }
1515}
1516
1517impl ComputedTranslate {
1519 fn resolve(&self) -> (LengthPercentage, LengthPercentage, Length) {
1520 match *self {
1525 Translate::None => (
1526 LengthPercentage::zero(),
1527 LengthPercentage::zero(),
1528 Length::zero(),
1529 ),
1530 Translate::Translate(ref tx, ref ty, ref tz) => (tx.clone(), ty.clone(), tz.clone()),
1531 }
1532 }
1533}
1534
1535impl Animate for ComputedTranslate {
1536 #[inline]
1537 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1538 match (self, other) {
1539 (&Translate::None, &Translate::None) => Ok(Translate::None),
1540 (&Translate::Translate(_, ..), _) | (_, &Translate::Translate(_, ..)) => {
1541 let (from, to) = (self.resolve(), other.resolve());
1542 Ok(Translate::Translate(
1543 from.0.animate(&to.0, procedure)?,
1544 from.1.animate(&to.1, procedure)?,
1545 from.2.animate(&to.2, procedure)?,
1546 ))
1547 },
1548 }
1549 }
1550}
1551
1552impl ComputeSquaredDistance for ComputedTranslate {
1553 #[inline]
1554 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1555 let (from, to) = (self.resolve(), other.resolve());
1556 Ok(from.0.compute_squared_distance(&to.0)?
1557 + from.1.compute_squared_distance(&to.1)?
1558 + from.2.compute_squared_distance(&to.2)?)
1559 }
1560}
1561
1562impl ComputedScale {
1564 fn resolve(&self) -> (Number, Number, Number) {
1565 match *self {
1570 Scale::None => (1.0, 1.0, 1.0),
1571 Scale::Scale(sx, sy, sz) => (sx, sy, sz),
1572 }
1573 }
1574}
1575
1576impl Animate for ComputedScale {
1577 #[inline]
1578 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1579 match (self, other) {
1580 (&Scale::None, &Scale::None) => Ok(Scale::None),
1581 (&Scale::Scale(_, ..), _) | (_, &Scale::Scale(_, ..)) => {
1582 let (from, to) = (self.resolve(), other.resolve());
1583 if procedure == Procedure::Add {
1588 return Ok(Scale::Scale(from.0 * to.0, from.1 * to.1, from.2 * to.2));
1590 }
1591 Ok(Scale::Scale(
1592 animate_multiplicative_factor(from.0, to.0, procedure)?,
1593 animate_multiplicative_factor(from.1, to.1, procedure)?,
1594 animate_multiplicative_factor(from.2, to.2, procedure)?,
1595 ))
1596 },
1597 }
1598 }
1599}
1600
1601impl ComputeSquaredDistance for ComputedScale {
1602 #[inline]
1603 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1604 let (from, to) = (self.resolve(), other.resolve());
1605 Ok(from.0.compute_squared_distance(&to.0)?
1606 + from.1.compute_squared_distance(&to.1)?
1607 + from.2.compute_squared_distance(&to.2)?)
1608 }
1609}