From 5ee93b28cb0f2ecd819a6e54c18367ad0e0fa355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Wed, 7 Sep 2016 01:23:00 +0300 Subject: [PATCH] Move decompose and recompose functions to impl and implement interpolations --- .../helpers/animated_properties.mako.rs | 302 ++++++++++-------- 1 file changed, 177 insertions(+), 125 deletions(-) diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 0f5c9f8f1996..d15e4d282960 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -21,7 +21,6 @@ use properties::longhands::z_index::computed_value::T as ZIndex; use std::cmp; use std::fmt; use super::ComputedValues; -use values::CSSFloat; use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; use values::computed::{BorderRadiusSize, LengthOrNone}; use values::computed::{CalcLengthOrPercentage, LengthOrPercentage}; @@ -624,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. @@ -702,8 +703,9 @@ 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)); + // TODO: It doesn't yet handle the case where one of the matrices are not 2D + let interpolated = from.interpolate(&_to, time).unwrap(); + result.push(TransformOperation::Matrix(interpolated)); } (&TransformOperation::Skew(fx, fy), &TransformOperation::Skew(tx, ty)) => { @@ -735,14 +737,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. @@ -758,9 +767,38 @@ 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 rad = a.radians(); + let sc = (rad / 2.0).sin() * (rad / 2.0).cos(); + let sq = 1.0 / 2.0 * (1.0 - (rad).cos()); + + 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 DecomposedMatrix { + pub struct InnerMatrix2D { pub m11: CSSFloat, pub m12: CSSFloat, pub m21: CSSFloat, pub m22: CSSFloat, } @@ -779,16 +817,16 @@ impl Interpolate for LengthOrNone { pub translate: Translate2D, pub scale: Scale2D, pub angle: f32, - pub matrix: DecomposedMatrix, + pub matrix: InnerMatrix2D, } - impl Interpolate for DecomposedMatrix { + impl Interpolate for InnerMatrix2D { fn interpolate(&self, other: &Self, time: f64) -> Result { - Ok(DecomposedMatrix { - m11: self.m11.interpolate(&other.m11, time).unwrap(), - m12: self.m12.interpolate(&other.m12, time).unwrap(), - m21: self.m21.interpolate(&other.m21, time).unwrap(), - m22: self.m22.interpolate(&other.m22, time).unwrap(), + 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)), }) } } @@ -796,8 +834,8 @@ impl Interpolate for LengthOrNone { impl Interpolate for Translate2D { fn interpolate(&self, other: &Self, time: f64) -> Result { Ok(Translate2D( - self.0.interpolate(&other.0, time).unwrap(), - self.1.interpolate(&other.1, time).unwrap() + try!(self.0.interpolate(&other.0, time)), + try!(self.1.interpolate(&other.1, time)) )) } } @@ -805,8 +843,8 @@ impl Interpolate for LengthOrNone { impl Interpolate for Scale2D { fn interpolate(&self, other: &Self, time: f64) -> Result { Ok(Scale2D( - self.0.interpolate(&other.0, time).unwrap(), - self.1.interpolate(&other.1, time).unwrap() + try!(self.0.interpolate(&other.0, time)), + try!(self.1.interpolate(&other.1, time)) )) } } @@ -816,9 +854,9 @@ impl Interpolate for LengthOrNone { fn interpolate(&self, other: &Self, time: f64) -> Result { // If x-axis of one is flipped, and y-axis of the other, // convert to an unflipped rotation. - let mut scale = self.scale.clone(); - let mut angle = self.angle.clone(); - let mut other_angle = other.angle.clone(); + 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; @@ -843,124 +881,138 @@ impl Interpolate for LengthOrNone { } // Interpolate all values. - let translate = self.translate.interpolate(&other.translate, time); - let scale = scale.interpolate(&other.scale, time); - let angle = angle.interpolate(&other_angle, time); - let matrix = self.matrix.interpolate(&other.matrix, time); + 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.unwrap(), - scale: scale.unwrap(), - angle: angle.unwrap(), - matrix: matrix.unwrap(), + translate: translate, + scale: scale, + angle: angle, + matrix: matrix, }) } } - /// Decompose a matrix. - /// https://drafts.csswg.org/css-transforms/#decomposing-a-2d-matrix - fn decompose_matrix(matrix: ComputedMatrix) -> MatrixDecomposed2D { - let mut row0x = matrix.m11; - let mut row0y = matrix.m12; - let mut row1x = matrix.m21; - let mut row1y = matrix.m22; + impl Interpolate for ComputedMatrix { + fn interpolate(&self, other: &Self, time: f64) -> Result { + 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)) + } + } - let translate = Translate2D(matrix.m41, matrix.m42); - let mut scale = Scale2D((row0x * row0x + row0y * row0y).sqrt(), - (row1x * row1x + row1y * row1y).sqrt()); + impl From for MatrixDecomposed2D { + /// Decompose a 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; + } + } - // 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; } - } - // 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; + } - // 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, + }; - let m = DecomposedMatrix { - 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, + } + } + } - // Convert into degrees because our rotation functions expect it. - angle = angle.to_degrees(); - MatrixDecomposed2D { - translate: translate, - scale: scale, - angle: angle, - matrix: m, - } - } - - /// https://drafts.csswg.org/css-transforms/#recomposing-to-a-2d-matrix - fn recompose_matrix(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 * decomposed.matrix.m11 + - decomposed.translate.1 * decomposed.matrix.m21; - computed_matrix.m42 = decomposed.translate.0 * decomposed.matrix.m11 + - decomposed.translate.1 * decomposed.matrix.m21; - - // 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; - - let matrix_clone = computed_matrix.clone(); - // Multiplication of computed_matrix and rotate_matrix - % for i in range(1, 5): - % for j in range(1, 5): - computed_matrix.m${i}${j} = (matrix_clone.m${i}1 * rotate_matrix.m1${j}) + - (matrix_clone.m${i}2 *rotate_matrix.m2${j}) + - (matrix_clone.m${i}3 * rotate_matrix.m3${j}) + - (matrix_clone.m${i}4 * rotate_matrix.m4${j}); + impl From for ComputedMatrix { + /// Recompose a 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 * decomposed.matrix.m11 + + decomposed.translate.1 * decomposed.matrix.m21; + computed_matrix.m42 = decomposed.translate.0 * decomposed.matrix.m12 + + decomposed.translate.1 * decomposed.matrix.m22; + + // 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; + + let matrix_clone = computed_matrix; + // Multiplication of computed_matrix and rotate_matrix + % for i in range(1, 5): + % for j in range(1, 5): + computed_matrix.m${i}${j} = (matrix_clone.m${i}1 * rotate_matrix.m1${j}) + + (matrix_clone.m${i}2 * rotate_matrix.m2${j}) + + (matrix_clone.m${i}3 * rotate_matrix.m3${j}) + + (matrix_clone.m${i}4 * rotate_matrix.m4${j}); + % endfor % endfor - % endfor - // 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 + // 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 + } } /// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms