1use super::animate_multiplicative_factor;
10use super::{Animate, Procedure, ToAnimatedZero};
11use crate::values::computed::transform::Rotate as ComputedRotate;
12use crate::values::computed::transform::Scale as ComputedScale;
13use crate::values::computed::transform::Transform as ComputedTransform;
14use crate::values::computed::transform::TransformOperation as ComputedTransformOperation;
15use crate::values::computed::transform::Translate as ComputedTranslate;
16use crate::values::computed::transform::{DirectionVector, Matrix, Matrix3D};
17use crate::values::computed::Angle;
18use crate::values::computed::{Length, LengthPercentage};
19use crate::values::computed::{Number, Percentage};
20use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
21use crate::values::generics::transform::{self, Transform, TransformOperation};
22use crate::values::generics::transform::{Rotate, Scale, Translate};
23use crate::values::CSSFloat;
24use crate::Zero;
25use std::cmp;
26use std::ops::Add;
27
28#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]
33#[cfg_attr(feature = "servo", derive(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#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
59#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
60pub struct Translate2D(f32, f32);
61
62#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]
64#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
65pub struct Scale2D(f32, f32);
66
67impl Animate for Scale2D {
68 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
69 Ok(Scale2D(
70 animate_multiplicative_factor(self.0, other.0, procedure)?,
71 animate_multiplicative_factor(self.1, other.1, procedure)?,
72 ))
73 }
74}
75
76#[derive(Clone, Copy, Debug)]
78#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
79pub struct MatrixDecomposed2D {
80 pub translate: Translate2D,
82 pub scale: Scale2D,
84 pub angle: f32,
86 pub matrix: InnerMatrix2D,
88}
89
90impl Animate for MatrixDecomposed2D {
91 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
93 let mut scale = self.scale;
96 let mut angle = self.angle;
97 let mut other_angle = other.angle;
98 if (scale.0 < 0.0 && other.scale.1 < 0.0) || (scale.1 < 0.0 && other.scale.0 < 0.0) {
99 scale.0 = -scale.0;
100 scale.1 = -scale.1;
101 angle += if angle < 0.0 { 180. } else { -180. };
102 }
103
104 if angle == 0.0 {
106 angle = 360.
107 }
108 if other_angle == 0.0 {
109 other_angle = 360.
110 }
111
112 if (angle - other_angle).abs() > 180. {
113 if angle > other_angle {
114 angle -= 360.
115 } else {
116 other_angle -= 360.
117 }
118 }
119
120 let translate = self.translate.animate(&other.translate, procedure)?;
122 let scale = scale.animate(&other.scale, procedure)?;
123 let angle = angle.animate(&other_angle, procedure)?;
124 let matrix = self.matrix.animate(&other.matrix, procedure)?;
125
126 Ok(MatrixDecomposed2D {
127 translate,
128 scale,
129 angle,
130 matrix,
131 })
132 }
133}
134
135impl ComputeSquaredDistance for MatrixDecomposed2D {
136 #[inline]
137 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
138 const RAD_PER_DEG: f64 = std::f64::consts::PI / 180.0;
140 let angle1 = self.angle as f64 * RAD_PER_DEG;
141 let angle2 = other.angle as f64 * RAD_PER_DEG;
142 Ok(self.translate.compute_squared_distance(&other.translate)?
143 + self.scale.compute_squared_distance(&other.scale)?
144 + angle1.compute_squared_distance(&angle2)?
145 + self.matrix.compute_squared_distance(&other.matrix)?)
146 }
147}
148
149impl From<Matrix3D> for MatrixDecomposed2D {
150 fn from(matrix: Matrix3D) -> MatrixDecomposed2D {
153 let mut row0x = matrix.m11;
154 let mut row0y = matrix.m12;
155 let mut row1x = matrix.m21;
156 let mut row1y = matrix.m22;
157
158 let translate = Translate2D(matrix.m41, matrix.m42);
159 let mut scale = Scale2D(
160 (row0x * row0x + row0y * row0y).sqrt(),
161 (row1x * row1x + row1y * row1y).sqrt(),
162 );
163
164 let determinant = row0x * row1y - row0y * row1x;
166 if determinant < 0. {
167 if row0x < row1y {
168 scale.0 = -scale.0;
169 } else {
170 scale.1 = -scale.1;
171 }
172 }
173
174 if scale.0 != 0.0 {
176 row0x *= 1. / scale.0;
177 row0y *= 1. / scale.0;
178 }
179 if scale.1 != 0.0 {
180 row1x *= 1. / scale.1;
181 row1y *= 1. / scale.1;
182 }
183
184 let mut angle = row0y.atan2(row0x);
186 if angle != 0.0 {
187 let sn = -row0y;
188 let cs = row0x;
189 let m11 = row0x;
190 let m12 = row0y;
191 let m21 = row1x;
192 let m22 = row1y;
193 row0x = cs * m11 + sn * m21;
194 row0y = cs * m12 + sn * m22;
195 row1x = -sn * m11 + cs * m21;
196 row1y = -sn * m12 + cs * m22;
197 }
198
199 let m = InnerMatrix2D {
200 m11: row0x,
201 m12: row0y,
202 m21: row1x,
203 m22: row1y,
204 };
205
206 angle = angle.to_degrees();
208 MatrixDecomposed2D {
209 translate: translate,
210 scale: scale,
211 angle: angle,
212 matrix: m,
213 }
214 }
215}
216
217impl From<MatrixDecomposed2D> for Matrix3D {
218 fn from(decomposed: MatrixDecomposed2D) -> Matrix3D {
221 let mut computed_matrix = Matrix3D::identity();
222 computed_matrix.m11 = decomposed.matrix.m11;
223 computed_matrix.m12 = decomposed.matrix.m12;
224 computed_matrix.m21 = decomposed.matrix.m21;
225 computed_matrix.m22 = decomposed.matrix.m22;
226
227 computed_matrix.m41 = decomposed.translate.0;
229 computed_matrix.m42 = decomposed.translate.1;
230
231 let angle = decomposed.angle.to_radians();
233 let cos_angle = angle.cos();
234 let sin_angle = angle.sin();
235
236 let mut rotate_matrix = Matrix3D::identity();
237 rotate_matrix.m11 = cos_angle;
238 rotate_matrix.m12 = sin_angle;
239 rotate_matrix.m21 = -sin_angle;
240 rotate_matrix.m22 = cos_angle;
241
242 computed_matrix = rotate_matrix.multiply(&computed_matrix);
244
245 computed_matrix.m11 *= decomposed.scale.0;
247 computed_matrix.m12 *= decomposed.scale.0;
248 computed_matrix.m21 *= decomposed.scale.1;
249 computed_matrix.m22 *= decomposed.scale.1;
250 computed_matrix
251 }
252}
253
254impl Animate for Matrix {
255 #[cfg(feature = "servo")]
256 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
257 let this = Matrix3D::from(*self);
258 let other = Matrix3D::from(*other);
259 let this = MatrixDecomposed2D::from(this);
260 let other = MatrixDecomposed2D::from(other);
261 Matrix3D::from(this.animate(&other, procedure)?).into_2d()
262 }
263
264 #[cfg(feature = "gecko")]
265 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
268 let this = Matrix3D::from(*self);
269 let other = Matrix3D::from(*other);
270 let from = decompose_2d_matrix(&this)?;
271 let to = decompose_2d_matrix(&other)?;
272 Matrix3D::from(from.animate(&to, procedure)?).into_2d()
273 }
274}
275
276#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
278#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
279pub struct Translate3D(pub f32, pub f32, pub f32);
280
281#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]
283#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
284pub struct Scale3D(pub f32, pub f32, pub f32);
285
286impl Scale3D {
287 fn negate(&mut self) {
289 self.0 *= -1.0;
290 self.1 *= -1.0;
291 self.2 *= -1.0;
292 }
293}
294
295impl Animate for Scale3D {
296 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
297 Ok(Scale3D(
298 animate_multiplicative_factor(self.0, other.0, procedure)?,
299 animate_multiplicative_factor(self.1, other.1, procedure)?,
300 animate_multiplicative_factor(self.2, other.2, procedure)?,
301 ))
302 }
303}
304
305#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
307#[derive(Animate, Clone, Copy, Debug)]
308pub struct Skew(f32, f32, f32);
309
310impl ComputeSquaredDistance for Skew {
311 #[inline]
314 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
315 Ok(self.0.atan().compute_squared_distance(&other.0.atan())?
316 + self.1.atan().compute_squared_distance(&other.1.atan())?
317 + self.2.atan().compute_squared_distance(&other.2.atan())?)
318 }
319}
320
321#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]
323#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
324pub struct Perspective(pub f32, pub f32, pub f32, pub f32);
325
326impl Animate for Perspective {
327 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
328 Ok(Perspective(
329 self.0.animate(&other.0, procedure)?,
330 self.1.animate(&other.1, procedure)?,
331 self.2.animate(&other.2, procedure)?,
332 animate_multiplicative_factor(self.3, other.3, procedure)?,
333 ))
334 }
335}
336
337#[derive(Clone, Copy, Debug)]
339#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
340pub struct Quaternion(f64, f64, f64, f64);
341
342impl Quaternion {
343 #[inline]
345 fn from_direction_and_angle(vector: &DirectionVector, angle: f64) -> Self {
346 debug_assert!(
347 (vector.length() - 1.).abs() < 0.0001,
348 "Only accept an unit direction vector to create a quaternion"
349 );
350
351 let half_angle = angle
356 .abs()
357 .rem_euclid(std::f64::consts::TAU)
358 .copysign(angle)
359 / 2.;
360
361 Quaternion(
371 vector.x as f64 * half_angle.sin(),
372 vector.y as f64 * half_angle.sin(),
373 vector.z as f64 * half_angle.sin(),
374 half_angle.cos(),
375 )
376 }
377
378 #[inline]
380 fn dot(&self, other: &Self) -> f64 {
381 self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3
382 }
383
384 #[inline]
386 fn scale(&self, factor: f64) -> Self {
387 Quaternion(
388 self.0 * factor,
389 self.1 * factor,
390 self.2 * factor,
391 self.3 * factor,
392 )
393 }
394}
395
396impl Add for Quaternion {
397 type Output = Self;
398
399 fn add(self, other: Self) -> Self {
400 Self(
401 self.0 + other.0,
402 self.1 + other.1,
403 self.2 + other.2,
404 self.3 + other.3,
405 )
406 }
407}
408
409impl Animate for Quaternion {
410 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
411 let (this_weight, other_weight) = procedure.weights();
412 debug_assert!(
413 (this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON * 2.0
416 || other_weight == 1.0f64
417 || other_weight == 0.0f64,
418 "animate should only be used for interpolating or accumulating transforms"
419 );
420
421 if let Procedure::Accumulate { .. } = procedure {
424 debug_assert_eq!(other_weight, 1.0);
425 if this_weight == 0.0 {
426 return Ok(*other);
427 }
428
429 let clamped_w = self.3.min(1.0).max(-1.0);
430
431 let mut theta = clamped_w.acos();
433 let mut scale = if theta == 0.0 { 0.0 } else { 1.0 / theta.sin() };
434 theta *= this_weight;
435 scale *= theta.sin();
436
437 let mut scaled_self = *self;
439 scaled_self.0 *= scale;
440 scaled_self.1 *= scale;
441 scaled_self.2 *= scale;
442 scaled_self.3 = theta.cos();
443
444 let a = &scaled_self;
446 let b = other;
447 return Ok(Quaternion(
448 a.3 * b.0 + a.0 * b.3 + a.1 * b.2 - a.2 * b.1,
449 a.3 * b.1 - a.0 * b.2 + a.1 * b.3 + a.2 * b.0,
450 a.3 * b.2 + a.0 * b.1 - a.1 * b.0 + a.2 * b.3,
451 a.3 * b.3 - a.0 * b.0 - a.1 * b.1 - a.2 * b.2,
452 ));
453 }
454
455 let cos_half_theta =
459 (self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3)
460 .min(1.0)
461 .max(-1.0);
462
463 if cos_half_theta.abs() == 1.0 {
464 return Ok(*self);
465 }
466
467 let half_theta = cos_half_theta.acos();
468 let sin_half_theta = (1.0 - cos_half_theta * cos_half_theta).sqrt();
469
470 let right_weight = (other_weight * half_theta).sin() / sin_half_theta;
471 let left_weight = (this_weight * half_theta).sin() / sin_half_theta;
484
485 Ok(self.scale(left_weight) + other.scale(right_weight))
486 }
487}
488
489impl ComputeSquaredDistance for Quaternion {
490 #[inline]
491 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
492 let distance = self.dot(other).max(-1.0).min(1.0).acos() * 2.0;
496 Ok(SquaredDistance::from_sqrt(distance))
497 }
498}
499
500#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
502#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
503pub struct MatrixDecomposed3D {
504 pub translate: Translate3D,
506 pub scale: Scale3D,
508 pub skew: Skew,
510 pub perspective: Perspective,
512 pub quaternion: Quaternion,
514}
515
516impl From<MatrixDecomposed3D> for Matrix3D {
517 fn from(decomposed: MatrixDecomposed3D) -> Matrix3D {
520 let mut matrix = Matrix3D::identity();
521
522 matrix.set_perspective(&decomposed.perspective);
524
525 matrix.apply_translate(&decomposed.translate);
527
528 {
530 let x = decomposed.quaternion.0;
531 let y = decomposed.quaternion.1;
532 let z = decomposed.quaternion.2;
533 let w = decomposed.quaternion.3;
534
535 let mut rotation_matrix = Matrix3D::identity();
538 rotation_matrix.m11 = 1.0 - 2.0 * (y * y + z * z) as f32;
539 rotation_matrix.m12 = 2.0 * (x * y + z * w) as f32;
540 rotation_matrix.m13 = 2.0 * (x * z - y * w) as f32;
541 rotation_matrix.m21 = 2.0 * (x * y - z * w) as f32;
542 rotation_matrix.m22 = 1.0 - 2.0 * (x * x + z * z) as f32;
543 rotation_matrix.m23 = 2.0 * (y * z + x * w) as f32;
544 rotation_matrix.m31 = 2.0 * (x * z + y * w) as f32;
545 rotation_matrix.m32 = 2.0 * (y * z - x * w) as f32;
546 rotation_matrix.m33 = 1.0 - 2.0 * (x * x + y * y) as f32;
547
548 matrix = rotation_matrix.multiply(&matrix);
549 }
550
551 {
553 let mut temp = Matrix3D::identity();
554 if decomposed.skew.2 != 0.0 {
555 temp.m32 = decomposed.skew.2;
556 matrix = temp.multiply(&matrix);
557 temp.m32 = 0.0;
558 }
559
560 if decomposed.skew.1 != 0.0 {
561 temp.m31 = decomposed.skew.1;
562 matrix = temp.multiply(&matrix);
563 temp.m31 = 0.0;
564 }
565
566 if decomposed.skew.0 != 0.0 {
567 temp.m21 = decomposed.skew.0;
568 matrix = temp.multiply(&matrix);
569 }
570 }
571
572 matrix.apply_scale(&decomposed.scale);
574
575 matrix
576 }
577}
578
579fn decompose_3d_matrix(mut matrix: Matrix3D) -> Result<MatrixDecomposed3D, ()> {
583 let combine = |a: [f32; 3], b: [f32; 3], ascl: f32, bscl: f32| {
585 [
586 (ascl * a[0]) + (bscl * b[0]),
587 (ascl * a[1]) + (bscl * b[1]),
588 (ascl * a[2]) + (bscl * b[2]),
589 ]
590 };
591 let dot = |a: [f32; 3], b: [f32; 3]| a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
593 let cross = |row1: [f32; 3], row2: [f32; 3]| {
595 [
596 row1[1] * row2[2] - row1[2] * row2[1],
597 row1[2] * row2[0] - row1[0] * row2[2],
598 row1[0] * row2[1] - row1[1] * row2[0],
599 ]
600 };
601
602 if matrix.m44 == 0.0 {
603 return Err(());
604 }
605
606 let scaling_factor = matrix.m44;
607
608 matrix.scale_by_factor(1.0 / scaling_factor);
610
611 let mut perspective_matrix = matrix;
614
615 perspective_matrix.m14 = 0.0;
616 perspective_matrix.m24 = 0.0;
617 perspective_matrix.m34 = 0.0;
618 perspective_matrix.m44 = 1.0;
619
620 if perspective_matrix.determinant() == 0.0 {
621 return Err(());
622 }
623
624 let perspective = if matrix.m14 != 0.0 || matrix.m24 != 0.0 || matrix.m34 != 0.0 {
626 let right_hand_side: [f32; 4] = [matrix.m14, matrix.m24, matrix.m34, matrix.m44];
627
628 perspective_matrix = perspective_matrix.inverse().unwrap().transpose();
629 let perspective = perspective_matrix.pre_mul_point4(&right_hand_side);
630 Perspective(
634 perspective[0],
635 perspective[1],
636 perspective[2],
637 perspective[3],
638 )
639 } else {
640 Perspective(0.0, 0.0, 0.0, 1.0)
641 };
642
643 let translate = Translate3D(matrix.m41, matrix.m42, matrix.m43);
645
646 let mut row = matrix.get_matrix_3x3_part();
648
649 let row0len = (row[0][0] * row[0][0] + row[0][1] * row[0][1] + row[0][2] * row[0][2]).sqrt();
651 let mut scale = Scale3D(row0len, 0.0, 0.0);
652 row[0] = [
653 row[0][0] / row0len,
654 row[0][1] / row0len,
655 row[0][2] / row0len,
656 ];
657
658 let mut skew = Skew(dot(row[0], row[1]), 0.0, 0.0);
660 row[1] = combine(row[1], row[0], 1.0, -skew.0);
661
662 let row1len = (row[1][0] * row[1][0] + row[1][1] * row[1][1] + row[1][2] * row[1][2]).sqrt();
664 scale.1 = row1len;
665 row[1] = [
666 row[1][0] / row1len,
667 row[1][1] / row1len,
668 row[1][2] / row1len,
669 ];
670 skew.0 /= scale.1;
671
672 skew.1 = dot(row[0], row[2]);
674 row[2] = combine(row[2], row[0], 1.0, -skew.1);
675 skew.2 = dot(row[1], row[2]);
676 row[2] = combine(row[2], row[1], 1.0, -skew.2);
677
678 let row2len = (row[2][0] * row[2][0] + row[2][1] * row[2][1] + row[2][2] * row[2][2]).sqrt();
680 scale.2 = row2len;
681 row[2] = [
682 row[2][0] / row2len,
683 row[2][1] / row2len,
684 row[2][2] / row2len,
685 ];
686 skew.1 /= scale.2;
687 skew.2 /= scale.2;
688
689 if dot(row[0], cross(row[1], row[2])) < 0.0 {
693 scale.negate();
694 for i in 0..3 {
695 row[i][0] *= -1.0;
696 row[i][1] *= -1.0;
697 row[i][2] *= -1.0;
698 }
699 }
700
701 let mut quaternion = Quaternion(
703 0.5 * ((1.0 + row[0][0] - row[1][1] - row[2][2]).max(0.0) as f64).sqrt(),
704 0.5 * ((1.0 - row[0][0] + row[1][1] - row[2][2]).max(0.0) as f64).sqrt(),
705 0.5 * ((1.0 - row[0][0] - row[1][1] + row[2][2]).max(0.0) as f64).sqrt(),
706 0.5 * ((1.0 + row[0][0] + row[1][1] + row[2][2]).max(0.0) as f64).sqrt(),
707 );
708
709 if row[2][1] > row[1][2] {
710 quaternion.0 = -quaternion.0
711 }
712 if row[0][2] > row[2][0] {
713 quaternion.1 = -quaternion.1
714 }
715 if row[1][0] > row[0][1] {
716 quaternion.2 = -quaternion.2
717 }
718
719 Ok(MatrixDecomposed3D {
720 translate,
721 scale,
722 skew,
723 perspective,
724 quaternion,
725 })
726}
727
728#[cfg(feature = "gecko")]
850fn decompose_2d_matrix(matrix: &Matrix3D) -> Result<MatrixDecomposed3D, ()> {
851 let (mut m11, mut m12) = (matrix.m11, matrix.m12);
857 let (mut m21, mut m22) = (matrix.m21, matrix.m22);
858 if m11 * m22 == m12 * m21 {
860 return Err(());
861 }
862
863 let mut scale_x = (m11 * m11 + m12 * m12).sqrt();
864 m11 /= scale_x;
865 m12 /= scale_x;
866
867 let mut shear_xy = m11 * m21 + m12 * m22;
868 m21 -= m11 * shear_xy;
869 m22 -= m12 * shear_xy;
870
871 let scale_y = (m21 * m21 + m22 * m22).sqrt();
872 m21 /= scale_y;
873 m22 /= scale_y;
874 shear_xy /= scale_y;
875
876 let determinant = m11 * m22 - m12 * m21;
877 if 0.99 > determinant.abs() || determinant.abs() > 1.01 {
879 return Err(());
880 }
881
882 if determinant < 0. {
883 m11 = -m11;
884 m12 = -m12;
885 shear_xy = -shear_xy;
886 scale_x = -scale_x;
887 }
888
889 Ok(MatrixDecomposed3D {
890 translate: Translate3D(matrix.m41, matrix.m42, 0.),
891 scale: Scale3D(scale_x, scale_y, 1.),
892 skew: Skew(shear_xy, 0., 0.),
893 perspective: Perspective(0., 0., 0., 1.),
894 quaternion: Quaternion::from_direction_and_angle(
895 &DirectionVector::new(0., 0., 1.),
896 m12.atan2(m11) as f64,
897 ),
898 })
899}
900
901impl Animate for Matrix3D {
902 #[cfg(feature = "servo")]
903 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
904 if self.is_3d() || other.is_3d() {
905 let decomposed_from = decompose_3d_matrix(*self);
906 let decomposed_to = decompose_3d_matrix(*other);
907 match (decomposed_from, decomposed_to) {
908 (Ok(this), Ok(other)) => Ok(Matrix3D::from(this.animate(&other, procedure)?)),
909 _ => Err(()),
913 }
914 } else {
915 let this = MatrixDecomposed2D::from(*self);
916 let other = MatrixDecomposed2D::from(*other);
917 Ok(Matrix3D::from(this.animate(&other, procedure)?))
918 }
919 }
920
921 #[cfg(feature = "gecko")]
922 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
925 let (from, to) = if self.is_3d() || other.is_3d() {
926 (decompose_3d_matrix(*self)?, decompose_3d_matrix(*other)?)
927 } else {
928 (decompose_2d_matrix(self)?, decompose_2d_matrix(other)?)
929 };
930 Ok(Matrix3D::from(from.animate(&to, procedure)?))
934 }
935}
936
937impl ComputeSquaredDistance for Matrix3D {
938 #[inline]
939 #[cfg(feature = "servo")]
940 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
941 if self.is_3d() || other.is_3d() {
942 let from = decompose_3d_matrix(*self)?;
943 let to = decompose_3d_matrix(*other)?;
944 from.compute_squared_distance(&to)
945 } else {
946 let from = MatrixDecomposed2D::from(*self);
947 let to = MatrixDecomposed2D::from(*other);
948 from.compute_squared_distance(&to)
949 }
950 }
951
952 #[inline]
953 #[cfg(feature = "gecko")]
954 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
955 let (from, to) = if self.is_3d() || other.is_3d() {
956 (decompose_3d_matrix(*self)?, decompose_3d_matrix(*other)?)
957 } else {
958 (decompose_2d_matrix(self)?, decompose_2d_matrix(other)?)
959 };
960 from.compute_squared_distance(&to)
961 }
962}
963
964fn is_matched_operation(
968 first: &ComputedTransformOperation,
969 second: &ComputedTransformOperation,
970) -> bool {
971 match (first, second) {
972 (&TransformOperation::Matrix(..), &TransformOperation::Matrix(..))
973 | (&TransformOperation::Matrix3D(..), &TransformOperation::Matrix3D(..))
974 | (&TransformOperation::Skew(..), &TransformOperation::Skew(..))
975 | (&TransformOperation::SkewX(..), &TransformOperation::SkewX(..))
976 | (&TransformOperation::SkewY(..), &TransformOperation::SkewY(..))
977 | (&TransformOperation::Rotate(..), &TransformOperation::Rotate(..))
978 | (&TransformOperation::Rotate3D(..), &TransformOperation::Rotate3D(..))
979 | (&TransformOperation::RotateX(..), &TransformOperation::RotateX(..))
980 | (&TransformOperation::RotateY(..), &TransformOperation::RotateY(..))
981 | (&TransformOperation::RotateZ(..), &TransformOperation::RotateZ(..))
982 | (&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => true,
983 (a, b) if a.is_translate() && b.is_translate() => true,
985 (a, b) if a.is_scale() && b.is_scale() => true,
986 (a, b) if a.is_rotate() && b.is_rotate() => true,
987 _ => false,
989 }
990}
991
992impl Animate for ComputedTransform {
994 #[inline]
995 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
996 use std::borrow::Cow;
997
998 if procedure == Procedure::Add {
1003 let result = self.0.iter().chain(&*other.0).cloned().collect();
1004 return Ok(Transform(result));
1005 }
1006
1007 let this = Cow::Borrowed(&self.0);
1008 let other = Cow::Borrowed(&other.0);
1009
1010 let mut result = this
1012 .iter()
1013 .zip(other.iter())
1014 .take_while(|(this, other)| is_matched_operation(this, other))
1015 .map(|(this, other)| this.animate(other, procedure))
1016 .collect::<Result<Vec<_>, _>>()?;
1017
1018 let this_remainder = if this.len() > result.len() {
1020 Some(&this[result.len()..])
1021 } else {
1022 None
1023 };
1024 let other_remainder = if other.len() > result.len() {
1025 Some(&other[result.len()..])
1026 } else {
1027 None
1028 };
1029
1030 match (this_remainder, other_remainder) {
1031 (Some(this_remainder), Some(other_remainder)) => {
1034 result.push(TransformOperation::animate_mismatched_transforms(
1035 this_remainder,
1036 other_remainder,
1037 procedure,
1038 )?);
1039 },
1040 (Some(remainder), None) | (None, Some(remainder)) => {
1044 let fill_right = this_remainder.is_some();
1045 result.append(
1046 &mut remainder
1047 .iter()
1048 .map(|transform| {
1049 let identity = transform.to_animated_zero().unwrap();
1050
1051 match transform {
1052 TransformOperation::AccumulateMatrix { .. }
1053 | TransformOperation::InterpolateMatrix { .. } => {
1054 let (from, to) = if fill_right {
1055 (transform, &identity)
1056 } else {
1057 (&identity, transform)
1058 };
1059
1060 TransformOperation::animate_mismatched_transforms(
1061 &[from.clone()],
1062 &[to.clone()],
1063 procedure,
1064 )
1065 },
1066 _ => {
1067 let (lhs, rhs) = if fill_right {
1068 (transform, &identity)
1069 } else {
1070 (&identity, transform)
1071 };
1072 lhs.animate(rhs, procedure)
1073 },
1074 }
1075 })
1076 .collect::<Result<Vec<_>, _>>()?,
1077 );
1078 },
1079 (None, None) => {},
1080 }
1081
1082 Ok(Transform(result.into()))
1083 }
1084}
1085
1086impl ComputeSquaredDistance for ComputedTransform {
1087 #[inline]
1088 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1089 let squared_dist = super::lists::with_zero::squared_distance(&self.0, &other.0);
1090
1091 if squared_dist.is_err() {
1097 let rect = euclid::Rect::zero();
1098 let matrix1: Matrix3D = self.to_transform_3d_matrix(Some(&rect))?.0.into();
1099 let matrix2: Matrix3D = other.to_transform_3d_matrix(Some(&rect))?.0.into();
1100 return matrix1.compute_squared_distance(&matrix2);
1101 }
1102
1103 squared_dist
1104 }
1105}
1106
1107impl Animate for ComputedTransformOperation {
1109 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1110 match (self, other) {
1111 (&TransformOperation::Matrix3D(ref this), &TransformOperation::Matrix3D(ref other)) => {
1112 Ok(TransformOperation::Matrix3D(
1113 this.animate(other, procedure)?,
1114 ))
1115 },
1116 (&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => {
1117 Ok(TransformOperation::Matrix(this.animate(other, procedure)?))
1118 },
1119 (
1120 &TransformOperation::Skew(ref fx, ref fy),
1121 &TransformOperation::Skew(ref tx, ref ty),
1122 ) => Ok(TransformOperation::Skew(
1123 fx.animate(tx, procedure)?,
1124 fy.animate(ty, procedure)?,
1125 )),
1126 (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t)) => {
1127 Ok(TransformOperation::SkewX(f.animate(t, procedure)?))
1128 },
1129 (&TransformOperation::SkewY(ref f), &TransformOperation::SkewY(ref t)) => {
1130 Ok(TransformOperation::SkewY(f.animate(t, procedure)?))
1131 },
1132 (
1133 &TransformOperation::Translate3D(ref fx, ref fy, ref fz),
1134 &TransformOperation::Translate3D(ref tx, ref ty, ref tz),
1135 ) => Ok(TransformOperation::Translate3D(
1136 fx.animate(tx, procedure)?,
1137 fy.animate(ty, procedure)?,
1138 fz.animate(tz, procedure)?,
1139 )),
1140 (
1141 &TransformOperation::Translate(ref fx, ref fy),
1142 &TransformOperation::Translate(ref tx, ref ty),
1143 ) => Ok(TransformOperation::Translate(
1144 fx.animate(tx, procedure)?,
1145 fy.animate(ty, procedure)?,
1146 )),
1147 (&TransformOperation::TranslateX(ref f), &TransformOperation::TranslateX(ref t)) => {
1148 Ok(TransformOperation::TranslateX(f.animate(t, procedure)?))
1149 },
1150 (&TransformOperation::TranslateY(ref f), &TransformOperation::TranslateY(ref t)) => {
1151 Ok(TransformOperation::TranslateY(f.animate(t, procedure)?))
1152 },
1153 (&TransformOperation::TranslateZ(ref f), &TransformOperation::TranslateZ(ref t)) => {
1154 Ok(TransformOperation::TranslateZ(f.animate(t, procedure)?))
1155 },
1156 (
1157 &TransformOperation::Scale3D(ref fx, ref fy, ref fz),
1158 &TransformOperation::Scale3D(ref tx, ref ty, ref tz),
1159 ) => Ok(TransformOperation::Scale3D(
1160 animate_multiplicative_factor(*fx, *tx, procedure)?,
1161 animate_multiplicative_factor(*fy, *ty, procedure)?,
1162 animate_multiplicative_factor(*fz, *tz, procedure)?,
1163 )),
1164 (&TransformOperation::ScaleX(ref f), &TransformOperation::ScaleX(ref t)) => Ok(
1165 TransformOperation::ScaleX(animate_multiplicative_factor(*f, *t, procedure)?),
1166 ),
1167 (&TransformOperation::ScaleY(ref f), &TransformOperation::ScaleY(ref t)) => Ok(
1168 TransformOperation::ScaleY(animate_multiplicative_factor(*f, *t, procedure)?),
1169 ),
1170 (&TransformOperation::ScaleZ(ref f), &TransformOperation::ScaleZ(ref t)) => Ok(
1171 TransformOperation::ScaleZ(animate_multiplicative_factor(*f, *t, procedure)?),
1172 ),
1173 (
1174 &TransformOperation::Scale(ref fx, ref fy),
1175 &TransformOperation::Scale(ref tx, ref ty),
1176 ) => Ok(TransformOperation::Scale(
1177 animate_multiplicative_factor(*fx, *tx, procedure)?,
1178 animate_multiplicative_factor(*fy, *ty, procedure)?,
1179 )),
1180 (
1181 &TransformOperation::Rotate3D(fx, fy, fz, fa),
1182 &TransformOperation::Rotate3D(tx, ty, tz, ta),
1183 ) => {
1184 let animated = Rotate::Rotate3D(fx, fy, fz, fa)
1185 .animate(&Rotate::Rotate3D(tx, ty, tz, ta), procedure)?;
1186 let (fx, fy, fz, fa) = ComputedRotate::resolve(&animated);
1187 Ok(TransformOperation::Rotate3D(fx, fy, fz, fa))
1188 },
1189 (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta)) => {
1190 Ok(TransformOperation::RotateX(fa.animate(&ta, procedure)?))
1191 },
1192 (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta)) => {
1193 Ok(TransformOperation::RotateY(fa.animate(&ta, procedure)?))
1194 },
1195 (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta)) => {
1196 Ok(TransformOperation::RotateZ(fa.animate(&ta, procedure)?))
1197 },
1198 (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {
1199 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1200 },
1201 (&TransformOperation::Rotate(fa), &TransformOperation::RotateZ(ta)) => {
1202 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1203 },
1204 (&TransformOperation::RotateZ(fa), &TransformOperation::Rotate(ta)) => {
1205 Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
1206 },
1207 (
1208 &TransformOperation::Perspective(ref fd),
1209 &TransformOperation::Perspective(ref td),
1210 ) => {
1211 use crate::values::computed::CSSPixelLength;
1212 use crate::values::generics::transform::create_perspective_matrix;
1213
1214 let from = create_perspective_matrix(fd.infinity_or(|l| l.px()));
1222 let to = create_perspective_matrix(td.infinity_or(|l| l.px()));
1223
1224 let interpolated = Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?;
1225
1226 let decomposed = decompose_3d_matrix(interpolated)?;
1227 let perspective_z = decomposed.perspective.2;
1228 let used_value = if perspective_z >= 0. {
1231 transform::PerspectiveFunction::None
1232 } else {
1233 transform::PerspectiveFunction::Length(CSSPixelLength::new(
1234 if perspective_z <= -1. {
1235 1.
1236 } else {
1237 -1. / perspective_z
1238 },
1239 ))
1240 };
1241 Ok(TransformOperation::Perspective(used_value))
1242 },
1243 _ if self.is_translate() && other.is_translate() => self
1244 .to_translate_3d()
1245 .animate(&other.to_translate_3d(), procedure),
1246 _ if self.is_scale() && other.is_scale() => {
1247 self.to_scale_3d().animate(&other.to_scale_3d(), procedure)
1248 },
1249 _ if self.is_rotate() && other.is_rotate() => self
1250 .to_rotate_3d()
1251 .animate(&other.to_rotate_3d(), procedure),
1252 _ => Err(()),
1253 }
1254 }
1255}
1256
1257impl ComputedTransformOperation {
1258 fn try_animate_mismatched_transforms_in_place(
1261 left: &[Self],
1262 right: &[Self],
1263 procedure: Procedure,
1264 ) -> Result<Self, ()> {
1265 let (left, _left_3d) = Transform::components_to_transform_3d_matrix(left, None)?;
1266 let (right, _right_3d) = Transform::components_to_transform_3d_matrix(right, None)?;
1267 Ok(Self::Matrix3D(
1268 Matrix3D::from(left).animate(&Matrix3D::from(right), procedure)?,
1269 ))
1270 }
1271
1272 fn animate_mismatched_transforms(
1273 left: &[Self],
1274 right: &[Self],
1275 procedure: Procedure,
1276 ) -> Result<Self, ()> {
1277 if let Ok(op) = Self::try_animate_mismatched_transforms_in_place(left, right, procedure) {
1278 return Ok(op);
1279 }
1280 let from_list = Transform(left.to_vec().into());
1281 let to_list = Transform(right.to_vec().into());
1282 Ok(match procedure {
1283 Procedure::Add => {
1284 debug_assert!(false, "Addition should've been handled earlier");
1285 return Err(());
1286 },
1287 Procedure::Interpolate { progress } => Self::InterpolateMatrix {
1288 from_list,
1289 to_list,
1290 progress: Percentage(progress as f32),
1291 },
1292 Procedure::Accumulate { count } => Self::AccumulateMatrix {
1293 from_list,
1294 to_list,
1295 count: cmp::min(count, i32::max_value() as u64) as i32,
1296 },
1297 })
1298 }
1299}
1300
1301impl ComputeSquaredDistance for ComputedTransformOperation {
1306 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1307 match (self, other) {
1308 (&TransformOperation::Matrix3D(ref this), &TransformOperation::Matrix3D(ref other)) => {
1309 this.compute_squared_distance(other)
1310 },
1311 (&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => {
1312 let this: Matrix3D = (*this).into();
1313 let other: Matrix3D = (*other).into();
1314 this.compute_squared_distance(&other)
1315 },
1316 (
1317 &TransformOperation::Skew(ref fx, ref fy),
1318 &TransformOperation::Skew(ref tx, ref ty),
1319 ) => Ok(fx.compute_squared_distance(&tx)? + fy.compute_squared_distance(&ty)?),
1320 (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t))
1321 | (&TransformOperation::SkewY(ref f), &TransformOperation::SkewY(ref t)) => {
1322 f.compute_squared_distance(&t)
1323 },
1324 (
1325 &TransformOperation::Translate3D(ref fx, ref fy, ref fz),
1326 &TransformOperation::Translate3D(ref tx, ref ty, ref tz),
1327 ) => {
1328 let basis = Length::new(0.);
1335 let fx = fx.resolve(basis).px();
1336 let fy = fy.resolve(basis).px();
1337 let tx = tx.resolve(basis).px();
1338 let ty = ty.resolve(basis).px();
1339
1340 Ok(fx.compute_squared_distance(&tx)?
1341 + fy.compute_squared_distance(&ty)?
1342 + fz.compute_squared_distance(&tz)?)
1343 },
1344 (
1345 &TransformOperation::Scale3D(ref fx, ref fy, ref fz),
1346 &TransformOperation::Scale3D(ref tx, ref ty, ref tz),
1347 ) => Ok(fx.compute_squared_distance(&tx)?
1348 + fy.compute_squared_distance(&ty)?
1349 + fz.compute_squared_distance(&tz)?),
1350 (
1351 &TransformOperation::Rotate3D(fx, fy, fz, fa),
1352 &TransformOperation::Rotate3D(tx, ty, tz, ta),
1353 ) => Rotate::Rotate3D(fx, fy, fz, fa)
1354 .compute_squared_distance(&Rotate::Rotate3D(tx, ty, tz, ta)),
1355 (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta))
1356 | (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta))
1357 | (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta))
1358 | (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {
1359 fa.compute_squared_distance(&ta)
1360 },
1361 (
1362 &TransformOperation::Perspective(ref fd),
1363 &TransformOperation::Perspective(ref td),
1364 ) => fd
1365 .infinity_or(|l| l.px())
1366 .compute_squared_distance(&td.infinity_or(|l| l.px())),
1367 (&TransformOperation::Perspective(ref p), &TransformOperation::Matrix3D(ref m))
1368 | (&TransformOperation::Matrix3D(ref m), &TransformOperation::Perspective(ref p)) => {
1369 let mut p_matrix = Matrix3D::identity();
1372 let p = p.infinity_or(|p| p.px());
1373 if p >= 0. {
1374 p_matrix.m34 = -1. / p.max(1.);
1375 }
1376 p_matrix.compute_squared_distance(&m)
1377 },
1378 _ if self.is_translate() && other.is_translate() => self
1382 .to_translate_3d()
1383 .compute_squared_distance(&other.to_translate_3d()),
1384 _ if self.is_scale() && other.is_scale() => self
1385 .to_scale_3d()
1386 .compute_squared_distance(&other.to_scale_3d()),
1387 _ if self.is_rotate() && other.is_rotate() => self
1388 .to_rotate_3d()
1389 .compute_squared_distance(&other.to_rotate_3d()),
1390 _ => Err(()),
1391 }
1392 }
1393}
1394
1395impl ComputedRotate {
1400 fn resolve(&self) -> (Number, Number, Number, Angle) {
1401 match *self {
1406 Rotate::None => (0., 0., 1., Angle::zero()),
1407 Rotate::Rotate3D(rx, ry, rz, angle) => (rx, ry, rz, angle),
1408 Rotate::Rotate(angle) => (0., 0., 1., angle),
1409 }
1410 }
1411}
1412
1413impl Animate for ComputedRotate {
1414 #[inline]
1415 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1416 use euclid::approxeq::ApproxEq;
1417 match (self, other) {
1418 (&Rotate::None, &Rotate::None) => Ok(Rotate::None),
1419 (&Rotate::Rotate3D(fx, fy, fz, fa), &Rotate::None) => {
1420 let (fx, fy, fz, fa) = transform::get_normalized_vector_and_angle(fx, fy, fz, fa);
1425 Ok(Rotate::Rotate3D(
1426 fx,
1427 fy,
1428 fz,
1429 fa.animate(&Angle::zero(), procedure)?,
1430 ))
1431 },
1432 (&Rotate::None, &Rotate::Rotate3D(tx, ty, tz, ta)) => {
1433 let (tx, ty, tz, ta) = transform::get_normalized_vector_and_angle(tx, ty, tz, ta);
1435 Ok(Rotate::Rotate3D(
1436 tx,
1437 ty,
1438 tz,
1439 Angle::zero().animate(&ta, procedure)?,
1440 ))
1441 },
1442 (&Rotate::Rotate3D(_, ..), _) | (_, &Rotate::Rotate3D(_, ..)) => {
1443 let (from, to) = (self.resolve(), other.resolve());
1446 let (fx, fy, fz, fa) =
1449 transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
1450 let (tx, ty, tz, ta) =
1451 transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
1452
1453 let fv = DirectionVector::new(fx, fy, fz);
1460 let tv = DirectionVector::new(tx, ty, tz);
1461 if fa.is_zero() || ta.is_zero() || fv.approx_eq(&tv) {
1462 let (x, y, z) = if fa.is_zero() && ta.is_zero() {
1463 (0., 0., 1.)
1464 } else if fa.is_zero() {
1465 (tx, ty, tz)
1466 } else {
1467 (fx, fy, fz)
1469 };
1470 return Ok(Rotate::Rotate3D(x, y, z, fa.animate(&ta, procedure)?));
1471 }
1472
1473 let rq = if procedure == Procedure::Add {
1483 let f = ComputedTransformOperation::Rotate3D(fx, fy, fz, fa);
1488 let t = ComputedTransformOperation::Rotate3D(tx, ty, tz, ta);
1489 let v =
1490 Transform(vec![f].into()).animate(&Transform(vec![t].into()), procedure)?;
1491 let (m, _) = v.to_transform_3d_matrix(None)?;
1492 decompose_3d_matrix(Matrix3D::from(m))?.quaternion
1494 } else {
1495 let fq = Quaternion::from_direction_and_angle(&fv, fa.radians64());
1508 let tq = Quaternion::from_direction_and_angle(&tv, ta.radians64());
1509 Quaternion::animate(&fq, &tq, procedure)?
1510 };
1511
1512 debug_assert!(rq.3 <= 1.0 && rq.3 >= -1.0, "Invalid cosine value");
1513 let (x, y, z, angle) = transform::get_normalized_vector_and_angle(
1514 rq.0 as f32,
1515 rq.1 as f32,
1516 rq.2 as f32,
1517 rq.3.acos() as f32 * 2.0,
1518 );
1519
1520 Ok(Rotate::Rotate3D(x, y, z, Angle::from_radians(angle)))
1521 },
1522 (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => {
1523 let (from, to) = (self.resolve().3, other.resolve().3);
1525 Ok(Rotate::Rotate(from.animate(&to, procedure)?))
1526 },
1527 }
1528 }
1529}
1530
1531impl ComputeSquaredDistance for ComputedRotate {
1532 #[inline]
1533 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1534 use euclid::approxeq::ApproxEq;
1535 match (self, other) {
1536 (&Rotate::None, &Rotate::None) => Ok(SquaredDistance::from_sqrt(0.)),
1537 (&Rotate::Rotate3D(_, _, _, a), &Rotate::None)
1538 | (&Rotate::None, &Rotate::Rotate3D(_, _, _, a)) => {
1539 a.compute_squared_distance(&Angle::zero())
1540 },
1541 (&Rotate::Rotate3D(_, ..), _) | (_, &Rotate::Rotate3D(_, ..)) => {
1542 let (from, to) = (self.resolve(), other.resolve());
1543 let (mut fx, mut fy, mut fz, angle1) =
1544 transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
1545 let (mut tx, mut ty, mut tz, angle2) =
1546 transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
1547
1548 if angle1.is_zero() && angle2.is_zero() {
1549 (fx, fy, fz) = (0., 0., 1.);
1550 (tx, ty, tz) = (0., 0., 1.);
1551 } else if angle1.is_zero() {
1552 (fx, fy, fz) = (tx, ty, tz);
1553 } else if angle2.is_zero() {
1554 (tx, ty, tz) = (fx, fy, fz);
1555 }
1556
1557 let v1 = DirectionVector::new(fx, fy, fz);
1558 let v2 = DirectionVector::new(tx, ty, tz);
1559 if v1.approx_eq(&v2) {
1560 angle1.compute_squared_distance(&angle2)
1561 } else {
1562 let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64());
1563 let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64());
1564 q1.compute_squared_distance(&q2)
1565 }
1566 },
1567 (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => self
1568 .resolve()
1569 .3
1570 .compute_squared_distance(&other.resolve().3),
1571 }
1572 }
1573}
1574
1575impl ComputedTranslate {
1577 fn resolve(&self) -> (LengthPercentage, LengthPercentage, Length) {
1578 match *self {
1583 Translate::None => (
1584 LengthPercentage::zero(),
1585 LengthPercentage::zero(),
1586 Length::zero(),
1587 ),
1588 Translate::Translate(ref tx, ref ty, ref tz) => (tx.clone(), ty.clone(), tz.clone()),
1589 }
1590 }
1591}
1592
1593impl Animate for ComputedTranslate {
1594 #[inline]
1595 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1596 match (self, other) {
1597 (&Translate::None, &Translate::None) => Ok(Translate::None),
1598 (&Translate::Translate(_, ..), _) | (_, &Translate::Translate(_, ..)) => {
1599 let (from, to) = (self.resolve(), other.resolve());
1600 Ok(Translate::Translate(
1601 from.0.animate(&to.0, procedure)?,
1602 from.1.animate(&to.1, procedure)?,
1603 from.2.animate(&to.2, procedure)?,
1604 ))
1605 },
1606 }
1607 }
1608}
1609
1610impl ComputeSquaredDistance for ComputedTranslate {
1611 #[inline]
1612 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1613 let (from, to) = (self.resolve(), other.resolve());
1614 Ok(from.0.compute_squared_distance(&to.0)?
1615 + from.1.compute_squared_distance(&to.1)?
1616 + from.2.compute_squared_distance(&to.2)?)
1617 }
1618}
1619
1620impl ComputedScale {
1622 fn resolve(&self) -> (Number, Number, Number) {
1623 match *self {
1628 Scale::None => (1.0, 1.0, 1.0),
1629 Scale::Scale(sx, sy, sz) => (sx, sy, sz),
1630 }
1631 }
1632}
1633
1634impl Animate for ComputedScale {
1635 #[inline]
1636 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
1637 match (self, other) {
1638 (&Scale::None, &Scale::None) => Ok(Scale::None),
1639 (&Scale::Scale(_, ..), _) | (_, &Scale::Scale(_, ..)) => {
1640 let (from, to) = (self.resolve(), other.resolve());
1641 if procedure == Procedure::Add {
1646 return Ok(Scale::Scale(from.0 * to.0, from.1 * to.1, from.2 * to.2));
1648 }
1649 Ok(Scale::Scale(
1650 animate_multiplicative_factor(from.0, to.0, procedure)?,
1651 animate_multiplicative_factor(from.1, to.1, procedure)?,
1652 animate_multiplicative_factor(from.2, to.2, procedure)?,
1653 ))
1654 },
1655 }
1656 }
1657}
1658
1659impl ComputeSquaredDistance for ComputedScale {
1660 #[inline]
1661 fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
1662 let (from, to) = (self.resolve(), other.resolve());
1663 Ok(from.0.compute_squared_distance(&to.0)?
1664 + from.1.compute_squared_distance(&to.1)?
1665 + from.2.compute_squared_distance(&to.2)?)
1666 }
1667}