aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/style/properties/helpers/animated_properties.mako.rs750
1 files changed, 744 insertions, 6 deletions
diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs
index 0c7c0f73300..384e5436976 100644
--- a/components/style/properties/helpers/animated_properties.mako.rs
+++ b/components/style/properties/helpers/animated_properties.mako.rs
@@ -623,6 +623,8 @@ impl Interpolate for LengthOrNone {
use properties::longhands::transform::computed_value::ComputedMatrix;
use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
use properties::longhands::transform::computed_value::T as TransformList;
+ use values::CSSFloat;
+ use values::specified::Angle as SpecifiedAngle;
/// Check if it's possible to do a direct numerical interpolation
/// between these two transform lists.
@@ -701,8 +703,8 @@ impl Interpolate for LengthOrNone {
match (from, to) {
(&TransformOperation::Matrix(from),
&TransformOperation::Matrix(_to)) => {
- // TODO(gw): Implement matrix decomposition and interpolation
- result.push(TransformOperation::Matrix(from));
+ let interpolated = from.interpolate(&_to, time).unwrap();
+ result.push(TransformOperation::Matrix(interpolated));
}
(&TransformOperation::Skew(fx, fy),
&TransformOperation::Skew(tx, ty)) => {
@@ -734,14 +736,21 @@ impl Interpolate for LengthOrNone {
let ia = fa.interpolate(&ta, time).unwrap();
result.push(TransformOperation::Rotate(fx, fy, fz, ia));
} else {
- // TODO(gw): Implement matrix decomposition and interpolation
- result.push(TransformOperation::Rotate(fx, fy, fz, fa));
+ let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
+ let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
+ let interpolated = matrix_f.interpolate(&matrix_t, time).unwrap();
+
+ result.push(TransformOperation::Matrix(interpolated));
}
}
(&TransformOperation::Perspective(fd),
&TransformOperation::Perspective(_td)) => {
- // TODO(gw): Implement matrix decomposition and interpolation
- result.push(TransformOperation::Perspective(fd));
+ let mut fd_matrix = ComputedMatrix::identity();
+ let mut td_matrix = ComputedMatrix::identity();
+ fd_matrix.m43 = -1. / fd.to_f32_px();
+ td_matrix.m43 = -1. / _td.to_f32_px();
+ let interpolated = fd_matrix.interpolate(&td_matrix, time).unwrap();
+ result.push(TransformOperation::Matrix(interpolated));
}
_ => {
// This should be unreachable due to the can_interpolate_list() call.
@@ -757,6 +766,735 @@ impl Interpolate for LengthOrNone {
TransformList(Some(result))
}
+ /// https://drafts.csswg.org/css-transforms/#Rotate3dDefined
+ fn rotate_to_matrix(x: f32, y: f32, z: f32, a: SpecifiedAngle) -> ComputedMatrix {
+ let half_rad = a.radians() / 2.0;
+ let sc = (half_rad).sin() * (half_rad).cos();
+ let sq = (half_rad).sin().powi(2);
+
+ ComputedMatrix {
+ m11: 1.0 - 2.0 * (y * y + z * z) * sq,
+ m12: 2.0 * (x * y * sq - z * sc),
+ m13: 2.0 * (x * z * sq + y * sc),
+ m14: 0.0,
+
+ m21: 2.0 * (x * y * sq + z * sc),
+ m22: 1.0 - 2.0 * (x * x + z * z) * sq,
+ m23: 2.0 * (y * z * sq - x * sc),
+ m24: 0.0,
+
+ m31: 2.0 * (x * z * sq - y * sc),
+ m32: 2.0 * (y * z * sq + x * sc),
+ m33: 1.0 - 2.0 * (x * x + y * y) * sq,
+ m34: 0.0,
+
+ m41: 0.0,
+ m42: 0.0,
+ m43: 0.0,
+ m44: 1.0
+ }
+ }
+
+ #[derive(Clone, Copy, Debug)]
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+ pub struct InnerMatrix2D {
+ pub m11: CSSFloat, pub m12: CSSFloat,
+ pub m21: CSSFloat, pub m22: CSSFloat,
+ }
+
+ #[derive(Clone, Copy, Debug)]
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+ pub struct Translate2D(f32, f32);
+
+ #[derive(Clone, Copy, Debug)]
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+ pub struct Scale2D(f32, f32);
+
+ #[derive(Clone, Copy, Debug)]
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+ pub struct MatrixDecomposed2D {
+ pub translate: Translate2D,
+ pub scale: Scale2D,
+ pub angle: f32,
+ pub matrix: InnerMatrix2D,
+ }
+
+ impl Interpolate for InnerMatrix2D {
+ fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
+ Ok(InnerMatrix2D {
+ m11: try!(self.m11.interpolate(&other.m11, time)),
+ m12: try!(self.m12.interpolate(&other.m12, time)),
+ m21: try!(self.m21.interpolate(&other.m21, time)),
+ m22: try!(self.m22.interpolate(&other.m22, time)),
+ })
+ }
+ }
+
+ impl Interpolate for Translate2D {
+ fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
+ Ok(Translate2D(
+ try!(self.0.interpolate(&other.0, time)),
+ try!(self.1.interpolate(&other.1, time))
+ ))
+ }
+ }
+
+ impl Interpolate for Scale2D {
+ fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
+ Ok(Scale2D(
+ try!(self.0.interpolate(&other.0, time)),
+ try!(self.1.interpolate(&other.1, time))
+ ))
+ }
+ }
+
+ impl Interpolate for MatrixDecomposed2D {
+ /// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-2d-matrix-values
+ fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
+ // If x-axis of one is flipped, and y-axis of the other,
+ // convert to an unflipped rotation.
+ let mut scale = self.scale;
+ let mut angle = self.angle;
+ let mut other_angle = other.angle;
+ if (scale.0 < 0.0 && other.scale.1 < 0.0) || (scale.1 < 0.0 && other.scale.0 < 0.0) {
+ scale.0 = -scale.0;
+ scale.1 = -scale.1;
+ angle += if angle < 0.0 {180.} else {-180.};
+ }
+
+ // Don't rotate the long way around.
+ if angle == 0.0 {
+ angle = 360.
+ }
+ if other_angle == 0.0 {
+ other_angle = 360.
+ }
+
+ if (angle - other_angle).abs() > 180. {
+ if angle > other_angle {
+ angle -= 360.
+ }
+ else{
+ other_angle -= 360.
+ }
+ }
+
+ // Interpolate all values.
+ let translate = try!(self.translate.interpolate(&other.translate, time));
+ let scale = try!(scale.interpolate(&other.scale, time));
+ let angle = try!(angle.interpolate(&other_angle, time));
+ let matrix = try!(self.matrix.interpolate(&other.matrix, time));
+
+ Ok(MatrixDecomposed2D {
+ translate: translate,
+ scale: scale,
+ angle: angle,
+ matrix: matrix,
+ })
+ }
+ }
+
+ impl Interpolate for ComputedMatrix {
+ fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
+ if self.is_3d() || other.is_3d() {
+ let decomposed_from = decompose_3d_matrix(*self);
+ let decomposed_to = decompose_3d_matrix(*other);
+ match (decomposed_from, decomposed_to) {
+ (Ok(from), Ok(to)) => {
+ let interpolated = try!(from.interpolate(&to, time));
+ Ok(ComputedMatrix::from(interpolated))
+ },
+ _ => {
+ let interpolated = if time < 0.5 {*self} else {*other};
+ Ok(interpolated)
+ }
+ }
+ } else {
+ let decomposed_from = MatrixDecomposed2D::from(*self);
+ let decomposed_to = MatrixDecomposed2D::from(*other);
+ let interpolated = try!(decomposed_from.interpolate(&decomposed_to, time));
+ Ok(ComputedMatrix::from(interpolated))
+ }
+ }
+ }
+
+ impl From<ComputedMatrix> for MatrixDecomposed2D {
+ /// Decompose a 2D matrix.
+ /// https://drafts.csswg.org/css-transforms/#decomposing-a-2d-matrix
+ fn from(matrix: ComputedMatrix) -> MatrixDecomposed2D {
+ let mut row0x = matrix.m11;
+ let mut row0y = matrix.m12;
+ let mut row1x = matrix.m21;
+ let mut row1y = matrix.m22;
+
+ let translate = Translate2D(matrix.m41, matrix.m42);
+ let mut scale = Scale2D((row0x * row0x + row0y * row0y).sqrt(),
+ (row1x * row1x + row1y * row1y).sqrt());
+
+ // If determinant is negative, one axis was flipped.
+ let determinant = row0x * row1y - row0y * row1x;
+ if determinant < 0. {
+ if row0x < row1y {
+ scale.0 = -scale.0;
+ } else {
+ scale.1 = -scale.1;
+ }
+ }
+
+ // Renormalize matrix to remove scale.
+ if scale.0 != 0.0 {
+ row0x *= 1. / scale.0;
+ row0y *= 1. / scale.0;
+ }
+ if scale.1 != 0.0 {
+ row1x *= 1. / scale.1;
+ row1y *= 1. / scale.1;
+ }
+
+ // Compute rotation and renormalize matrix.
+ let mut angle = row0y.atan2(row0x);
+ if angle != 0.0 {
+ let sn = -row0y;
+ let cs = row0x;
+ let m11 = row0x;
+ let m12 = row0y;
+ let m21 = row1x;
+ let m22 = row1y;
+ row0x = cs * m11 + sn * m21;
+ row0y = cs * m12 + sn * m22;
+ row1x = -sn * m11 + cs * m21;
+ row1y = -sn * m12 + cs * m22;
+ }
+
+ let m = InnerMatrix2D {
+ m11: row0x, m12: row0y,
+ m21: row1x, m22: row1y,
+ };
+
+ // Convert into degrees because our rotation functions expect it.
+ angle = angle.to_degrees();
+ MatrixDecomposed2D {
+ translate: translate,
+ scale: scale,
+ angle: angle,
+ matrix: m,
+ }
+ }
+ }
+
+ impl From<MatrixDecomposed2D> for ComputedMatrix {
+ /// Recompose a 2D matrix.
+ /// https://drafts.csswg.org/css-transforms/#recomposing-to-a-2d-matrix
+ fn from(decomposed: MatrixDecomposed2D) -> ComputedMatrix {
+ let mut computed_matrix = ComputedMatrix::identity();
+ computed_matrix.m11 = decomposed.matrix.m11;
+ computed_matrix.m12 = decomposed.matrix.m12;
+ computed_matrix.m21 = decomposed.matrix.m21;
+ computed_matrix.m22 = decomposed.matrix.m22;
+
+ // Translate matrix.
+ computed_matrix.m41 = decomposed.translate.0;
+ computed_matrix.m42 = decomposed.translate.1;
+
+ // Rotate matrix.
+ let angle = decomposed.angle.to_radians();
+ let cos_angle = angle.cos();
+ let sin_angle = angle.sin();
+
+ let mut rotate_matrix = ComputedMatrix::identity();
+ rotate_matrix.m11 = cos_angle;
+ rotate_matrix.m12 = sin_angle;
+ rotate_matrix.m21 = -sin_angle;
+ rotate_matrix.m22 = cos_angle;
+
+ // Multiplication of computed_matrix and rotate_matrix
+ computed_matrix = multiply(rotate_matrix, computed_matrix);
+
+ // Scale matrix.
+ computed_matrix.m11 *= decomposed.scale.0;
+ computed_matrix.m12 *= decomposed.scale.0;
+ computed_matrix.m21 *= decomposed.scale.1;
+ computed_matrix.m22 *= decomposed.scale.1;
+ computed_matrix
+ }
+ }
+
+ #[derive(Clone, Copy, Debug)]
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+ pub struct Translate3D(f32, f32, f32);
+
+ #[derive(Clone, Copy, Debug)]
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+ pub struct Scale3D(f32, f32, f32);
+
+ #[derive(Clone, Copy, Debug)]
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+ pub struct Skew(f32, f32, f32);
+
+ #[derive(Clone, Copy, Debug)]
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+ pub struct Perspective(f32, f32, f32, f32);
+
+ #[derive(Clone, Copy, Debug)]
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+ pub struct Quaternion(f32, f32, f32, f32);
+
+ #[derive(Clone, Copy, Debug)]
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+ pub struct MatrixDecomposed3D {
+ pub translate: Translate3D,
+ pub scale: Scale3D,
+ pub skew: Skew,
+ pub perspective: Perspective,
+ pub quaternion: Quaternion,
+ }
+
+ /// Decompose a 3D matrix.
+ /// https://drafts.csswg.org/css-transforms/#decomposing-a-3d-matrix
+ fn decompose_3d_matrix(mut matrix: ComputedMatrix) -> Result<MatrixDecomposed3D, ()> {
+ // Normalize the matrix.
+ if matrix.m44 == 0.0 {
+ return Err(());
+ }
+
+ let scaling_factor = matrix.m44;
+ % for i in range(1, 5):
+ % for j in range(1, 5):
+ matrix.m${i}${j} /= scaling_factor;
+ % endfor
+ % endfor
+
+ // perspective_matrix is used to solve for perspective, but it also provides
+ // an easy way to test for singularity of the upper 3x3 component.
+ let mut perspective_matrix = matrix;
+
+ % for i in range(1, 4):
+ perspective_matrix.m${i}4 = 0.0;
+ % endfor
+ perspective_matrix.m44 = 1.0;
+
+ if perspective_matrix.determinant() == 0.0 {
+ return Err(());
+ }
+
+ // First, isolate perspective.
+ let perspective = if matrix.m14 != 0.0 || matrix.m24 != 0.0 || matrix.m34 != 0.0 {
+ let right_hand_side: [f32; 4] = [
+ matrix.m14,
+ matrix.m24,
+ matrix.m34,
+ matrix.m44
+ ];
+
+ perspective_matrix = perspective_matrix.inverse().unwrap();
+
+ // Transpose perspective_matrix
+ perspective_matrix = ComputedMatrix {
+ % for i in range(1, 5):
+ % for j in range(1, 5):
+ m${i}${j}: perspective_matrix.m${j}${i},
+ % endfor
+ % endfor
+ };
+
+ // Multiply right_hand_side with perspective_matrix
+ let mut tmp: [f32; 4] = [0.0; 4];
+ % for i in range(1, 5):
+ tmp[${i - 1}] = (right_hand_side[0] * perspective_matrix.m1${i}) +
+ (right_hand_side[1] * perspective_matrix.m2${i}) +
+ (right_hand_side[2] * perspective_matrix.m3${i}) +
+ (right_hand_side[3] * perspective_matrix.m4${i});
+ % endfor
+
+ Perspective(tmp[0], tmp[1], tmp[2], tmp[3])
+ } else {
+ Perspective(0.0, 0.0, 0.0, 1.0)
+ };
+
+ // Next take care of translation
+ let translate = Translate3D (
+ matrix.m41,
+ matrix.m42,
+ matrix.m43
+ );
+
+ // Now get scale and shear. 'row' is a 3 element array of 3 component vectors
+ let mut row: [[f32; 3]; 3] = [[0.0; 3]; 3];
+ % for i in range(1, 4):
+ row[${i - 1}][0] = matrix.m${i}1;
+ row[${i - 1}][1] = matrix.m${i}2;
+ row[${i - 1}][2] = matrix.m${i}3;
+ % endfor
+
+ // Compute X scale factor and normalize first row.
+ let row0len = (row[0][0] * row[0][0] + row[0][1] * row[0][1] + row[0][2] * row[0][2]).sqrt();
+ let mut scale = Scale3D(row0len, 0.0, 0.0);
+ row[0] = [row[0][0] / row0len, row[0][1] / row0len, row[0][2] / row0len];
+
+ // Compute XY shear factor and make 2nd row orthogonal to 1st.
+ let mut skew = Skew(dot(row[0], row[1]), 0.0, 0.0);
+ row[1] = combine(row[1], row[0], 1.0, -skew.0);
+
+ // Now, compute Y scale and normalize 2nd row.
+ let row1len = (row[0][0] * row[0][0] + row[0][1] * row[0][1] + row[0][2] * row[0][2]).sqrt();
+ scale.1 = row1len;
+ row[1] = [row[1][0] / row1len, row[1][1] / row1len, row[1][2] / row1len];
+ skew.0 /= scale.1;
+
+ // Compute XZ and YZ shears, orthogonalize 3rd row
+ skew.1 = dot(row[0], row[2]);
+ row[2] = combine(row[2], row[0], 1.0, -skew.1);
+ skew.2 = dot(row[1], row[2]);
+ row[2] = combine(row[2], row[1], 1.0, -skew.2);
+
+ // Next, get Z scale and normalize 3rd row.
+ let row2len = (row[2][0] * row[2][0] + row[2][1] * row[2][1] + row[2][2] * row[2][2]).sqrt();
+ scale.2 = row2len;
+ row[2] = [row[2][0] / row2len, row[2][1] / row2len, row[2][2] / row2len];
+ skew.1 /= scale.2;
+ skew.2 /= scale.2;
+
+ // At this point, the matrix (in rows) is orthonormal.
+ // Check for a coordinate system flip. If the determinant
+ // is -1, then negate the matrix and the scaling factors.
+ let pdum3 = cross(row[1], row[2]);
+ if dot(row[0], pdum3) < 0.0 {
+ % for i in range(3):
+ scale.${i} *= -1.0;
+ row[${i}][0] *= -1.0;
+ row[${i}][1] *= -1.0;
+ row[${i}][2] *= -1.0;
+ % endfor
+ }
+
+ // Now, get the rotations out
+ let mut quaternion = Quaternion (
+ 0.5 * ((1.0 + row[0][0] - row[1][1] - row[2][2]).max(0.0)).sqrt(),
+ 0.5 * ((1.0 - row[0][0] + row[1][1] - row[2][2]).max(0.0)).sqrt(),
+ 0.5 * ((1.0 - row[0][0] - row[1][1] + row[2][2]).max(0.0)).sqrt(),
+ 0.5 * ((1.0 + row[0][0] + row[1][1] + row[2][2]).max(0.0)).sqrt()
+ );
+
+ if row[2][1] > row[1][2] {
+ quaternion.0 = -quaternion.0
+ }
+ if row[0][2] > row[2][0] {
+ quaternion.1 = -quaternion.1
+ }
+ if row[1][0] > row[0][1] {
+ quaternion.2 = -quaternion.2
+ }
+
+ Ok(MatrixDecomposed3D {
+ translate: translate,
+ scale: scale,
+ skew: skew,
+ perspective: perspective,
+ quaternion: quaternion
+ })
+ }
+
+ // Combine 2 point.
+ fn combine(a: [f32; 3], b: [f32; 3], ascl: f32, bscl: f32) -> [f32; 3] {
+ [
+ (ascl * a[0]) + (bscl * b[0]),
+ (ascl * a[1]) + (bscl * b[1]),
+ (ascl * a[2]) + (bscl * b[2])
+ ]
+ }
+
+ // Dot product.
+ fn dot(a: [f32; 3], b: [f32; 3]) -> f32 {
+ a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
+ }
+
+ // Cross product.
+ fn cross(row1: [f32; 3], row2: [f32; 3]) -> [f32; 3] {
+ [
+ row1[1] * row2[2] - row1[2] * row2[1],
+ row1[2] * row2[0] - row1[0] * row2[2],
+ row1[0] * row2[1] - row1[1] * row2[0]
+ ]
+ }
+
+ impl Interpolate for Translate3D {
+ fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
+ Ok(Translate3D(
+ try!(self.0.interpolate(&other.0, time)),
+ try!(self.1.interpolate(&other.1, time)),
+ try!(self.2.interpolate(&other.2, time))
+ ))
+ }
+ }
+
+ impl Interpolate for Scale3D {
+ fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
+ Ok(Scale3D(
+ try!(self.0.interpolate(&other.0, time)),
+ try!(self.1.interpolate(&other.1, time)),
+ try!(self.2.interpolate(&other.2, time))
+ ))
+ }
+ }
+
+ impl Interpolate for Skew {
+ fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
+ Ok(Skew(
+ try!(self.0.interpolate(&other.0, time)),
+ try!(self.1.interpolate(&other.1, time)),
+ try!(self.2.interpolate(&other.2, time))
+ ))
+ }
+ }
+
+ impl Interpolate for Perspective {
+ fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
+ Ok(Perspective(
+ try!(self.0.interpolate(&other.0, time)),
+ try!(self.1.interpolate(&other.1, time)),
+ try!(self.2.interpolate(&other.2, time)),
+ try!(self.3.interpolate(&other.3, time))
+ ))
+ }
+ }
+
+ impl Interpolate for MatrixDecomposed3D {
+ /// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-3d-matrix-values
+ fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
+ let mut interpolated = *self;
+
+ // Interpolate translate, scale, skew and perspective components.
+ interpolated.translate = try!(self.translate.interpolate(&other.translate, time));
+ interpolated.scale = try!(self.scale.interpolate(&other.scale, time));
+ interpolated.skew = try!(self.skew.interpolate(&other.skew, time));
+ interpolated.perspective = try!(self.perspective.interpolate(&other.perspective, time));
+
+ // Interpolate quaternions using spherical linear interpolation (Slerp).
+ 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(interpolated);
+ }
+
+ let theta = product.acos();
+ let w = (time as f32 * 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} *= (time as f32 * theta).cos() - product * w;
+ b.quaternion.${i} *= w;
+ interpolated.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i};
+ % endfor
+
+ Ok(interpolated)
+ }
+ }
+
+ impl From<MatrixDecomposed3D> for ComputedMatrix {
+ /// Recompose a 3D matrix.
+ /// https://drafts.csswg.org/css-transforms/#recomposing-to-a-3d-matrix
+ fn from(decomposed: MatrixDecomposed3D) -> ComputedMatrix {
+ let mut matrix = ComputedMatrix::identity();
+
+ // Apply perspective
+ % for i in range(1, 5):
+ matrix.m${i}4 = decomposed.perspective.${i - 1};
+ % endfor
+
+ // Apply translation
+ % for i in range(1, 4):
+ % for j in range(1, 4):
+ matrix.m4${i} += decomposed.translate.${j - 1} * matrix.m${j}${i};
+ % endfor
+ % endfor
+
+ // Apply rotation
+ let x = decomposed.quaternion.0;
+ let y = decomposed.quaternion.1;
+ let z = decomposed.quaternion.2;
+ let w = decomposed.quaternion.3;
+
+ // Construct a composite rotation matrix from the quaternion values
+ // rotationMatrix is a identity 4x4 matrix initially
+ let mut rotation_matrix = ComputedMatrix::identity();
+ rotation_matrix.m11 = 1.0 - 2.0 * (y * y + z * z);
+ rotation_matrix.m12 = 2.0 * (x * y + z * w);
+ rotation_matrix.m13 = 2.0 * (x * z - y * w);
+ rotation_matrix.m21 = 2.0 * (x * y - z * w);
+ rotation_matrix.m22 = 1.0 - 2.0 * (x * x + z * z);
+ rotation_matrix.m23 = 2.0 * (y * z + x * w);
+ rotation_matrix.m31 = 2.0 * (x * z + y * w);
+ rotation_matrix.m32 = 2.0 * (y * z - x * w);
+ rotation_matrix.m33 = 1.0 - 2.0 * (x * x + y * y);
+
+ matrix = multiply(rotation_matrix, matrix);
+
+ // Apply skew
+ let mut temp = ComputedMatrix::identity();
+ if decomposed.skew.2 != 0.0 {
+ temp.m32 = decomposed.skew.2;
+ matrix = multiply(matrix, temp);
+ }
+
+ if decomposed.skew.1 != 0.0 {
+ temp.m32 = 0.0;
+ temp.m31 = decomposed.skew.1;
+ matrix = multiply(matrix, temp);
+ }
+
+ if decomposed.skew.0 != 0.0 {
+ temp.m31 = 0.0;
+ temp.m21 = decomposed.skew.0;
+ matrix = multiply(matrix, temp);
+ }
+
+ // Apply scale
+ % for i in range(1, 4):
+ % for j in range(1, 4):
+ matrix.m${i}${j} *= decomposed.scale.${i - 1};
+ % endfor
+ % endfor
+
+ matrix
+ }
+ }
+
+ // Multiplication of two 4x4 matrices.
+ fn multiply(a: ComputedMatrix, b: ComputedMatrix) -> ComputedMatrix {
+ let mut a_clone = a;
+ % for i in range(1, 5):
+ % for j in range(1, 5):
+ a_clone.m${i}${j} = (a.m${i}1 * b.m1${j}) +
+ (a.m${i}2 * b.m2${j}) +
+ (a.m${i}3 * b.m3${j}) +
+ (a.m${i}4 * b.m4${j});
+ % endfor
+ % endfor
+ a_clone
+ }
+
+ impl ComputedMatrix {
+ fn is_3d(&self) -> bool {
+ self.m13 != 0.0 || self.m14 != 0.0 ||
+ self.m23 != 0.0 || self.m24 != 0.0 ||
+ self.m31 != 0.0 || self.m32 != 0.0 || self.m33 != 1.0 || self.m34 != 0.0 ||
+ self.m43 != 0.0 || self.m44 != 1.0
+ }
+
+ pub fn determinant(&self) -> CSSFloat {
+ self.m14 * self.m23 * self.m32 * self.m41 -
+ self.m13 * self.m24 * self.m32 * self.m41 -
+ self.m14 * self.m22 * self.m33 * self.m41 +
+ self.m12 * self.m24 * self.m33 * self.m41 +
+ self.m13 * self.m22 * self.m34 * self.m41 -
+ self.m12 * self.m23 * self.m34 * self.m41 -
+ self.m14 * self.m23 * self.m31 * self.m42 +
+ self.m13 * self.m24 * self.m31 * self.m42 +
+ self.m14 * self.m21 * self.m33 * self.m42 -
+ self.m11 * self.m24 * self.m33 * self.m42 -
+ self.m13 * self.m21 * self.m34 * self.m42 +
+ self.m11 * self.m23 * self.m34 * self.m42 +
+ self.m14 * self.m22 * self.m31 * self.m43 -
+ self.m12 * self.m24 * self.m31 * self.m43 -
+ self.m14 * self.m21 * self.m32 * self.m43 +
+ self.m11 * self.m24 * self.m32 * self.m43 +
+ self.m12 * self.m21 * self.m34 * self.m43 -
+ self.m11 * self.m22 * self.m34 * self.m43 -
+ self.m13 * self.m22 * self.m31 * self.m44 +
+ self.m12 * self.m23 * self.m31 * self.m44 +
+ self.m13 * self.m21 * self.m32 * self.m44 -
+ self.m11 * self.m23 * self.m32 * self.m44 -
+ self.m12 * self.m21 * self.m33 * self.m44 +
+ self.m11 * self.m22 * self.m33 * self.m44
+ }
+
+ fn inverse(&self) -> Option<ComputedMatrix> {
+ let mut det = self.determinant();
+
+ if det == 0.0 {
+ return None;
+ }
+
+ det = 1.0 / det;
+ let x = ComputedMatrix {
+ m11: det *
+ (self.m23*self.m34*self.m42 - self.m24*self.m33*self.m42 +
+ self.m24*self.m32*self.m43 - self.m22*self.m34*self.m43 -
+ self.m23*self.m32*self.m44 + self.m22*self.m33*self.m44),
+ m12: det *
+ (self.m14*self.m33*self.m42 - self.m13*self.m34*self.m42 -
+ self.m14*self.m32*self.m43 + self.m12*self.m34*self.m43 +
+ self.m13*self.m32*self.m44 - self.m12*self.m33*self.m44),
+ m13: det *
+ (self.m13*self.m24*self.m42 - self.m14*self.m23*self.m42 +
+ self.m14*self.m22*self.m43 - self.m12*self.m24*self.m43 -
+ self.m13*self.m22*self.m44 + self.m12*self.m23*self.m44),
+ m14: det *
+ (self.m14*self.m23*self.m32 - self.m13*self.m24*self.m32 -
+ self.m14*self.m22*self.m33 + self.m12*self.m24*self.m33 +
+ self.m13*self.m22*self.m34 - self.m12*self.m23*self.m34),
+ m21: det *
+ (self.m24*self.m33*self.m41 - self.m23*self.m34*self.m41 -
+ self.m24*self.m31*self.m43 + self.m21*self.m34*self.m43 +
+ self.m23*self.m31*self.m44 - self.m21*self.m33*self.m44),
+ m22: det *
+ (self.m13*self.m34*self.m41 - self.m14*self.m33*self.m41 +
+ self.m14*self.m31*self.m43 - self.m11*self.m34*self.m43 -
+ self.m13*self.m31*self.m44 + self.m11*self.m33*self.m44),
+ m23: det *
+ (self.m14*self.m23*self.m41 - self.m13*self.m24*self.m41 -
+ self.m14*self.m21*self.m43 + self.m11*self.m24*self.m43 +
+ self.m13*self.m21*self.m44 - self.m11*self.m23*self.m44),
+ m24: det *
+ (self.m13*self.m24*self.m31 - self.m14*self.m23*self.m31 +
+ self.m14*self.m21*self.m33 - self.m11*self.m24*self.m33 -
+ self.m13*self.m21*self.m34 + self.m11*self.m23*self.m34),
+ m31: det *
+ (self.m22*self.m34*self.m41 - self.m24*self.m32*self.m41 +
+ self.m24*self.m31*self.m42 - self.m21*self.m34*self.m42 -
+ self.m22*self.m31*self.m44 + self.m21*self.m32*self.m44),
+ m32: det *
+ (self.m14*self.m32*self.m41 - self.m12*self.m34*self.m41 -
+ self.m14*self.m31*self.m42 + self.m11*self.m34*self.m42 +
+ self.m12*self.m31*self.m44 - self.m11*self.m32*self.m44),
+ m33: det *
+ (self.m12*self.m24*self.m41 - self.m14*self.m22*self.m41 +
+ self.m14*self.m21*self.m42 - self.m11*self.m24*self.m42 -
+ self.m12*self.m21*self.m44 + self.m11*self.m22*self.m44),
+ m34: det *
+ (self.m14*self.m22*self.m31 - self.m12*self.m24*self.m31 -
+ self.m14*self.m21*self.m32 + self.m11*self.m24*self.m32 +
+ self.m12*self.m21*self.m34 - self.m11*self.m22*self.m34),
+ m41: det *
+ (self.m23*self.m32*self.m41 - self.m22*self.m33*self.m41 -
+ self.m23*self.m31*self.m42 + self.m21*self.m33*self.m42 +
+ self.m22*self.m31*self.m43 - self.m21*self.m32*self.m43),
+ m42: det *
+ (self.m12*self.m33*self.m41 - self.m13*self.m32*self.m41 +
+ self.m13*self.m31*self.m42 - self.m11*self.m33*self.m42 -
+ self.m12*self.m31*self.m43 + self.m11*self.m32*self.m43),
+ m43: det *
+ (self.m13*self.m22*self.m41 - self.m12*self.m23*self.m41 -
+ self.m13*self.m21*self.m42 + self.m11*self.m23*self.m42 +
+ self.m12*self.m21*self.m43 - self.m11*self.m22*self.m43),
+ m44: det *
+ (self.m12*self.m23*self.m31 - self.m13*self.m22*self.m31 +
+ self.m13*self.m21*self.m32 - self.m11*self.m23*self.m32 -
+ self.m12*self.m21*self.m33 + self.m11*self.m22*self.m33),
+ };
+
+ Some(x)
+ }
+ }
+
/// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms
impl Interpolate for TransformList {
#[inline]