mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Implement interpolation of Quaternions
So that we can reuse the code in ComputedRotate.
This commit is contained in:
parent
174f5f7128
commit
9a62c0bf02
1 changed files with 71 additions and 82 deletions
|
@ -1696,7 +1696,7 @@ pub struct Perspective(f32, f32, f32, f32);
|
|||
pub struct Quaternion(f64, f64, f64, f64);
|
||||
|
||||
/// A decomposed 3d matrix.
|
||||
#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]
|
||||
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
||||
pub struct MatrixDecomposed3D {
|
||||
/// A translation function.
|
||||
|
@ -1739,6 +1739,76 @@ impl Quaternion {
|
|||
}
|
||||
}
|
||||
|
||||
impl Animate for Quaternion {
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
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");
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
let clamped_w = self.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;
|
||||
% for i in range(3):
|
||||
scaled_self.${i} *= scale;
|
||||
% endfor
|
||||
scaled_self.3 = theta.cos();
|
||||
|
||||
// Multiply scaled-self by other.
|
||||
let a = &scaled_self;
|
||||
let b = other;
|
||||
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,
|
||||
));
|
||||
}
|
||||
|
||||
let mut product = self.0 * other.0 +
|
||||
self.1 * other.1 +
|
||||
self.2 * other.2 +
|
||||
self.3 * other.3;
|
||||
|
||||
// Clamp product to -1.0 <= product <= 1.0
|
||||
product = product.min(1.0);
|
||||
product = product.max(-1.0);
|
||||
|
||||
if product == 1.0 {
|
||||
return Ok(*self);
|
||||
}
|
||||
|
||||
let theta = product.acos();
|
||||
let w = (other_weight * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
|
||||
|
||||
let mut a = *self;
|
||||
let mut b = *other;
|
||||
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
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputeSquaredDistance for Quaternion {
|
||||
#[inline]
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||
|
@ -2002,87 +2072,6 @@ impl Animate for Perspective {
|
|||
}
|
||||
}
|
||||
|
||||
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, ()> {
|
||||
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)
|
||||
}
|
||||
|
||||
let clamped_w = self.quaternion.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;
|
||||
% for i in range(3):
|
||||
scaled_self.quaternion.${i} *= scale;
|
||||
% endfor
|
||||
scaled_self.quaternion.3 = theta.cos();
|
||||
|
||||
// Multiply scaled-self by other.
|
||||
let a = &scaled_self.quaternion;
|
||||
let b = &other.quaternion;
|
||||
sum.quaternion = 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;
|
||||
|
||||
// Clamp product to -1.0 <= product <= 1.0
|
||||
product = product.min(1.0);
|
||||
product = product.max(-1.0);
|
||||
|
||||
if product == 1.0 {
|
||||
return Ok(sum);
|
||||
}
|
||||
|
||||
let theta = product.acos();
|
||||
let w = (other_weight * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Ok(sum)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MatrixDecomposed3D> for Matrix3D {
|
||||
/// Recompose a 3D matrix.
|
||||
/// <https://drafts.csswg.org/css-transforms/#recomposing-to-a-3d-matrix>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue