Clean up transform animations

This commit is contained in:
Anthony Ramine 2017-08-19 12:32:21 +02:00
parent 181f41ed37
commit baf3597477
2 changed files with 288 additions and 281 deletions

View file

@ -14,6 +14,7 @@ use euclid::{Point2D, Point3D, Size2D};
#[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID; #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
#[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI}; #[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
#[cfg(feature = "gecko")] use gecko_string_cache::Atom; #[cfg(feature = "gecko")] use gecko_string_cache::Atom;
use itertools::{EitherOrBoth, Itertools};
use properties::{CSSWideKeyword, PropertyDeclaration}; use properties::{CSSWideKeyword, PropertyDeclaration};
use properties::longhands; use properties::longhands;
use properties::longhands::background_size::computed_value::T as BackgroundSizeList; use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
@ -30,6 +31,7 @@ use properties::longhands::visibility::computed_value::T as Visibility;
#[cfg(feature = "gecko")] use properties::{ShorthandId}; #[cfg(feature = "gecko")] use properties::{ShorthandId};
use selectors::parser::SelectorParseError; use selectors::parser::SelectorParseError;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::borrow::Cow;
use std::cmp; use std::cmp;
#[cfg(feature = "gecko")] use fnv::FnvHashMap; #[cfg(feature = "gecko")] use fnv::FnvHashMap;
use style_traits::ParseError; use style_traits::ParseError;
@ -1288,62 +1290,39 @@ impl ToAnimatedZero for ClipRect {
fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) } fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
} }
/// Check if it's possible to do a direct numerical interpolation
/// between these two transform lists.
/// http://dev.w3.org/csswg/css-transforms/#transform-transform-animation
fn can_interpolate_list(from_list: &[TransformOperation],
to_list: &[TransformOperation]) -> bool {
// Lists must be equal length
if from_list.len() != to_list.len() {
return false;
}
// Each transform operation must match primitive type in other list
for (from, to) in from_list.iter().zip(to_list) {
match (from, to) {
(&TransformOperation::Matrix(..), &TransformOperation::Matrix(..)) |
(&TransformOperation::Skew(..), &TransformOperation::Skew(..)) |
(&TransformOperation::Translate(..), &TransformOperation::Translate(..)) |
(&TransformOperation::Scale(..), &TransformOperation::Scale(..)) |
(&TransformOperation::Rotate(..), &TransformOperation::Rotate(..)) |
(&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => {}
_ => {
return false;
}
}
}
true
}
/// Build an equivalent 'identity transform function list' based /// Build an equivalent 'identity transform function list' based
/// on an existing transform list. /// on an existing transform list.
/// http://dev.w3.org/csswg/css-transforms/#none-transform-animation /// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOperation> { impl ToAnimatedZero for TransformOperation {
let mut result = vec!(); fn to_animated_zero(&self) -> Result<Self, ()> {
match *self {
for operation in list {
match *operation {
TransformOperation::Matrix(..) => { TransformOperation::Matrix(..) => {
let identity = ComputedMatrix::identity(); Ok(TransformOperation::Matrix(ComputedMatrix::identity()))
result.push(TransformOperation::Matrix(identity)); },
} TransformOperation::MatrixWithPercents(..) => {
TransformOperation::MatrixWithPercents(..) => {} // FIXME(nox): Should be MatrixWithPercents value.
TransformOperation::Skew(..) => { Ok(TransformOperation::Matrix(ComputedMatrix::identity()))
result.push(TransformOperation::Skew(Angle::zero(), Angle::zero())) },
} TransformOperation::Skew(sx, sy) => {
TransformOperation::Translate(..) => { Ok(TransformOperation::Skew(
result.push(TransformOperation::Translate(LengthOrPercentage::zero(), sx.to_animated_zero()?,
LengthOrPercentage::zero(), sy.to_animated_zero()?,
Au(0))); ))
} },
TransformOperation::Translate(ref tx, ref ty, ref tz) => {
Ok(TransformOperation::Translate(
tx.to_animated_zero()?,
ty.to_animated_zero()?,
tz.to_animated_zero()?,
))
},
TransformOperation::Scale(..) => { TransformOperation::Scale(..) => {
result.push(TransformOperation::Scale(1.0, 1.0, 1.0)); Ok(TransformOperation::Scale(1.0, 1.0, 1.0))
} },
TransformOperation::Rotate(x, y, z, a) => { TransformOperation::Rotate(x, y, z, a) => {
let (x, y, z, _) = get_normalized_vector_and_angle(x, y, z, a); let (x, y, z, _) = get_normalized_vector_and_angle(x, y, z, a);
result.push(TransformOperation::Rotate(x, y, z, Angle::zero())); Ok(TransformOperation::Rotate(x, y, z, Angle::zero()))
} },
TransformOperation::Perspective(..) | TransformOperation::Perspective(..) |
TransformOperation::AccumulateMatrix { .. } | TransformOperation::AccumulateMatrix { .. } |
TransformOperation::InterpolateMatrix { .. } => { TransformOperation::InterpolateMatrix { .. } => {
@ -1355,13 +1334,10 @@ fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOp
// //
// Therefore, we use an identity matrix to represent the identity transform list. // Therefore, we use an identity matrix to represent the identity transform list.
// http://dev.w3.org/csswg/css-transforms/#identity-transform-function // http://dev.w3.org/csswg/css-transforms/#identity-transform-function
let identity = ComputedMatrix::identity(); Ok(TransformOperation::Matrix(ComputedMatrix::identity()))
result.push(TransformOperation::Matrix(identity)); },
}
} }
} }
result
} }
/// A wrapper for calling add_weighted that interpolates the distance of the two values from /// A wrapper for calling add_weighted that interpolates the distance of the two values from
@ -1379,99 +1355,88 @@ fn add_weighted_with_initial_val<T: Animatable>(a: &T,
result.add_weighted(&initial_val, 1.0, 1.0) result.add_weighted(&initial_val, 1.0, 1.0)
} }
/// Add two transform lists.
/// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms /// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
fn add_weighted_transform_lists(from_list: &[TransformOperation], impl Animatable for TransformOperation {
to_list: &[TransformOperation], fn add_weighted(
self_portion: f64, &self,
other_portion: f64) -> TransformList { other: &Self,
let mut result = vec![]; self_portion: f64,
other_portion: f64,
if can_interpolate_list(from_list, to_list) { ) -> Result<Self, ()> {
for (from, to) in from_list.iter().zip(to_list) { match (self, other) {
match (from, to) { (
(&TransformOperation::Matrix(from), &TransformOperation::Matrix(ref this),
&TransformOperation::Matrix(_to)) => { &TransformOperation::Matrix(ref other),
let sum = from.add_weighted(&_to, self_portion, other_portion).unwrap(); ) => {
result.push(TransformOperation::Matrix(sum)); Ok(TransformOperation::Matrix(
this.add_weighted(other, self_portion, other_portion)?,
))
},
(
&TransformOperation::Skew(ref fx, ref fy),
&TransformOperation::Skew(ref tx, ref ty),
) => {
Ok(TransformOperation::Skew(
fx.add_weighted(tx, self_portion, other_portion)?,
fy.add_weighted(ty, self_portion, other_portion)?,
))
},
(
&TransformOperation::Translate(ref fx, ref fy, ref fz),
&TransformOperation::Translate(ref tx, ref ty, ref tz),
) => {
Ok(TransformOperation::Translate(
fx.add_weighted(tx, self_portion, other_portion)?,
fy.add_weighted(ty, self_portion, other_portion)?,
fz.add_weighted(tz, self_portion, other_portion)?,
))
},
(
&TransformOperation::Scale(ref fx, ref fy, ref fz),
&TransformOperation::Scale(ref tx, ref ty, ref tz),
) => {
Ok(TransformOperation::Scale(
add_weighted_with_initial_val(fx, tx, self_portion, other_portion, &1.0)?,
add_weighted_with_initial_val(fy, ty, self_portion, other_portion, &1.0)?,
add_weighted_with_initial_val(fz, tz, self_portion, other_portion, &1.0)?,
))
},
(
&TransformOperation::Rotate(fx, fy, fz, fa),
&TransformOperation::Rotate(tx, ty, tz, ta),
) => {
let (fx, fy, fz, fa) = get_normalized_vector_and_angle(fx, fy, fz, fa);
let (tx, ty, tz, ta) = get_normalized_vector_and_angle(tx, ty, tz, ta);
if (fx, fy, fz) == (tx, ty, tz) {
let ia = fa.add_weighted(&ta, self_portion, other_portion)?;
Ok(TransformOperation::Rotate(fx, fy, fz, ia))
} else {
let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
Ok(TransformOperation::Matrix(
matrix_f.add_weighted(&matrix_t, self_portion, other_portion)?
))
} }
(&TransformOperation::MatrixWithPercents(_), },
&TransformOperation::MatrixWithPercents(_)) => { (
// We don't add_weighted `-moz-transform` matrices yet. &TransformOperation::Perspective(ref fd),
// They contain percentage values. &TransformOperation::Perspective(ref 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();
} }
(&TransformOperation::Skew(fx, fy), if td.0 > 0 {
&TransformOperation::Skew(tx, ty)) => { td_matrix.m34 = -1. / td.to_f32_px();
let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap();
let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap();
result.push(TransformOperation::Skew(ix, iy));
} }
(&TransformOperation::Translate(fx, fy, fz), Ok(TransformOperation::Matrix(
&TransformOperation::Translate(tx, ty, tz)) => { fd_matrix.add_weighted(&td_matrix, self_portion, other_portion)?,
let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap(); ))
let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap(); },
let iz = fz.add_weighted(&tz, self_portion, other_portion).unwrap(); _ => Err(()),
result.push(TransformOperation::Translate(ix, iy, iz));
}
(&TransformOperation::Scale(fx, fy, fz),
&TransformOperation::Scale(tx, ty, tz)) => {
let ix = add_weighted_with_initial_val(&fx, &tx, self_portion,
other_portion, &1.0).unwrap();
let iy = add_weighted_with_initial_val(&fy, &ty, self_portion,
other_portion, &1.0).unwrap();
let iz = add_weighted_with_initial_val(&fz, &tz, self_portion,
other_portion, &1.0).unwrap();
result.push(TransformOperation::Scale(ix, iy, iz));
}
(&TransformOperation::Rotate(fx, fy, fz, fa),
&TransformOperation::Rotate(tx, ty, tz, ta)) => {
let (fx, fy, fz, fa) = get_normalized_vector_and_angle(fx, fy, fz, fa);
let (tx, ty, tz, ta) = get_normalized_vector_and_angle(tx, ty, tz, ta);
if (fx, fy, fz) == (tx, ty, tz) {
let ia = fa.add_weighted(&ta, self_portion, other_portion).unwrap();
result.push(TransformOperation::Rotate(fx, fy, fz, ia));
} else {
let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
let sum = matrix_f.add_weighted(&matrix_t, self_portion, other_portion)
.unwrap();
result.push(TransformOperation::Matrix(sum));
}
}
(&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();
}
let sum = fd_matrix.add_weighted(&td_matrix, self_portion, other_portion)
.unwrap();
result.push(TransformOperation::Matrix(sum));
}
_ => {
// This should be unreachable due to the can_interpolate_list() call.
unreachable!();
}
}
} }
} else {
let from_transform_list = TransformList(Some(from_list.to_vec()));
let to_transform_list = TransformList(Some(to_list.to_vec()));
result.push(
TransformOperation::InterpolateMatrix { from_list: from_transform_list,
to_list: to_transform_list,
progress: Percentage(other_portion as f32) });
} }
TransformList(Some(result))
} }
/// https://www.w3.org/TR/css-transforms-1/#Rotate3dDefined /// https://www.w3.org/TR/css-transforms-1/#Rotate3dDefined
@ -2420,151 +2385,178 @@ impl ComputedMatrix {
impl Animatable for TransformList { impl Animatable for TransformList {
#[inline] #[inline]
fn add_weighted(&self, other: &TransformList, self_portion: f64, other_portion: f64) -> Result<Self, ()> { fn add_weighted(&self, other: &TransformList, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms let result = self.animate_with_similar_list(
let result = match (&self.0, &other.0) { other,
(&Some(ref from_list), &Some(ref to_list)) => { |this, other| this.add_weighted(other, self_portion, other_portion),
// Two lists of transforms );
add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion) let (this, other) = match result {
} Ok(list) => return Ok(list),
(&Some(ref from_list), &None) => { Err(None) => return Err(()),
// http://dev.w3.org/csswg/css-transforms/#none-transform-animation Err(Some(pair)) => pair,
let to_list = build_identity_transform_list(from_list);
add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion)
}
(&None, &Some(ref to_list)) => {
// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
let from_list = build_identity_transform_list(to_list);
add_weighted_transform_lists(&from_list, to_list, self_portion, other_portion)
}
_ => {
// http://dev.w3.org/csswg/css-transforms/#none-none-animation
TransformList(None)
}
}; };
Ok(TransformList(Some(vec![TransformOperation::InterpolateMatrix {
Ok(result) from_list: this,
to_list: other,
progress: Percentage(other_portion as f32),
}])))
} }
fn add(&self, other: &Self) -> Result<Self, ()> { fn add(&self, other: &Self) -> Result<Self, ()> {
match (&self.0, &other.0) { let this = self.0.as_ref().map_or(&[][..], |l| l);
(&Some(ref from_list), &Some(ref to_list)) => { let other = other.0.as_ref().map_or(&[][..], |l| l);
Ok(TransformList(Some([&from_list[..], &to_list[..]].concat()))) let result = this.iter().chain(other).cloned().collect::<Vec<_>>();
} Ok(TransformList(if result.is_empty() {
(&Some(_), &None) => { None
Ok(self.clone()) } else {
} Some(result)
(&None, &Some(_)) => { }))
Ok(other.clone())
}
_ => {
Ok(TransformList(None))
}
}
} }
#[inline] #[inline]
fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> { fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
match (&self.0, &other.0) { let result = self.animate_with_similar_list(
(&Some(ref from_list), &Some(ref to_list)) => { other,
if can_interpolate_list(from_list, to_list) { |this, other| this.accumulate(other, count),
Ok(add_weighted_transform_lists(from_list, &to_list, count as f64, 1.0)) );
} else { let (this, other) = match result {
use std::i32; Ok(list) => return Ok(list),
let result = vec![TransformOperation::AccumulateMatrix { Err(None) => return Err(()),
from_list: self.clone(), Err(Some(pair)) => pair,
to_list: other.clone(), };
count: cmp::min(count, i32::MAX as u64) as i32
}]; Ok(TransformList(Some(vec![TransformOperation::AccumulateMatrix {
Ok(TransformList(Some(result))) from_list: this,
to_list: other,
count: cmp::min(count, i32::max_value() as u64) as i32,
}])))
}
}
impl TransformList {
fn animate_with_similar_list<F>(
&self,
other: &Self,
animate: F,
) -> Result<Self, Option<(TransformList, TransformList)>>
where
F: Fn(&TransformOperation, &TransformOperation) -> Result<TransformOperation, ()>,
{
if self.0.is_none() && other.0.is_none() {
return Ok(TransformList(None));
}
let this = if self.0.is_some() {
Cow::Borrowed(self)
} else {
Cow::Owned(other.to_animated_zero().map_err(|_| None)?)
};
let other = if other.0.is_some() {
Cow::Borrowed(other)
} else {
Cow::Owned(self.to_animated_zero().map_err(|_| None)?)
};
{
let this = (*this).0.as_ref().map_or(&[][..], |l| l);
let other = (*other).0.as_ref().map_or(&[][..], |l| l);
if this.len() == other.len() {
let result = this.iter().zip(other).map(|(this, other)| {
animate(this, other)
}).collect::<Result<Vec<_>, _>>();
if let Ok(list) = result {
return Ok(TransformList(if list.is_empty() {
None
} else {
Some(list)
}));
} }
} }
(&Some(ref from_list), &None) => {
Ok(add_weighted_transform_lists(from_list, from_list, count as f64, 0.0))
}
(&None, &Some(_)) => {
// If |self| is 'none' then we are calculating:
//
// none * |count| + |other|
// = none + |other|
// = |other|
//
// Hence the result is just |other|.
Ok(other.clone())
}
_ => {
Ok(TransformList(None))
}
} }
Err(Some((this.into_owned(), other.into_owned())))
} }
} }
/// A helper function to retrieve the pixel length and percentage value.
fn extract_pixel_calc_value(lop: &LengthOrPercentage) -> (f64, CSSFloat) {
match lop {
&LengthOrPercentage::Length(au) => (au.to_f64_px(), 0.),
&LengthOrPercentage::Percentage(percent) => (0., percent.0),
&LengthOrPercentage::Calc(calc) => (calc.length().to_f64_px(), calc.percentage())
}
}
/// Compute the squared distance of two transform lists.
// This might not be the most useful definition of distance. It might be better, for example, // This might not be the most useful definition of distance. It might be better, for example,
// to trace the distance travelled by a point as its transform is interpolated between the two // to trace the distance travelled by a point as its transform is interpolated between the two
// lists. That, however, proves to be quite complicated so we take a simple approach for now. // lists. That, however, proves to be quite complicated so we take a simple approach for now.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1318591#c0. // See https://bugzilla.mozilla.org/show_bug.cgi?id=1318591#c0.
fn compute_transform_lists_squared_distance(from_list: &[TransformOperation], impl ComputeSquaredDistance for TransformOperation {
to_list: &[TransformOperation]) fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
-> Result<SquaredDistance, ()> { match (self, other) {
let zero_distance = SquaredDistance::Value(0.); (
let squared_distance = from_list.iter().zip(to_list.iter()).map(|(from, to)| { &TransformOperation::Matrix(ref this),
match (from, to) { &TransformOperation::Matrix(ref other),
(&TransformOperation::Matrix(from), ) => {
&TransformOperation::Matrix(to)) => { this.compute_squared_distance(other)
from.compute_squared_distance(&to).unwrap_or(zero_distance) },
} (
(&TransformOperation::Skew(fx, fy), &TransformOperation::Skew(ref fx, ref fy),
&TransformOperation::Skew(tx, ty)) => { &TransformOperation::Skew(ref tx, ref ty),
fx.compute_squared_distance(&tx).unwrap_or(zero_distance) + ) => {
fy.compute_squared_distance(&ty).unwrap_or(zero_distance) Ok(
} fx.compute_squared_distance(&tx)? +
(&TransformOperation::Translate(fx, fy, fz), fy.compute_squared_distance(&ty)?,
&TransformOperation::Translate(tx, ty, tz)) => { )
},
(
&TransformOperation::Translate(ref fx, ref fy, ref fz),
&TransformOperation::Translate(ref tx, ref ty, ref tz),
) => {
// We don't want to require doing layout in order to calculate the result, so // We don't want to require doing layout in order to calculate the result, so
// drop the percentage part. However, dropping percentage makes us impossible to // drop the percentage part. However, dropping percentage makes us impossible to
// compute the distance for the percentage-percentage case, but Gecko uses the // compute the distance for the percentage-percentage case, but Gecko uses the
// same formula, so it's fine for now. // same formula, so it's fine for now.
// Note: We use pixel value to compute the distance for translate, so we have to // Note: We use pixel value to compute the distance for translate, so we have to
// convert Au into px. // convert Au into px.
let diff_x = fx.add_weighted(&tx, 1., -1.).unwrap_or(LengthOrPercentage::zero()); let extract_pixel_length = |lop: &LengthOrPercentage| {
let diff_y = fy.add_weighted(&ty, 1., -1.).unwrap_or(LengthOrPercentage::zero()); match *lop {
let (diff_x_length, _) = extract_pixel_calc_value(&diff_x); LengthOrPercentage::Length(au) => au.to_f64_px(),
let (diff_y_length, _) = extract_pixel_calc_value(&diff_y); LengthOrPercentage::Percentage(_) => 0.,
SquaredDistance::Value(diff_x_length * diff_x_length) + LengthOrPercentage::Calc(calc) => calc.length().to_f64_px(),
SquaredDistance::Value(diff_y_length * diff_y_length) + }
fz.to_f64_px().compute_squared_distance(&tz.to_f64_px()).unwrap_or(zero_distance) };
}
(&TransformOperation::Scale(fx, fy, fz), let fx = extract_pixel_length(&fx);
&TransformOperation::Scale(tx, ty, tz)) => { let fy = extract_pixel_length(&fy);
fx.compute_squared_distance(&tx).unwrap_or(zero_distance) + let tx = extract_pixel_length(&tx);
fy.compute_squared_distance(&ty).unwrap_or(zero_distance) + let ty = extract_pixel_length(&ty);
fz.compute_squared_distance(&tz).unwrap_or(zero_distance)
} Ok(
(&TransformOperation::Rotate(fx, fy, fz, fa), fx.compute_squared_distance(&tx)? +
&TransformOperation::Rotate(tx, ty, tz, ta)) => { fy.compute_squared_distance(&ty)? +
fz.to_f64_px().compute_squared_distance(&tz.to_f64_px())?,
)
},
(
&TransformOperation::Scale(ref fx, ref fy, ref fz),
&TransformOperation::Scale(ref tx, ref ty, ref tz),
) => {
Ok(
fx.compute_squared_distance(&tx)? +
fy.compute_squared_distance(&ty)? +
fz.compute_squared_distance(&tz)?,
)
},
(
&TransformOperation::Rotate(fx, fy, fz, fa),
&TransformOperation::Rotate(tx, ty, tz, ta),
) => {
let (fx, fy, fz, angle1) = get_normalized_vector_and_angle(fx, fy, fz, fa); let (fx, fy, fz, angle1) = get_normalized_vector_and_angle(fx, fy, fz, fa);
let (tx, ty, tz, angle2) = get_normalized_vector_and_angle(tx, ty, tz, ta); let (tx, ty, tz, angle2) = get_normalized_vector_and_angle(tx, ty, tz, ta);
if (fx, fy, fz) == (tx, ty, tz) { if (fx, fy, fz) == (tx, ty, tz) {
angle1.compute_squared_distance(&angle2).unwrap_or(zero_distance) angle1.compute_squared_distance(&angle2)
} else { } else {
let v1 = DirectionVector::new(fx, fy, fz); let v1 = DirectionVector::new(fx, fy, fz);
let v2 = DirectionVector::new(tx, ty, tz); let v2 = DirectionVector::new(tx, ty, tz);
let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64()); let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64());
let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64()); let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64());
q1.compute_squared_distance(&q2).unwrap_or(zero_distance) q1.compute_squared_distance(&q2)
} }
} }
(&TransformOperation::Perspective(fd), (
&TransformOperation::Perspective(td)) => { &TransformOperation::Perspective(ref fd),
&TransformOperation::Perspective(ref td),
) => {
let mut fd_matrix = ComputedMatrix::identity(); let mut fd_matrix = ComputedMatrix::identity();
let mut td_matrix = ComputedMatrix::identity(); let mut td_matrix = ComputedMatrix::identity();
if fd.0 > 0 { if fd.0 > 0 {
@ -2574,52 +2566,56 @@ fn compute_transform_lists_squared_distance(from_list: &[TransformOperation],
if td.0 > 0 { if td.0 > 0 {
td_matrix.m34 = -1. / td.to_f32_px(); td_matrix.m34 = -1. / td.to_f32_px();
} }
fd_matrix.compute_squared_distance(&td_matrix).unwrap_or(zero_distance) fd_matrix.compute_squared_distance(&td_matrix)
} }
(&TransformOperation::Perspective(p), &TransformOperation::Matrix(m)) | (
(&TransformOperation::Matrix(m), &TransformOperation::Perspective(p)) => { &TransformOperation::Perspective(ref p),
&TransformOperation::Matrix(ref m),
) | (
&TransformOperation::Matrix(ref m),
&TransformOperation::Perspective(ref p),
) => {
let mut p_matrix = ComputedMatrix::identity(); let mut p_matrix = ComputedMatrix::identity();
if p.0 > 0 { if p.0 > 0 {
p_matrix.m34 = -1. / p.to_f32_px(); p_matrix.m34 = -1. / p.to_f32_px();
} }
p_matrix.compute_squared_distance(&m).unwrap_or(zero_distance) p_matrix.compute_squared_distance(&m)
}
_ => {
// We don't support computation of distance for InterpolateMatrix and
// AccumulateMatrix.
zero_distance
} }
_ => Err(()),
} }
}).sum(); }
Ok(squared_distance)
} }
impl ComputeSquaredDistance for TransformList { impl ComputeSquaredDistance for TransformList {
#[inline] #[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
match (self.0.as_ref(), other.0.as_ref()) { let this = self.0.as_ref().map_or(&[][..], |l| l);
(Some(from_list), Some(to_list)) => { let other = other.0.as_ref().map_or(&[][..], |l| l);
if can_interpolate_list(from_list, to_list) {
compute_transform_lists_squared_distance(from_list, to_list) this.iter().zip_longest(other).map(|it| {
} else { match it {
// Bug 1390039: we don't handle mismatch transform lists for now. EitherOrBoth::Both(this, other) => {
Err(()) this.compute_squared_distance(other)
} },
}, EitherOrBoth::Left(list) | EitherOrBoth::Right(list) => {
(Some(list), None) | (None, Some(list)) => { list.to_animated_zero()?.compute_squared_distance(list)
let none = build_identity_transform_list(list); },
compute_transform_lists_squared_distance(list, &none)
} }
_ => Ok(SquaredDistance::Value(0.)) }).sum()
}
} }
} }
impl ToAnimatedZero for TransformList { impl ToAnimatedZero for TransformList {
#[inline] #[inline]
fn to_animated_zero(&self) -> Result<Self, ()> { fn to_animated_zero(&self) -> Result<Self, ()> {
Ok(TransformList(None)) match self.0 {
None => Ok(TransformList(None)),
Some(ref list) => {
Ok(TransformList(Some(
list.iter().map(|op| op.to_animated_zero()).collect::<Result<Vec<_>, _>>()?
)))
},
}
} }
} }
@ -3092,8 +3088,6 @@ impl Animatable for AnimatedFilterList {
impl ComputeSquaredDistance for AnimatedFilterList { impl ComputeSquaredDistance for AnimatedFilterList {
#[inline] #[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
use itertools::{EitherOrBoth, Itertools};
self.0.iter().zip_longest(other.0.iter()).map(|it| { self.0.iter().zip_longest(other.0.iter()).map(|it| {
match it { match it {
EitherOrBoth::Both(from, to) => { EitherOrBoth::Both(from, to) => {

View file

@ -9,6 +9,7 @@ use std::{f32, f64, fmt};
use std::f64::consts::PI; use std::f64::consts::PI;
use style_traits::ToCss; use style_traits::ToCss;
use values::CSSFloat; use values::CSSFloat;
use values::animated::ToAnimatedZero;
use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::distance::{ComputeSquaredDistance, SquaredDistance};
/// A computed angle. /// A computed angle.
@ -87,6 +88,18 @@ impl Animatable for Angle {
} }
} }
impl ToAnimatedZero for Angle {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
match *self {
Angle::Degree(value) => Ok(Angle::Degree(value.to_animated_zero()?)),
Angle::Gradian(value) => Ok(Angle::Gradian(value.to_animated_zero()?)),
Angle::Radian(value) => Ok(Angle::Radian(value.to_animated_zero()?)),
Angle::Turn(value) => Ok(Angle::Turn(value.to_animated_zero()?)),
}
}
}
impl ComputeSquaredDistance for Angle { impl ComputeSquaredDistance for Angle {
#[inline] #[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {