Skip to content

Commit

Permalink
Implement ComputeSquaredDistance for Matrix and Perspective.
Browse files Browse the repository at this point in the history
  • Loading branch information
BorisChiou committed Aug 17, 2017
1 parent 3a5cbfb commit e8fad23
Showing 1 changed file with 86 additions and 20 deletions.
106 changes: 86 additions & 20 deletions components/style/properties/helpers/animated_properties.mako.rs
Expand Up @@ -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);

Expand Down Expand Up @@ -1606,6 +1610,20 @@ impl Animatable for MatrixDecomposed2D {
}
}

impl ComputeSquaredDistance for MatrixDecomposed2D {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
// 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<Self, ()> {
if self.is_3d() || other.is_3d() {
Expand All @@ -1630,6 +1648,21 @@ impl Animatable for ComputedMatrix {
}
}

impl ComputeSquaredDistance for ComputedMatrix {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
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<ComputedMatrix> for MatrixDecomposed2D {
/// Decompose a 2D matrix.
/// https://drafts.csswg.org/css-transforms/#decomposing-a-2d-matrix
Expand Down Expand Up @@ -1754,12 +1787,12 @@ impl From<ComputedMatrix> 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);

Expand All @@ -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);

Expand All @@ -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.
Expand Down Expand Up @@ -1827,6 +1860,17 @@ impl Quaternion {
}
}

impl ComputeSquaredDistance for Quaternion {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
// 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]
Expand Down Expand Up @@ -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<SquaredDistance, ()> {
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<Self, ()> {
Ok(Perspective(
Expand Down Expand Up @@ -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)) => {
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit e8fad23

Please sign in to comment.