mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Clean up transform animations
This commit is contained in:
parent
181f41ed37
commit
baf3597477
2 changed files with 288 additions and 281 deletions
|
@ -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) => {
|
||||||
|
|
|
@ -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, ()> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue