diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 9d9f261496a..cd0d01a5d42 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -1497,21 +1497,25 @@ fn rotate_to_matrix(x: f32, y: f32, z: f32, a: Angle) -> ComputedMatrix { } /// A 2d matrix for interpolation. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] +// FIXME: We use custom derive for ComputeSquaredDistance. However, If possible, we should convert +// the InnerMatrix2D into types with physical meaning. This custom derive computes the squared +// distance from each matrix item, and this makes the result different from that in Gecko if we +// have skew factor in the ComputedMatrix. pub struct InnerMatrix2D { pub m11: CSSFloat, pub m12: CSSFloat, pub m21: CSSFloat, pub m22: CSSFloat, } /// A 2d translation function. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct Translate2D(f32, f32); /// A 2d scale function. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct Scale2D(f32, f32); @@ -1606,6 +1610,20 @@ impl Animatable for MatrixDecomposed2D { } } +impl ComputeSquaredDistance for MatrixDecomposed2D { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + // Use Radian to compute the distance. + const RAD_PER_DEG: f64 = ::std::f64::consts::PI / 180.0; + let angle1 = self.angle as f64 * RAD_PER_DEG; + let angle2 = other.angle as f64 * RAD_PER_DEG; + Ok(self.translate.compute_squared_distance(&other.translate)? + + self.scale.compute_squared_distance(&other.scale)? + + angle1.compute_squared_distance(&angle2)? + + self.matrix.compute_squared_distance(&other.matrix)?) + } +} + impl Animatable for ComputedMatrix { fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { if self.is_3d() || other.is_3d() { @@ -1630,6 +1648,21 @@ impl Animatable for ComputedMatrix { } } +impl ComputeSquaredDistance for ComputedMatrix { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + if self.is_3d() || other.is_3d() { + let from = decompose_3d_matrix(*self)?; + let to = decompose_3d_matrix(*other)?; + from.compute_squared_distance(&to) + } else { + let from = MatrixDecomposed2D::from(*self); + let to = MatrixDecomposed2D::from(*other); + from.compute_squared_distance(&to) + } + } +} + impl From for MatrixDecomposed2D { /// Decompose a 2D matrix. /// https://drafts.csswg.org/css-transforms/#decomposing-a-2d-matrix @@ -1754,12 +1787,12 @@ impl From for RawGeckoGfxMatrix4x4 { } /// A 3d translation. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct Translate3D(f32, f32, f32); /// A 3d scale function. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct Scale3D(f32, f32, f32); @@ -1769,7 +1802,7 @@ pub struct Scale3D(f32, f32, f32); pub struct Skew(f32, f32, f32); /// A 3d perspective transformation. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct Perspective(f32, f32, f32, f32); @@ -1779,7 +1812,7 @@ pub struct Perspective(f32, f32, f32, f32); pub struct Quaternion(f64, f64, f64, f64); /// A decomposed 3d matrix. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct MatrixDecomposed3D { /// A translation function. @@ -1827,6 +1860,17 @@ impl Quaternion { } } +impl ComputeSquaredDistance for Quaternion { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + // Use quaternion vectors to get the angle difference. Both q1 and q2 are unit vectors, + // so we can get their angle difference by: + // cos(theta/2) = (q1 dot q2) / (|q1| * |q2|) = q1 dot q2. + let distance = self.dot(other).max(-1.0).min(1.0).acos() * 2.0; + Ok(SquaredDistance::Value(distance * distance)) + } +} + impl DirectionVector { /// Create a DirectionVector. #[inline] @@ -2053,6 +2097,17 @@ impl Animatable for Skew { } } +impl ComputeSquaredDistance for Skew { + // We have to use atan() to convert the skew factors into skew angles, so implement + // ComputeSquaredDistance manually. + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + Ok(self.0.atan().compute_squared_distance(&other.0.atan())? + + self.1.atan().compute_squared_distance(&other.1.atan())? + + self.2.atan().compute_squared_distance(&other.2.atan())?) + } +} + impl Animatable for Perspective { fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { Ok(Perspective( @@ -2443,10 +2498,9 @@ fn compute_transform_lists_squared_distance(from_list: &[TransformOperation], let zero_distance = SquaredDistance::Value(0.); let squared_distance = from_list.iter().zip(to_list.iter()).map(|(from, to)| { match (from, to) { - (&TransformOperation::Matrix(_from), - &TransformOperation::Matrix(_to)) => { - // TODO: decompose matrix. - zero_distance + (&TransformOperation::Matrix(from), + &TransformOperation::Matrix(to)) => { + from.compute_squared_distance(&to).unwrap_or(zero_distance) } (&TransformOperation::Skew(fx, fy), &TransformOperation::Skew(tx, ty)) => { @@ -2494,19 +2548,31 @@ fn compute_transform_lists_squared_distance(from_list: &[TransformOperation], if vector1 == vector2 { angle1.compute_squared_distance(&angle2).unwrap_or(zero_distance) } else { - // Use quaternion vectors to get the angle difference. Both q1 and q2 - // are unit vectors, so we can get their angle difference by - // cos(theta/2) = (q1 dot q2) / (|q1| * |q2|) = q1 dot q2. let q1 = Quaternion::from_direction_and_angle(&vector1, angle1.radians64()); let q2 = Quaternion::from_direction_and_angle(&vector2, angle2.radians64()); - let dist = q1.dot(&q2).max(-1.).min(1.).acos() * 2.0; - SquaredDistance::Value(dist * dist) + q1.compute_squared_distance(&q2).unwrap_or(zero_distance) } } - (&TransformOperation::Perspective(_fd), - &TransformOperation::Perspective(_td)) => { - // TODO: decompose matrix. - zero_distance + (&TransformOperation::Perspective(fd), + &TransformOperation::Perspective(td)) => { + let mut fd_matrix = ComputedMatrix::identity(); + let mut td_matrix = ComputedMatrix::identity(); + if fd.0 > 0 { + fd_matrix.m34 = -1. / fd.to_f32_px(); + } + + if td.0 > 0 { + td_matrix.m34 = -1. / td.to_f32_px(); + } + fd_matrix.compute_squared_distance(&td_matrix).unwrap_or(zero_distance) + } + (&TransformOperation::Perspective(p), &TransformOperation::Matrix(m)) | + (&TransformOperation::Matrix(m), &TransformOperation::Perspective(p)) => { + let mut p_matrix = ComputedMatrix::identity(); + if p.0 > 0 { + p_matrix.m34 = -1. / p.to_f32_px(); + } + p_matrix.compute_squared_distance(&m).unwrap_or(zero_distance) } _ => { // We don't support computation of distance for InterpolateMatrix and