Bug 1207734 - Part 8.a. (stylo) Implement MatrixDecomposed3D::slerp.
So that we can reuse the code in ComputedRotate.
MozReview-Commit-ID: 2uVO4Sf0at1
--- 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>