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