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 #[cfg(feature = "servo")]
253 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
254 let this = Matrix3D::from(*self);
255 let other = Matrix3D::from(*other);
256 let this = MatrixDecomposed2D::from(this);
257 let other = MatrixDecomposed2D::from(other);
258 Matrix3D::from(this.animate(&other, procedure)?).into_2d()
259 }
260
261 #[cfg(feature = "gecko")]
262 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
265 let this = Matrix3D::from(*self);
266 let other = Matrix3D::from(*other);
267 let from = decompose_2d_matrix(&this)?;
268 let to = decompose_2d_matrix(&other)?;
269 Matrix3D::from(from.animate(&to, procedure)?).into_2d()
270 }
271}
272
273#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
275pub struct Translate3D(pub f32, pub f32, pub f32);
276
277#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
279pub struct Scale3D(pub f32, pub f32, pub f32);
280
281impl Scale3D {
282 fn negate(&mut self) {
284 self.0 *= -1.0;
285 self.1 *= -1.0;
286 self.2 *= -1.0;
287 }
288}
289
290impl Animate for Scale3D {
291 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
292 Ok(Scale3D(
293 animate_multiplicative_factor(self.0, other.0, procedure)?,
294 animate_multiplicative_factor(self.1, other.1, procedure)?,
295 animate_multiplicative_factor(self.2, other.2, procedure)?,
296 ))
297 }
298}
299
300#[derive(Animate, Clone, Copy, Debug, MallocSizeOf)]
302pub struct Skew(f32, f32, f32);
303
304impl ComputeSquaredDistance for Skew {
305 #[inline]
308 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
309 Ok(self.0.atan().compute_squared_distance(&other.0.atan())?
310 + self.1.atan().compute_squared_distance(&other.1.atan())?
311 + self.2.atan().compute_squared_distance(&other.2.atan())?)
312 }
313}
314
315#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
317pub struct Perspective(pub f32, pub f32, pub f32, pub f32);
318
319impl Animate for Perspective {
320 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
321 Ok(Perspective(
322 self.0.animate(&other.0, procedure)?,
323 self.1.animate(&other.1, procedure)?,
324 self.2.animate(&other.2, procedure)?,
325 animate_multiplicative_factor(self.3, other.3, procedure)?,
326 ))
327 }
328}
329
330#[derive(Clone, Copy, Debug, MallocSizeOf)]
332pub struct Quaternion(f64, f64, f64, f64);
333
334impl Quaternion {
335 #[inline]
337 fn from_direction_and_angle(vector: &DirectionVector, angle: f64) -> Self {
338 debug_assert!(
339 (vector.length() - 1.).abs() < 0.0001,
340 "Only accept an unit direction vector to create a quaternion"
341 );
342
343 let half_angle = angle
348 .abs()
349 .rem_euclid(std::f64::consts::TAU)
350 .copysign(angle)
351 / 2.;
352
353 Quaternion(
363 vector.x as f64 * half_angle.sin(),
364 vector.y as f64 * half_angle.sin(),
365 vector.z as f64 * half_angle.sin(),
366 half_angle.cos(),
367 )
368 }
369
370 #[inline]
372 fn dot(&self, other: &Self) -> f64 {
373 self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3
374 }
375
376 #[inline]
378 fn scale(&self, factor: f64) -> Self {
379 Quaternion(
380 self.0 * factor,
381 self.1 * factor,
382 self.2 * factor,
383 self.3 * factor,
384 )
385 }
386}
387
388impl Add for Quaternion {
389 type Output = Self;
390
391 fn add(self, other: Self) -> Self {
392 Self(
393 self.0 + other.0,
394 self.1 + other.1,
395 self.2 + other.2,
396 self.3 + other.3,
397 )
398 }
399}
400
401impl Animate for Quaternion {
402 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
403 let (this_weight, other_weight) = procedure.weights();
404 debug_assert!(
405 (this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON * 2.0
408 || other_weight == 1.0f64
409 || other_weight == 0.0f64,
410 "animate should only be used for interpolating or accumulating transforms"
411 );
412
413 if let Procedure::Accumulate { .. } = procedure {
416 debug_assert_eq!(other_weight, 1.0);
417 if this_weight == 0.0 {
418 return Ok(*other);
419 }
420
421 let clamped_w = self.3.min(1.0).max(-1.0);
422
423 let mut theta = clamped_w.acos();
425 let mut scale = if theta == 0.0 { 0.0 } else { 1.0 / theta.sin() };
426 theta *= this_weight;
427 scale *= theta.sin();
428
429 let mut scaled_self = *self;
431 scaled_self.0 *= scale;
432 scaled_self.1 *= scale;
433 scaled_self.2 *= scale;
434 scaled_self.3 = theta.cos();
435
436 let a = &scaled_self;
438 let b = other;
439 return Ok(Quaternion(
440 a.3 * b.0 + a.0 * b.3 + a.1 * b.2 - a.2 * b.1,
441 a.3 * b.1 - a.0 * b.2 + a.1 * b.3 + a.2 * b.0,
442 a.3 * b.2 + a.0 * b.1 - a.1 * b.0 + a.2 * b.3,
443 a.3 * b.3 - a.0 * b.0 - a.1 * b.1 - a.2 * b.2,
444 ));
445 }
446
447 let cos_half_theta =
451 (self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3)
452 .min(1.0)
453 .max(-1.0);
454
455 if cos_half_theta.abs() == 1.0 {
456 return Ok(*self);
457 }
458
459 let half_theta = cos_half_theta.acos();
460 let sin_half_theta = (1.0 - cos_half_theta * cos_half_theta).sqrt();
461
462 let right_weight = (other_weight * half_theta).sin() / sin_half_theta;
463 let left_weight = (this_weight * half_theta).sin() / sin_half_theta;
476
477 Ok(self.scale(left_weight) + other.scale(right_weight))
478 }
479}
480
481impl ComputeSquaredDistance for Quaternion {
482 #[inline]
483 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
484 let distance = self.dot(other).max(-1.0).min(1.0).acos() * 2.0;
488 Ok(SquaredDistance::from_sqrt(distance))
489 }
490}
491
492#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
494pub struct MatrixDecomposed3D {
495 pub translate: Translate3D,
497 pub scale: Scale3D,
499 pub skew: Skew,
501 pub perspective: Perspective,
503 pub quaternion: Quaternion,
505}
506
507impl From<MatrixDecomposed3D> for Matrix3D {
508 fn from(decomposed: MatrixDecomposed3D) -> Matrix3D {
511 let mut matrix = Matrix3D::identity();
512
513 matrix.set_perspective(&decomposed.perspective);
515
516 matrix.apply_translate(&decomposed.translate);
518
519 {
521 let x = decomposed.quaternion.0;
522 let y = decomposed.quaternion.1;
523 let z = decomposed.quaternion.2;
524 let w = decomposed.quaternion.3;
525
526 let mut rotation_matrix = Matrix3D::identity();
529 rotation_matrix.m11 = 1.0 - 2.0 * (y * y + z * z) as f32;
530 rotation_matrix.m12 = 2.0 * (x * y + z * w) as f32;
531 rotation_matrix.m13 = 2.0 * (x * z - y * w) as f32;
532 rotation_matrix.m21 = 2.0 * (x * y - z * w) as f32;
533 rotation_matrix.m22 = 1.0 - 2.0 * (x * x + z * z) as f32;
534 rotation_matrix.m23 = 2.0 * (y * z + x * w) as f32;
535 rotation_matrix.m31 = 2.0 * (x * z + y * w) as f32;
536 rotation_matrix.m32 = 2.0 * (y * z - x * w) as f32;
537 rotation_matrix.m33 = 1.0 - 2.0 * (x * x + y * y) as f32;
538
539 matrix = rotation_matrix.multiply(&matrix);
540 }
541
542 {
544 let mut temp = Matrix3D::identity();
545 if decomposed.skew.2 != 0.0 {
546 temp.m32 = decomposed.skew.2;
547 matrix = temp.multiply(&matrix);
548 temp.m32 = 0.0;
549 }
550
551 if decomposed.skew.1 != 0.0 {
552 temp.m31 = decomposed.skew.1;
553 matrix = temp.multiply(&matrix);
554 temp.m31 = 0.0;
555 }
556
557 if decomposed.skew.0 != 0.0 {
558 temp.m21 = decomposed.skew.0;
559 matrix = temp.multiply(&matrix);
560 }
561 }
562
563 matrix.apply_scale(&decomposed.scale);
565
566 matrix
567 }
568}
569
570fn decompose_3d_matrix(mut matrix: Matrix3D) -> Result<MatrixDecomposed3D, ()> {
574 let combine = |a: [f32; 3], b: [f32; 3], ascl: f32, bscl: f32| {
576 [
577 (ascl * a[0]) + (bscl * b[0]),
578 (ascl * a[1]) + (bscl * b[1]),
579 (ascl * a[2]) + (bscl * b[2]),
580 ]
581 };
582 let dot = |a: [f32; 3], b: [f32; 3]| a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
584 let cross = |row1: [f32; 3], row2: [f32; 3]| {
586 [
587 row1[1] * row2[2] - row1[2] * row2[1],
588 row1[2] * row2[0] - row1[0] * row2[2],
589 row1[0] * row2[1] - row1[1] * row2[0],
590 ]
591 };
592
593 if matrix.m44 == 0.0 {
594 return Err(());
595 }
596
597 let scaling_factor = matrix.m44;
598
599 matrix.scale_by_factor(1.0 / scaling_factor);
601
602 let mut perspective_matrix = matrix;
605
606 perspective_matrix.m14 = 0.0;
607 perspective_matrix.m24 = 0.0;
608 perspective_matrix.m34 = 0.0;
609 perspective_matrix.m44 = 1.0;
610
611 if perspective_matrix.determinant() == 0.0 {
612 return Err(());
613 }
614
615 let perspective = if matrix.m14 != 0.0 || matrix.m24 != 0.0 || matrix.m34 != 0.0 {
617 let right_hand_side: [f32; 4] = [matrix.m14, matrix.m24, matrix.m34, matrix.m44];
618
619 perspective_matrix = perspective_matrix.inverse().unwrap().transpose();
620 let perspective = perspective_matrix.pre_mul_point4(&right_hand_side);
621 Perspective(
625 perspective[0],
626 perspective[1],
627 perspective[2],
628 perspective[3],
629 )
630 } else {
631 Perspective(0.0, 0.0, 0.0, 1.0)
632 };
633
634 let translate = Translate3D(matrix.m41, matrix.m42, matrix.m43);
636
637 let mut row = matrix.get_matrix_3x3_part();
639
640 let row0len = (row[0][0] * row[0][0] + row[0][1] * row[0][1] + row[0][2] * row[0][2]).sqrt();
642 let mut scale = Scale3D(row0len, 0.0, 0.0);
643 row[0] = [
644 row[0][0] / row0len,
645 row[0][1] / row0len,
646 row[0][2] / row0len,
647 ];
648
649 let mut skew = Skew(dot(row[0], row[1]), 0.0, 0.0);
651 row[1] = combine(row[1], row[0], 1.0, -skew.0);
652
653 let row1len = (row[1][0] * row[1][0] + row[1][1] * row[1][1] + row[1][2] * row[1][2]).sqrt();
655 scale.1 = row1len;
656 row[1] = [
657 row[1][0] / row1len,
658 row[1][1] / row1len,
659 row[1][2] / row1len,
660 ];
661 skew.0 /= scale.1;
662
663 skew.1 = dot(row[0], row[2]);
665 row[2] = combine(row[2], row[0], 1.0, -skew.1);
666 skew.2 = dot(row[1], row[2]);
667 row[2] = combine(row[2], row[1], 1.0, -skew.2);
668
669 let row2len = (row[2][0] * row[2][0] + row[2][1] * row[2][1] + row[2][2] * row[2][2]).sqrt();
671 scale.2 = row2len;
672 row[2] = [
673 row[2][0] / row2len,
674 row[2][1] / row2len,
675 row[2][2] / row2len,
676 ];
677 skew.1 /= scale.2;
678 skew.2 /= scale.2;
679
680 if dot(row[0], cross(row[1], row[2])) < 0.0 {
684 scale.negate();
685 for i in 0..3 {
686 row[i][0] *= -1.0;
687 row[i][1] *= -1.0;
688 row[i][2] *= -1.0;
689 }
690 }
691
692 let mut quaternion = Quaternion(
694 0.5 * ((1.0 + row[0][0] - row[1][1] - row[2][2]).max(0.0) as f64).sqrt(),
695 0.5 * ((1.0 - row[0][0] + row[1][1] - row[2][2]).max(0.0) as f64).sqrt(),
696 0.5 * ((1.0 - row[0][0] - row[1][1] + row[2][2]).max(0.0) as f64).sqrt(),
697 0.5 * ((1.0 + row[0][0] + row[1][1] + row[2][2]).max(0.0) as f64).sqrt(),
698 );
699
700 if row[2][1] > row[1][2] {
701 quaternion.0 = -quaternion.0
702 }
703 if row[0][2] > row[2][0] {
704 quaternion.1 = -quaternion.1
705 }
706 if row[1][0] > row[0][1] {
707 quaternion.2 = -quaternion.2
708 }
709
710 Ok(MatrixDecomposed3D {
711 translate,
712 scale,
713 skew,
714 perspective,
715 quaternion,
716 })
717}
718
719#[cfg(feature = "gecko")]
841fn decompose_2d_matrix(matrix: &Matrix3D) -> Result<MatrixDecomposed3D, ()> {
842 let (mut m11, mut m12) = (matrix.m11, matrix.m12);
848 let (mut m21, mut m22) = (matrix.m21, matrix.m22);
849 if m11 * m22 == m12 * m21 {
851 return Err(());
852 }
853
854 let mut scale_x = (m11 * m11 + m12 * m12).sqrt();
855 m11 /= scale_x;
856 m12 /= scale_x;
857
858 let mut shear_xy = m11 * m21 + m12 * m22;
859 m21 -= m11 * shear_xy;
860 m22 -= m12 * shear_xy;
861
862 let scale_y = (m21 * m21 + m22 * m22).sqrt();
863 m21 /= scale_y;
864 m22 /= scale_y;
865 shear_xy /= scale_y;
866
867 let determinant = m11 * m22 - m12 * m21;
868 if 0.99 > determinant.abs() || determinant.abs() > 1.01 {
870 return Err(());
871 }
872
873 if determinant < 0. {
874 m11 = -m11;
875 m12 = -m12;
876 shear_xy = -shear_xy;
877 scale_x = -scale_x;
878 }
879
880 Ok(MatrixDecomposed3D {
881 translate: Translate3D(matrix.m41, matrix.m42, 0.),
882 scale: Scale3D(scale_x, scale_y, 1.),
883 skew: Skew(shear_xy, 0., 0.),
884 perspective: Perspective(0., 0., 0., 1.),
885 quaternion: Quaternion::from_direction_and_angle(
886 &DirectionVector::new(0., 0., 1.),
887 m12.atan2(m11) as f64,
888 ),
889 })
890}
891
892impl Animate for Matrix3D {
893 #[cfg(feature = "servo")]
894 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
895 if self.is_3d() || other.is_3d() {
896 let decomposed_from = decompose_3d_matrix(*self);
897 let decomposed_to = decompose_3d_matrix(*other);
898 match (decomposed_from, decomposed_to) {
899 (Ok(this), Ok(other)) => Ok(Matrix3D::from(this.animate(&other, procedure)?)),
900 _ => Err(()),
904 }
905 } else {
906 let this = MatrixDecomposed2D::from(*self);
907 let other = MatrixDecomposed2D::from(*other);
908 Ok(Matrix3D::from(this.animate(&other, procedure)?))
909 }
910 }
911
912 #[cfg(feature = "gecko")]
913 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
916 let (from, to) = if self.is_3d() || other.is_3d() {
917 (decompose_3d_matrix(*self)?, decompose_3d_matrix(*other)?)
918 } else {
919 (decompose_2d_matrix(self)?, decompose_2d_matrix(other)?)
920 };
921 Ok(Matrix3D::from(from.animate(&to, procedure)?))
925 }
926}
927
928impl ComputeSquaredDistance for Matrix3D {
929 #[inline]
930 #[cfg(feature = "servo")]
931 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
932 if self.is_3d() || other.is_3d() {
933 let from = decompose_3d_matrix(*self)?;
934 let to = decompose_3d_matrix(*other)?;
935 from.compute_squared_distance(&to)
936 } else {
937 let from = MatrixDecomposed2D::from(*self);
938 let to = MatrixDecomposed2D::from(*other);
939 from.compute_squared_distance(&to)
940 }
941 }
942
943 #[inline]
944 #[cfg(feature = "gecko")]
945 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
946 let (from, to) = if self.is_3d() || other.is_3d() {
947 (decompose_3d_matrix(*self)?, decompose_3d_matrix(*other)?)
948 } else {
949 (decompose_2d_matrix(self)?, decompose_2d_matrix(other)?)
950 };
951 from.compute_squared_distance(&to)
952 }
953}
954
955fn is_matched_operation(
959 first: &ComputedTransformOperation,
960 second: &ComputedTransformOperation,
961) -> bool {
962 match (first, second) {
963 (&TransformOperation::Matrix(..), &TransformOperation::Matrix(..))
964 | (&TransformOperation::Matrix3D(..), &TransformOperation::Matrix3D(..))
965 | (&TransformOperation::Skew(..), &TransformOperation::Skew(..))
966 | (&TransformOperation::SkewX(..), &TransformOperation::SkewX(..))
967 | (&TransformOperation::SkewY(..), &TransformOperation::SkewY(..))
968 | (&TransformOperation::Rotate(..), &TransformOperation::Rotate(..))
969 | (&TransformOperation::Rotate3D(..), &TransformOperation::Rotate3D(..))
970 | (&TransformOperation::RotateX(..), &TransformOperation::RotateX(..))
971 | (&TransformOperation::RotateY(..), &TransformOperation::RotateY(..))
972 | (&TransformOperation::RotateZ(..), &TransformOperation::RotateZ(..))
973 | (&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => true,
974 (a, b) if a.is_translate() && b.is_translate() => true,
976 (a, b) if a.is_scale() && b.is_scale() => true,
977 (a, b) if a.is_rotate() && b.is_rotate() => true,
978 _ => false,
980 }
981}
982
983impl Animate for ComputedTransform {
985 #[inline]
986 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
987 use std::borrow::Cow;
988
989 if procedure == Procedure::Add {
994 let result = self.0.iter().chain(&*other.0).cloned().collect();
995 return Ok(Transform(result));
996 }
997
998 let this = Cow::Borrowed(&self.0);
999 let other = Cow::Borrowed(&other.0);
1000
1001 let mut result = this
1003 .iter()
1004 .zip(other.iter())
1005 .take_while(|(this, other)| is_matched_operation(this, other))
1006 .map(|(this, other)| this.animate(other, procedure))
1007 .collect::<Result<Vec<_>, _>>()?;
1008
1009 let this_remainder = if this.len() > result.len() {
1011 Some(&this[result.len()..])
1012 } else {
1013 None
1014 };
1015 let other_remainder = if other.len() > result.len() {
1016 Some(&other[result.len()..])
1017 } else {
1018 None
1019 };
1020
1021 match (this_remainder, other_remainder) {
1022 (Some(this_remainder), Some(other_remainder)) => {
1025 result.push(TransformOperation::animate_mismatched_transforms(
1026 this_remainder,
1027 other_remainder,
1028 procedure,
1029 )?);
1030 },
1031 (Some(remainder), None) | (None, Some(remainder)) => {
1035 let fill_right = this_remainder.is_some();
1036 result.append(
1037 &mut remainder
1038 .iter()
1039 .map(|transform| {
1040 let identity = transform.to_animated_zero().unwrap();
1041
1042 match transform {
1043 TransformOperation::AccumulateMatrix { .. }
1044 | TransformOperation::InterpolateMatrix { .. } => {
1045 let (from, to) = if fill_right {
1046 (transform, &identity)
1047 } else {
1048 (&identity, transform)
1049 };
1050
1051 TransformOperation::animate_mismatched_transforms(
1052 &[from.clone()],
1053 &[to.clone()],
1054 procedure,
1055 )
1056 },
1057 _ => {
1058 let (lhs, rhs) = if fill_right {
1059 (transform, &identity)
1060 } else {
1061 (&identity, transform)
1062 };
1063 lhs.animate(rhs, procedure)
1064 },
1065 }
1066 })
1067 .collect::<Result<Vec<_>, _>>()?,
1068 );
1069 },
1070 (None, None) => {},
1071 }
1072
1073 Ok(Transform(result.into()))
1074 }
1075}
1076
1077impl ComputeSquaredDistance for ComputedTransform {
1078 #[inline]
1079 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1080 let squared_dist = super::lists::with_zero::squared_distance(&self.0, &other.0);
1081
1082 if squared_dist.is_err() {
1088 let rect = euclid::Rect::zero();
1089 let matrix1: Matrix3D = self.to_transform_3d_matrix(Some(&rect))?.0.into();
1090 let matrix2: Matrix3D = other.to_transform_3d_matrix(Some(&rect))?.0.into();
1091 return matrix1.compute_squared_distance(&matrix2);
1092 }
1093
1094 squared_dist
1095 }
1096}
1097
1098impl Animate for ComputedTransformOperation {
1100 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1101 match (self, other) {
1102 (&TransformOperation::Matrix3D(ref this), &TransformOperation::Matrix3D(ref other)) => {
1103 Ok(TransformOperation::Matrix3D(
1104 this.animate(other, procedure)?,
1105 ))
1106 },
1107 (&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => {
1108 Ok(TransformOperation::Matrix(this.animate(other, procedure)?))
1109 },
1110 (
1111 &TransformOperation::Skew(ref fx, ref fy),
1112 &TransformOperation::Skew(ref tx, ref ty),
1113 ) => Ok(TransformOperation::Skew(
1114 fx.animate(tx, procedure)?,
1115 fy.animate(ty, procedure)?,
1116 )),
1117 (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t)) => {
1118 Ok(TransformOperation::SkewX(f.animate(t, procedure)?))
1119 },
1120 (&TransformOperation::SkewY(ref f), &TransformOperation::SkewY(ref t)) => {
1121 Ok(TransformOperation::SkewY(f.animate(t, procedure)?))
1122 },
1123 (
1124 &TransformOperation::Translate3D(ref fx, ref fy, ref fz),
1125 &TransformOperation::Translate3D(ref tx, ref ty, ref tz),
1126 ) => Ok(TransformOperation::Translate3D(
1127 fx.animate(tx, procedure)?,
1128 fy.animate(ty, procedure)?,
1129 fz.animate(tz, procedure)?,
1130 )),
1131 (
1132 &TransformOperation::Translate(ref fx, ref fy),
1133 &TransformOperation::Translate(ref tx, ref ty),
1134 ) => Ok(TransformOperation::Translate(
1135 fx.animate(tx, procedure)?,
1136 fy.animate(ty, procedure)?,
1137 )),
1138 (&TransformOperation::TranslateX(ref f), &TransformOperation::TranslateX(ref t)) => {
1139 Ok(TransformOperation::TranslateX(f.animate(t, procedure)?))
1140 },
1141 (&TransformOperation::TranslateY(ref f), &TransformOperation::TranslateY(ref t)) => {
1142 Ok(TransformOperation::TranslateY(f.animate(t, procedure)?))
1143 },
1144 (&TransformOperation::TranslateZ(ref f), &TransformOperation::TranslateZ(ref t)) => {
1145 Ok(TransformOperation::TranslateZ(f.animate(t, procedure)?))
1146 },
1147 (
1148 &TransformOperation::Scale3D(ref fx, ref fy, ref fz),
1149 &TransformOperation::Scale3D(ref tx, ref ty, ref tz),
1150 ) => Ok(TransformOperation::Scale3D(
1151 animate_multiplicative_factor(*fx, *tx, procedure)?,
1152 animate_multiplicative_factor(*fy, *ty, procedure)?,
1153 animate_multiplicative_factor(*fz, *tz, procedure)?,
1154 )),
1155 (&TransformOperation::ScaleX(ref f), &TransformOperation::ScaleX(ref t)) => Ok(
1156 TransformOperation::ScaleX(animate_multiplicative_factor(*f, *t, procedure)?),
1157 ),
1158 (&TransformOperation::ScaleY(ref f), &TransformOperation::ScaleY(ref t)) => Ok(
1159 TransformOperation::ScaleY(animate_multiplicative_factor(*f, *t, procedure)?),
1160 ),
1161 (&TransformOperation::ScaleZ(ref f), &TransformOperation::ScaleZ(ref t)) => Ok(
1162 TransformOperation::ScaleZ(animate_multiplicative_factor(*f, *t, procedure)?),
1163 ),
1164 (
1165 &TransformOperation::Scale(ref fx, ref fy),
1166 &TransformOperation::Scale(ref tx, ref ty),
1167 ) => Ok(TransformOperation::Scale(
1168 animate_multiplicative_factor(*fx, *tx, procedure)?,
1169 animate_multiplicative_factor(*fy, *ty, procedure)?,
1170 )),
1171 (
1172 &TransformOperation::Rotate3D(fx, fy, fz, fa),
1173 &TransformOperation::Rotate3D(tx, ty, tz, ta),
1174 ) => {
1175 let animated = Rotate::Rotate3D(fx, fy, fz, fa)
1176 .animate(&Rotate::Rotate3D(tx, ty, tz, ta), procedure)?;
1177 let (fx, fy, fz, fa) = ComputedRotate::resolve(&animated);
1178 Ok(TransformOperation::Rotate3D(fx, fy, fz, fa))
1179 },
1180 (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta)) => {
1181 Ok(TransformOperation::RotateX(fa.animate(&ta, procedure)?))
1182 },
1183 (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta)) => {
1184 Ok(TransformOperation::RotateY(fa.animate(&ta, procedure)?))
1185 },
1186 (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta)) => {
1187 Ok(TransformOperation::RotateZ(fa.animate(&ta, procedure)?))
1188 },
1189 (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {
1190 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1191 },
1192 (&TransformOperation::Rotate(fa), &TransformOperation::RotateZ(ta)) => {
1193 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1194 },
1195 (&TransformOperation::RotateZ(fa), &TransformOperation::Rotate(ta)) => {
1196 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1197 },
1198 (
1199 &TransformOperation::Perspective(ref fd),
1200 &TransformOperation::Perspective(ref td),
1201 ) => {
1202 use crate::values::computed::CSSPixelLength;
1203 use crate::values::generics::transform::create_perspective_matrix;
1204
1205 let from = create_perspective_matrix(fd.infinity_or(|l| l.px()));
1213 let to = create_perspective_matrix(td.infinity_or(|l| l.px()));
1214
1215 let interpolated = Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?;
1216
1217 let decomposed = decompose_3d_matrix(interpolated)?;
1218 let perspective_z = decomposed.perspective.2;
1219 let used_value = if perspective_z >= 0. {
1222 transform::PerspectiveFunction::None
1223 } else {
1224 transform::PerspectiveFunction::Length(CSSPixelLength::new(
1225 if perspective_z <= -1. {
1226 1.
1227 } else {
1228 -1. / perspective_z
1229 },
1230 ))
1231 };
1232 Ok(TransformOperation::Perspective(used_value))
1233 },
1234 _ if self.is_translate() && other.is_translate() => self
1235 .to_translate_3d()
1236 .animate(&other.to_translate_3d(), procedure),
1237 _ if self.is_scale() && other.is_scale() => {
1238 self.to_scale_3d().animate(&other.to_scale_3d(), procedure)
1239 },
1240 _ if self.is_rotate() && other.is_rotate() => self
1241 .to_rotate_3d()
1242 .animate(&other.to_rotate_3d(), procedure),
1243 _ => Err(()),
1244 }
1245 }
1246}
1247
1248impl ComputedTransformOperation {
1249 fn try_animate_mismatched_transforms_in_place(
1252 left: &[Self],
1253 right: &[Self],
1254 procedure: Procedure,
1255 ) -> Result<Self, ()> {
1256 let (left, _left_3d) = Transform::components_to_transform_3d_matrix(left, None)?;
1257 let (right, _right_3d) = Transform::components_to_transform_3d_matrix(right, None)?;
1258 Ok(Self::Matrix3D(
1259 Matrix3D::from(left).animate(&Matrix3D::from(right), procedure)?,
1260 ))
1261 }
1262
1263 fn animate_mismatched_transforms(
1264 left: &[Self],
1265 right: &[Self],
1266 procedure: Procedure,
1267 ) -> Result<Self, ()> {
1268 if let Ok(op) = Self::try_animate_mismatched_transforms_in_place(left, right, procedure) {
1269 return Ok(op);
1270 }
1271 let from_list = Transform(left.to_vec().into());
1272 let to_list = Transform(right.to_vec().into());
1273 Ok(match procedure {
1274 Procedure::Add => {
1275 debug_assert!(false, "Addition should've been handled earlier");
1276 return Err(());
1277 },
1278 Procedure::Interpolate { progress } => Self::InterpolateMatrix {
1279 from_list,
1280 to_list,
1281 progress: Percentage(progress as f32),
1282 },
1283 Procedure::Accumulate { count } => Self::AccumulateMatrix {
1284 from_list,
1285 to_list,
1286 count: cmp::min(count, i32::max_value() as u64) as i32,
1287 },
1288 })
1289 }
1290}
1291
1292impl ComputeSquaredDistance for ComputedTransformOperation {
1297 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1298 match (self, other) {
1299 (&TransformOperation::Matrix3D(ref this), &TransformOperation::Matrix3D(ref other)) => {
1300 this.compute_squared_distance(other)
1301 },
1302 (&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => {
1303 let this: Matrix3D = (*this).into();
1304 let other: Matrix3D = (*other).into();
1305 this.compute_squared_distance(&other)
1306 },
1307 (
1308 &TransformOperation::Skew(ref fx, ref fy),
1309 &TransformOperation::Skew(ref tx, ref ty),
1310 ) => Ok(fx.compute_squared_distance(&tx)? + fy.compute_squared_distance(&ty)?),
1311 (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t))
1312 | (&TransformOperation::SkewY(ref f), &TransformOperation::SkewY(ref t)) => {
1313 f.compute_squared_distance(&t)
1314 },
1315 (
1316 &TransformOperation::Translate3D(ref fx, ref fy, ref fz),
1317 &TransformOperation::Translate3D(ref tx, ref ty, ref tz),
1318 ) => {
1319 let basis = Length::new(0.);
1326 let fx = fx.resolve(basis).px();
1327 let fy = fy.resolve(basis).px();
1328 let tx = tx.resolve(basis).px();
1329 let ty = ty.resolve(basis).px();
1330
1331 Ok(fx.compute_squared_distance(&tx)?
1332 + fy.compute_squared_distance(&ty)?
1333 + fz.compute_squared_distance(&tz)?)
1334 },
1335 (
1336 &TransformOperation::Scale3D(ref fx, ref fy, ref fz),
1337 &TransformOperation::Scale3D(ref tx, ref ty, ref tz),
1338 ) => Ok(fx.compute_squared_distance(&tx)?
1339 + fy.compute_squared_distance(&ty)?
1340 + fz.compute_squared_distance(&tz)?),
1341 (
1342 &TransformOperation::Rotate3D(fx, fy, fz, fa),
1343 &TransformOperation::Rotate3D(tx, ty, tz, ta),
1344 ) => Rotate::Rotate3D(fx, fy, fz, fa)
1345 .compute_squared_distance(&Rotate::Rotate3D(tx, ty, tz, ta)),
1346 (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta))
1347 | (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta))
1348 | (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta))
1349 | (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {
1350 fa.compute_squared_distance(&ta)
1351 },
1352 (
1353 &TransformOperation::Perspective(ref fd),
1354 &TransformOperation::Perspective(ref td),
1355 ) => fd
1356 .infinity_or(|l| l.px())
1357 .compute_squared_distance(&td.infinity_or(|l| l.px())),
1358 (&TransformOperation::Perspective(ref p), &TransformOperation::Matrix3D(ref m))
1359 | (&TransformOperation::Matrix3D(ref m), &TransformOperation::Perspective(ref p)) => {
1360 let mut p_matrix = Matrix3D::identity();
1363 let p = p.infinity_or(|p| p.px());
1364 if p >= 0. {
1365 p_matrix.m34 = -1. / p.max(1.);
1366 }
1367 p_matrix.compute_squared_distance(&m)
1368 },
1369 _ if self.is_translate() && other.is_translate() => self
1373 .to_translate_3d()
1374 .compute_squared_distance(&other.to_translate_3d()),
1375 _ if self.is_scale() && other.is_scale() => self
1376 .to_scale_3d()
1377 .compute_squared_distance(&other.to_scale_3d()),
1378 _ if self.is_rotate() && other.is_rotate() => self
1379 .to_rotate_3d()
1380 .compute_squared_distance(&other.to_rotate_3d()),
1381 _ => Err(()),
1382 }
1383 }
1384}
1385
1386impl ComputedRotate {
1391 fn resolve(&self) -> (Number, Number, Number, Angle) {
1392 match *self {
1397 Rotate::None => (0., 0., 1., Angle::zero()),
1398 Rotate::Rotate3D(rx, ry, rz, angle) => (rx, ry, rz, angle),
1399 Rotate::Rotate(angle) => (0., 0., 1., angle),
1400 }
1401 }
1402}
1403
1404impl Animate for ComputedRotate {
1405 #[inline]
1406 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1407 use euclid::approxeq::ApproxEq;
1408 match (self, other) {
1409 (&Rotate::None, &Rotate::None) => Ok(Rotate::None),
1410 (&Rotate::Rotate3D(fx, fy, fz, fa), &Rotate::None) => {
1411 let (fx, fy, fz, fa) = transform::get_normalized_vector_and_angle(fx, fy, fz, fa);
1416 Ok(Rotate::Rotate3D(
1417 fx,
1418 fy,
1419 fz,
1420 fa.animate(&Angle::zero(), procedure)?,
1421 ))
1422 },
1423 (&Rotate::None, &Rotate::Rotate3D(tx, ty, tz, ta)) => {
1424 let (tx, ty, tz, ta) = transform::get_normalized_vector_and_angle(tx, ty, tz, ta);
1426 Ok(Rotate::Rotate3D(
1427 tx,
1428 ty,
1429 tz,
1430 Angle::zero().animate(&ta, procedure)?,
1431 ))
1432 },
1433 (&Rotate::Rotate3D(_, ..), _) | (_, &Rotate::Rotate3D(_, ..)) => {
1434 let (from, to) = (self.resolve(), other.resolve());
1437 let (fx, fy, fz, fa) =
1440 transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
1441 let (tx, ty, tz, ta) =
1442 transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
1443
1444 let fv = DirectionVector::new(fx, fy, fz);
1451 let tv = DirectionVector::new(tx, ty, tz);
1452 if fa.is_zero() || ta.is_zero() || fv.approx_eq(&tv) {
1453 let (x, y, z) = if fa.is_zero() && ta.is_zero() {
1454 (0., 0., 1.)
1455 } else if fa.is_zero() {
1456 (tx, ty, tz)
1457 } else {
1458 (fx, fy, fz)
1460 };
1461 return Ok(Rotate::Rotate3D(x, y, z, fa.animate(&ta, procedure)?));
1462 }
1463
1464 let rq = if procedure == Procedure::Add {
1474 let f = ComputedTransformOperation::Rotate3D(fx, fy, fz, fa);
1479 let t = ComputedTransformOperation::Rotate3D(tx, ty, tz, ta);
1480 let v =
1481 Transform(vec![f].into()).animate(&Transform(vec![t].into()), procedure)?;
1482 let (m, _) = v.to_transform_3d_matrix(None)?;
1483 decompose_3d_matrix(Matrix3D::from(m))?.quaternion
1485 } else {
1486 let fq = Quaternion::from_direction_and_angle(&fv, fa.radians64());
1499 let tq = Quaternion::from_direction_and_angle(&tv, ta.radians64());
1500 Quaternion::animate(&fq, &tq, procedure)?
1501 };
1502
1503 let (x, y, z, angle) = transform::get_normalized_vector_and_angle(
1504 rq.0 as f32,
1505 rq.1 as f32,
1506 rq.2 as f32,
1507 rq.3.clamp(-1.0, 1.0).acos() as f32 * 2.0,
1510 );
1511
1512 Ok(Rotate::Rotate3D(x, y, z, Angle::from_radians(angle)))
1513 },
1514 (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => {
1515 let (from, to) = (self.resolve().3, other.resolve().3);
1517 Ok(Rotate::Rotate(from.animate(&to, procedure)?))
1518 },
1519 }
1520 }
1521}
1522
1523impl ComputeSquaredDistance for ComputedRotate {
1524 #[inline]
1525 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1526 use euclid::approxeq::ApproxEq;
1527 match (self, other) {
1528 (&Rotate::None, &Rotate::None) => Ok(SquaredDistance::from_sqrt(0.)),
1529 (&Rotate::Rotate3D(_, _, _, a), &Rotate::None)
1530 | (&Rotate::None, &Rotate::Rotate3D(_, _, _, a)) => {
1531 a.compute_squared_distance(&Angle::zero())
1532 },
1533 (&Rotate::Rotate3D(_, ..), _) | (_, &Rotate::Rotate3D(_, ..)) => {
1534 let (from, to) = (self.resolve(), other.resolve());
1535 let (mut fx, mut fy, mut fz, angle1) =
1536 transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
1537 let (mut tx, mut ty, mut tz, angle2) =
1538 transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
1539
1540 if angle1.is_zero() && angle2.is_zero() {
1541 (fx, fy, fz) = (0., 0., 1.);
1542 (tx, ty, tz) = (0., 0., 1.);
1543 } else if angle1.is_zero() {
1544 (fx, fy, fz) = (tx, ty, tz);
1545 } else if angle2.is_zero() {
1546 (tx, ty, tz) = (fx, fy, fz);
1547 }
1548
1549 let v1 = DirectionVector::new(fx, fy, fz);
1550 let v2 = DirectionVector::new(tx, ty, tz);
1551 if v1.approx_eq(&v2) {
1552 angle1.compute_squared_distance(&angle2)
1553 } else {
1554 let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64());
1555 let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64());
1556 q1.compute_squared_distance(&q2)
1557 }
1558 },
1559 (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => self
1560 .resolve()
1561 .3
1562 .compute_squared_distance(&other.resolve().3),
1563 }
1564 }
1565}
1566
1567impl ComputedTranslate {
1569 fn resolve(&self) -> (LengthPercentage, LengthPercentage, Length) {
1570 match *self {
1575 Translate::None => (
1576 LengthPercentage::zero(),
1577 LengthPercentage::zero(),
1578 Length::zero(),
1579 ),
1580 Translate::Translate(ref tx, ref ty, ref tz) => (tx.clone(), ty.clone(), tz.clone()),
1581 }
1582 }
1583}
1584
1585impl Animate for ComputedTranslate {
1586 #[inline]
1587 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1588 match (self, other) {
1589 (&Translate::None, &Translate::None) => Ok(Translate::None),
1590 (&Translate::Translate(_, ..), _) | (_, &Translate::Translate(_, ..)) => {
1591 let (from, to) = (self.resolve(), other.resolve());
1592 Ok(Translate::Translate(
1593 from.0.animate(&to.0, procedure)?,
1594 from.1.animate(&to.1, procedure)?,
1595 from.2.animate(&to.2, procedure)?,
1596 ))
1597 },
1598 }
1599 }
1600}
1601
1602impl ComputeSquaredDistance for ComputedTranslate {
1603 #[inline]
1604 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1605 let (from, to) = (self.resolve(), other.resolve());
1606 Ok(from.0.compute_squared_distance(&to.0)?
1607 + from.1.compute_squared_distance(&to.1)?
1608 + from.2.compute_squared_distance(&to.2)?)
1609 }
1610}
1611
1612impl ComputedScale {
1614 fn resolve(&self) -> (Number, Number, Number) {
1615 match *self {
1620 Scale::None => (1.0, 1.0, 1.0),
1621 Scale::Scale(sx, sy, sz) => (sx, sy, sz),
1622 }
1623 }
1624}
1625
1626impl Animate for ComputedScale {
1627 #[inline]
1628 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1629 match (self, other) {
1630 (&Scale::None, &Scale::None) => Ok(Scale::None),
1631 (&Scale::Scale(_, ..), _) | (_, &Scale::Scale(_, ..)) => {
1632 let (from, to) = (self.resolve(), other.resolve());
1633 if procedure == Procedure::Add {
1638 return Ok(Scale::Scale(from.0 * to.0, from.1 * to.1, from.2 * to.2));
1640 }
1641 Ok(Scale::Scale(
1642 animate_multiplicative_factor(from.0, to.0, procedure)?,
1643 animate_multiplicative_factor(from.1, to.1, procedure)?,
1644 animate_multiplicative_factor(from.2, to.2, procedure)?,
1645 ))
1646 },
1647 }
1648 }
1649}
1650
1651impl ComputeSquaredDistance for ComputedScale {
1652 #[inline]
1653 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1654 let (from, to) = (self.resolve(), other.resolve());
1655 Ok(from.0.compute_squared_distance(&to.0)?
1656 + from.1.compute_squared_distance(&to.1)?
1657 + from.2.compute_squared_distance(&to.2)?)
1658 }
1659}