Bug 1207734 - Part 8.a. (stylo) Implement MatrixDecomposed3D::slerp. draft
authorcku <cku@mozilla.com>
Fri, 12 Jan 2018 18:01:51 +0800
changeset 720538 e9a3f3618006113350146d96253944c0480c3cc5
parent 720537 9a3ec102ae54029533c5bbffc264ae9583c34494
child 720539 a4b514cb4845620b27afb2899b5e70b048b27107
push id95571
push userbmo:cku@mozilla.com
push dateMon, 15 Jan 2018 18:35:41 +0000
bugs1207734
milestone59.0a1
Bug 1207734 - Part 8.a. (stylo) Implement MatrixDecomposed3D::slerp. So that we can reuse the code in ComputedRotate. MozReview-Commit-ID: 2uVO4Sf0at1
servo/components/style/properties/helpers/animated_properties.mako.rs
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -1994,92 +1994,99 @@ impl Animate for Perspective {
             self.0.animate(&other.0, procedure)?,
             self.1.animate(&other.1, procedure)?,
             self.2.animate(&other.2, procedure)?,
             animate_multiplicative_factor(self.3, other.3, procedure)?,
         ))
     }
 }
 
-impl Animate for MatrixDecomposed3D {
-    /// <https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-3d-matrix-values>
-    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+impl MatrixDecomposed3D {
+    fn slerp(from: &Quaternion, to: &Quaternion, procedure: Procedure) -> Result<Quaternion, ()> {
         use std::f64;
 
         let (this_weight, other_weight) = procedure.weights();
-
         debug_assert!((this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON ||
                       other_weight == 1.0f64 || other_weight == 0.0f64,
                       "animate should only be used for interpolating or accumulating transforms");
 
-        let mut sum = *self;
-
-        // Add translate, scale, skew and perspective components.
-        sum.translate = self.translate.animate(&other.translate, procedure)?;
-        sum.scale = self.scale.animate(&other.scale, procedure)?;
-        sum.skew = self.skew.animate(&other.skew, procedure)?;
-        sum.perspective = self.perspective.animate(&other.perspective, procedure)?;
-
-        // Add quaternions using spherical linear interpolation (Slerp).
-        //
         // We take a specialized code path for accumulation (where other_weight is 1)
         if other_weight == 1.0 {
             if this_weight == 0.0 {
-                return Ok(*other)
+                return Ok(*to);
             }
 
-            let clamped_w = self.quaternion.3.min(1.0).max(-1.0);
+            let clamped_w = from.3.min(1.0).max(-1.0);
 
             // Determine the scale factor.
             let mut theta = clamped_w.acos();
             let mut scale = if theta == 0.0 { 0.0 } else { 1.0 / theta.sin() };
             theta *= this_weight;
             scale *= theta.sin();
 
             // Scale the self matrix by this_weight.
-            let mut scaled_self = *self;
+            let mut scaled_from = *from;
             % for i in range(3):
-                scaled_self.quaternion.${i} *= scale;
+                scaled_from.${i} *= scale;
             % endfor
-            scaled_self.quaternion.3 = theta.cos();
+            scaled_from.3 = theta.cos();
 
             // Multiply scaled-self by other.
-            let a = &scaled_self.quaternion;
-            let b = &other.quaternion;
-            sum.quaternion = Quaternion(
+            let a = &scaled_from;
+            let b = to;
+            return Ok(Quaternion(
                 a.3 * b.0 + a.0 * b.3 + a.1 * b.2 - a.2 * b.1,
                 a.3 * b.1 - a.0 * b.2 + a.1 * b.3 + a.2 * b.0,
                 a.3 * b.2 + a.0 * b.1 - a.1 * b.0 + a.2 * b.3,
                 a.3 * b.3 - a.0 * b.0 - a.1 * b.1 - a.2 * b.2,
-            );
-        } else {
-            let mut product = self.quaternion.0 * other.quaternion.0 +
-                              self.quaternion.1 * other.quaternion.1 +
-                              self.quaternion.2 * other.quaternion.2 +
-                              self.quaternion.3 * other.quaternion.3;
+            ));
+        }
+
+        let mut product = from.0 * to.0 +
+                          from.1 * to.1 +
+                          from.2 * to.2 +
+                          from.3 * to.3;
 
-            // Clamp product to -1.0 <= product <= 1.0
-            product = product.min(1.0);
-            product = product.max(-1.0);
+        // Clamp product to -1.0 <= product <= 1.0
+        product = product.min(1.0);
+        product = product.max(-1.0);
+
+        if product == 1.0 {
+            return Ok(*from);
+        }
+
+        let theta = product.acos();
+        let w = (other_weight * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
 
-            if product == 1.0 {
-                return Ok(sum);
-            }
+        let mut a = *from;
+        let mut b = *to;
+        let mut result = Quaternion(0., 0., 0., 0.,);
+        % for i in range(4):
+            a.${i} *= (other_weight * theta).cos() - product * w;
+            b.${i} *= w;
+            result.${i} = a.${i} + b.${i};
+        % endfor
 
-            let theta = product.acos();
-            let w = (other_weight * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
+        Ok(result)
+    }
+}
 
-            let mut a = *self;
-            let mut b = *other;
-            % for i in range(4):
-                a.quaternion.${i} *= (other_weight * theta).cos() - product * w;
-                b.quaternion.${i} *= w;
-                sum.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i};
-            % endfor
-        }
+impl Animate for MatrixDecomposed3D {
+    /// <https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-3d-matrix-values>
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+        let mut sum = *self;
+
+        // Add translate, scale, skew and perspective components.
+        sum.translate = self.translate.animate(&other.translate, procedure)?;
+        sum.scale = self.scale.animate(&other.scale, procedure)?;
+        sum.skew = self.skew.animate(&other.skew, procedure)?;
+        sum.perspective = self.perspective.animate(&other.perspective, procedure)?;
+        sum.quaternion = MatrixDecomposed3D::slerp(&self.quaternion,
+                                                   &other.quaternion,
+                                                   procedure)?;
 
         Ok(sum)
     }
 }
 
 impl From<MatrixDecomposed3D> for Matrix3D {
     /// Recompose a 3D matrix.
     /// <https://drafts.csswg.org/css-transforms/#recomposing-to-a-3d-matrix>