diff options
26 files changed, 545 insertions, 741 deletions
diff --git a/components/style/macros.rs b/components/style/macros.rs index d72ecc73c06..8af642f80da 100644 --- a/components/style/macros.rs +++ b/components/style/macros.rs @@ -84,7 +84,7 @@ macro_rules! define_keyword_type { ($name: ident, $css: expr) => { #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - #[derive(Clone, Copy, PartialEq, ToCss)] + #[derive(Clone, ComputeSquaredDistance, Copy, PartialEq, ToCss)] pub struct $name; impl $crate::properties::animated_properties::Animatable for $name { diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 82eeb653e16..e82ee073c87 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -119,8 +119,11 @@ use values::computed::ComputedVecIter; /// The computed value, effectively a list of single values. - #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + #[derive(Clone, Debug, PartialEq)] + % if need_animatable or animation_value_type == "ComputedValue": + #[derive(ComputeSquaredDistance)] + % endif pub struct T( % if allow_empty and allow_empty != "NotInitial": pub Vec<single_value::T>, @@ -142,16 +145,6 @@ fn add(&self, other: &Self) -> Result<Self, ()> { self.0.add(&other.0).map(T) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.0.compute_distance(&other.0) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - self.0.compute_squared_distance(&other.0) - } } impl ToAnimatedZero for T { @@ -956,63 +949,6 @@ %> </%def> -/// Macro for defining Animatable trait for tuple struct which has Option<T>, -/// e.g. struct T(pub Option<Au>). -<%def name="impl_animatable_for_option_tuple(value_for_none)"> - impl Animatable for T { - #[inline] - fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) - -> Result<Self, ()> { - match (self, other) { - (&T(Some(ref this)), &T(Some(ref other))) => { - Ok(T(this.add_weighted(other, self_portion, other_portion).ok())) - }, - (&T(Some(ref this)), &T(None)) => { - Ok(T(this.add_weighted(&${value_for_none}, self_portion, other_portion).ok())) - }, - (&T(None), &T(Some(ref other))) => { - Ok(T(${value_for_none}.add_weighted(other, self_portion, other_portion).ok())) - }, - (&T(None), &T(None)) => { - Ok(T(None)) - }, - } - } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&T(Some(ref this)), &T(Some(ref other))) => { - this.compute_distance(other) - }, - (&T(Some(ref value)), &T(None)) | - (&T(None), &T(Some(ref value)))=> { - value.compute_distance(&${value_for_none}) - }, - (&T(None), &T(None)) => { - Ok(0.0) - }, - } - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&T(Some(ref this)), &T(Some(ref other))) => { - this.compute_squared_distance(other) - }, - (&T(Some(ref value)), &T(None)) | - (&T(None), &T(Some(ref value))) => { - value.compute_squared_distance(&${value_for_none}) - }, - (&T(None), &T(None)) => { - Ok(0.0) - }, - } - } - } -</%def> - // Define property that supports prefixed intrinsic size keyword values for gecko. // E.g. -moz-max-content, -moz-min-content, etc. <%def name="gecko_size_type(name, length_type, initial_value, logical, **kwargs)"> diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 1b4755b4bb7..4b423751d6b 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -49,6 +49,7 @@ use values::computed::{LengthOrPercentage, MaxLength, MozLength, Percentage, ToC use values::computed::{NonNegativeAu, NonNegativeNumber, PositiveIntegerOrAuto}; use values::computed::length::{NonNegativeLengthOrAuto, NonNegativeLengthOrNormal}; use values::computed::length::NonNegativeLengthOrPercentage; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::{GreaterThanOrEqualToOne, NonNegative}; use values::generics::effects::Filter; use values::generics::position as generic_position; @@ -83,16 +84,6 @@ pub trait Animatable: Sized { fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> { self.add_weighted(other, count as f64, 1.0) } - - /// Compute distance between a value and another for a given property. - fn compute_distance(&self, _other: &Self) -> Result<f64, ()> { Err(()) } - - /// In order to compute the Euclidean distance of a list or property value with multiple - /// components, we need to compute squared distance for each element, so the vector can sum it - /// and then get its squared root as the distance. - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_distance(other).map(|d| d * d) - } } /// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list @@ -741,28 +732,31 @@ impl Animatable for AnimationValue { } } } +} - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { +impl ComputeSquaredDistance for AnimationValue { + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { match (self, other) { % for prop in data.longhands: - % if prop.animatable: - % if prop.animation_value_type != "discrete": - (&AnimationValue::${prop.camel_case}(ref from), - &AnimationValue::${prop.camel_case}(ref to)) => { - from.compute_distance(to) - }, - % else: - (&AnimationValue::${prop.camel_case}(ref _from), - &AnimationValue::${prop.camel_case}(ref _to)) => { - Err(()) - }, - % endif - % endif + % if prop.animatable: + % if prop.animation_value_type != "discrete": + (&AnimationValue::${prop.camel_case}(ref this), &AnimationValue::${prop.camel_case}(ref other)) => { + this.compute_squared_distance(other) + }, + % else: + (&AnimationValue::${prop.camel_case}(_), &AnimationValue::${prop.camel_case}(_)) => { + Err(()) + }, + % endif + % endif % endfor _ => { - panic!("Expected compute_distance of computed values of the same \ - property, got: {:?}, {:?}", self, other); - } + panic!( + "computed values should be of the same property, got: {:?}, {:?}", + self, + other + ); + }, } } } @@ -802,22 +796,21 @@ macro_rules! repeated_vec_impl { me.add_weighted(you, self_portion, other_portion) }).collect() } + } + impl<T> ComputeSquaredDistance for $ty + where + T: ComputeSquaredDistance + RepeatableListAnimatable, + { #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - // If the length of either list is zero, the least common multiple is undefined. - if cmp::min(self.len(), other.len()) < 1 { + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + if self.is_empty() || other.is_empty() { return Err(()); } use num_integer::lcm; let len = lcm(self.len(), other.len()); - self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(me, you)| { - me.compute_squared_distance(you) + self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(this, other)| { + this.compute_squared_distance(other) }).sum() } })* @@ -832,11 +825,6 @@ impl Animatable for Au { fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok(Au((self.0 as f64 * self_portion + other.0 as f64 * other_portion).round() as i32)) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.0.compute_distance(&other.0) - } } impl <T> Animatable for Option<T> @@ -852,28 +840,6 @@ impl <T> Animatable for Option<T> _ => Err(()), } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&Some(ref this), &Some(ref other)) => { - this.compute_distance(other) - }, - (&None, &None) => Ok(0.0), - _ => Err(()), - } - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&Some(ref this), &Some(ref other)) => { - this.compute_squared_distance(other) - }, - (&None, &None) => Ok(0.0), - _ => Err(()), - } - } } /// https://drafts.csswg.org/css-transitions/#animtype-number @@ -882,11 +848,6 @@ impl Animatable for f32 { fn add_weighted(&self, other: &f32, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok((*self as f64 * self_portion + *other as f64 * other_portion) as f32) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - Ok((*self - *other).abs() as f64) - } } /// https://drafts.csswg.org/css-transitions/#animtype-number @@ -895,11 +856,6 @@ impl Animatable for f64 { fn add_weighted(&self, other: &f64, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok(*self * self_portion + *other * other_portion) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - Ok((*self - *other).abs()) - } } /// https://drafts.csswg.org/css-transitions/#animtype-integer @@ -908,11 +864,6 @@ impl Animatable for i32 { fn add_weighted(&self, other: &i32, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok((*self as f64 * self_portion + *other as f64 * other_portion).round() as i32) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - Ok((*self - *other).abs() as f64) - } } /// https://drafts.csswg.org/css-transitions/#animtype-number @@ -934,13 +885,6 @@ impl Animatable for Angle { } } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - // Use the formula for calculating the distance between angles defined in SVG: - // https://www.w3.org/TR/SVG/animate.html#complexDistances - Ok((self.radians64() - other.radians64()).abs()) - } } /// https://drafts.csswg.org/css-transitions/#animtype-percentage @@ -949,11 +893,6 @@ impl Animatable for Percentage { fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { Ok(Percentage((self.0 as f64 * self_portion + other.0 as f64 * other_portion) as f32)) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - Ok((self.0 as f64 - other.0 as f64).abs()) - } } impl ToAnimatedZero for Percentage { @@ -977,14 +916,12 @@ impl Animatable for Visibility { _ => Err(()), } } +} +impl ComputeSquaredDistance for Visibility { #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - if *self == *other { - Ok(0.0) - } else { - Ok(1.0) - } + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + Ok(SquaredDistance::Value(if *self == *other { 0. } else { 1. })) } } @@ -1003,16 +940,6 @@ impl<T: Animatable + Copy> Animatable for Size2D<T> { Ok(Size2D::new(width, height)) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - Ok(self.compute_squared_distance(other)?.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - Ok(self.width.compute_squared_distance(&other.width)? + self.height.compute_squared_distance(&other.height)?) - } } impl<T: Animatable + Copy> Animatable for Point2D<T> { @@ -1044,15 +971,20 @@ impl Animatable for VerticalAlign { _ => Err(()), } } +} +impl ComputeSquaredDistance for VerticalAlign { #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (*self, *other) { - (VerticalAlign::LengthOrPercentage(ref this), - VerticalAlign::LengthOrPercentage(ref other)) => { - this.compute_distance(other) + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + match (self, other) { + (&VerticalAlign::LengthOrPercentage(ref this), &VerticalAlign::LengthOrPercentage(ref other)) => { + this.compute_squared_distance(other) + }, + _ => { + // FIXME(nox): Should this return `Ok(SquaredDistance::Value(0.))` + // if `self` and `other` are the same keyword value? + Err(()) }, - _ => Err(()), } } } @@ -1087,18 +1019,6 @@ impl Animatable for CalcLengthOrPercentage { let percentage = add_weighted_half(self.percentage, other.percentage, self_portion, other_portion)?; Ok(CalcLengthOrPercentage::with_clamping_mode(length, percentage, self.clamping_mode)) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(|sq| sq.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - let length_diff = (self.unclamped_length().0 - other.unclamped_length().0) as f64; - let percentage_diff = (self.percentage() - other.percentage()) as f64; - Ok(length_diff * length_diff + percentage_diff * percentage_diff) - } } /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc @@ -1131,48 +1051,6 @@ impl Animatable for LengthOrPercentage { } } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (*self, *other) { - (LengthOrPercentage::Length(ref this), - LengthOrPercentage::Length(ref other)) => { - this.compute_distance(other) - }, - (LengthOrPercentage::Percentage(ref this), - LengthOrPercentage::Percentage(ref other)) => { - this.compute_distance(other) - }, - (this, other) => { - let this: CalcLengthOrPercentage = From::from(this); - let other: CalcLengthOrPercentage = From::from(other); - this.compute_distance(&other) - } - } - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - match (*self, *other) { - (LengthOrPercentage::Length(ref this), - LengthOrPercentage::Length(ref other)) => { - let diff = (this.0 - other.0) as f64; - Ok(diff * diff) - }, - (LengthOrPercentage::Percentage(ref this), - LengthOrPercentage::Percentage(ref other)) => { - let diff = this.0 as f64 - other.0 as f64; - Ok(diff * diff) - }, - (this, other) => { - let this: CalcLengthOrPercentage = From::from(this); - let other: CalcLengthOrPercentage = From::from(other); - let length_diff = (this.unclamped_length().0 - other.unclamped_length().0) as f64; - let percentage_diff = (this.percentage() - other.percentage()) as f64; - Ok(length_diff * length_diff + percentage_diff * percentage_diff) - } - } - } } impl ToAnimatedZero for LengthOrPercentage { @@ -1210,53 +1088,6 @@ impl Animatable for LengthOrPercentageOrAuto { } } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (*self, *other) { - (LengthOrPercentageOrAuto::Length(ref this), - LengthOrPercentageOrAuto::Length(ref other)) => { - this.compute_distance(other) - }, - (LengthOrPercentageOrAuto::Percentage(ref this), - LengthOrPercentageOrAuto::Percentage(ref other)) => { - this.compute_distance(other) - }, - (this, other) => { - // If one of the element is Auto, Option<> will be None, and the returned distance is Err(()) - let this: Option<CalcLengthOrPercentage> = From::from(this); - let other: Option<CalcLengthOrPercentage> = From::from(other); - this.compute_distance(&other) - } - } - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - match (*self, *other) { - (LengthOrPercentageOrAuto::Length(ref this), - LengthOrPercentageOrAuto::Length(ref other)) => { - let diff = (this.0 - other.0) as f64; - Ok(diff * diff) - }, - (LengthOrPercentageOrAuto::Percentage(ref this), - LengthOrPercentageOrAuto::Percentage(ref other)) => { - let diff = this.0 as f64 - other.0 as f64; - Ok(diff * diff) - }, - (this, other) => { - let this: Option<CalcLengthOrPercentage> = From::from(this); - let other: Option<CalcLengthOrPercentage> = From::from(other); - if let (Some(this), Some(other)) = (this, other) { - let length_diff = (this.unclamped_length().0 - other.unclamped_length().0) as f64; - let percentage_diff = (this.percentage() - other.percentage()) as f64; - Ok(length_diff * length_diff + percentage_diff * percentage_diff) - } else { - Err(()) - } - } - } - } } impl ToAnimatedZero for LengthOrPercentageOrAuto { @@ -1301,26 +1132,6 @@ impl Animatable for LengthOrPercentageOrNone { }, } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (*self, *other) { - (LengthOrPercentageOrNone::Length(ref this), - LengthOrPercentageOrNone::Length(ref other)) => { - this.compute_distance(other) - }, - (LengthOrPercentageOrNone::Percentage(ref this), - LengthOrPercentageOrNone::Percentage(ref other)) => { - this.compute_distance(other) - }, - (this, other) => { - // If one of the element is Auto, Option<> will be None, and the returned distance is Err(()) - let this = <Option<CalcLengthOrPercentage>>::from(this); - let other = <Option<CalcLengthOrPercentage>>::from(other); - this.compute_distance(&other) - }, - } - } } impl ToAnimatedZero for LengthOrPercentageOrNone { @@ -1350,17 +1161,6 @@ impl Animatable for MozLength { _ => Err(()), } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (*self, *other) { - (MozLength::LengthOrPercentageOrAuto(ref this), - MozLength::LengthOrPercentageOrAuto(ref other)) => { - this.compute_distance(other) - }, - _ => Err(()), - } - } } impl ToAnimatedZero for MozLength { @@ -1388,17 +1188,6 @@ impl Animatable for MaxLength { _ => Err(()), } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (*self, *other) { - (MaxLength::LengthOrPercentageOrNone(ref this), - MaxLength::LengthOrPercentageOrNone(ref other)) => { - this.compute_distance(other) - }, - _ => Err(()), - } - } } impl ToAnimatedZero for MaxLength { @@ -1417,13 +1206,6 @@ impl Animatable for FontWeight { let weight = (weight.max(100.).min(900.) / 100.).round() * 100.; Ok(FontWeight(weight as u16)) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - let a = self.0 as f64; - let b = other.0 as f64; - a.compute_distance(&b) - } } impl ToAnimatedZero for FontWeight { @@ -1447,12 +1229,12 @@ impl Animatable for FontStretch { Ok(result.into()) } +} +impl ComputeSquaredDistance for FontStretch { #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - let from = f64::from(*self); - let to = f64::from(*other); - from.compute_distance(&to) + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + f64::from(*self).compute_squared_distance(&(*other).into()) } } @@ -1500,17 +1282,6 @@ impl<H: Animatable, V: Animatable> Animatable for generic_position::Position<H, vertical: self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?, }) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - Ok(self.horizontal.compute_squared_distance(&other.horizontal)? + - self.vertical.compute_squared_distance(&other.vertical)?) - } } impl<H, V> ToAnimatedZero for generic_position::Position<H, V> @@ -1542,22 +1313,6 @@ impl Animatable for ClipRect { left: self.left.add_weighted(&other.left, self_portion, other_portion)?, }) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - let list = [ - self.top.compute_distance(&other.top)?, - self.right.compute_distance(&other.right)?, - self.bottom.compute_distance(&other.bottom)?, - self.left.compute_distance(&other.left)? - ]; - Ok(list.iter().fold(0.0f64, |sum, diff| sum + diff * diff)) - } } impl ToAnimatedZero for ClipRect { @@ -2641,6 +2396,14 @@ impl Animatable for TransformList { } } +impl ComputeSquaredDistance for TransformList { + #[inline] + fn compute_squared_distance(&self, _other: &Self) -> Result<SquaredDistance, ()> { + // FIXME: This should be implemented. + Err(()) + } +} + impl ToAnimatedZero for TransformList { #[inline] fn to_animated_zero(&self) -> Result<Self, ()> { @@ -2666,32 +2429,6 @@ impl<T, U> Animatable for Either<T, U> } } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&Either::First(ref this), &Either::First(ref other)) => { - this.compute_distance(other) - }, - (&Either::Second(ref this), &Either::Second(ref other)) => { - this.compute_distance(other) - }, - _ => Err(()) - } - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&Either::First(ref this), &Either::First(ref other)) => { - this.compute_squared_distance(other) - }, - (&Either::Second(ref this), &Either::Second(ref other)) => { - this.compute_squared_distance(other) - }, - _ => Err(()) - } - } } impl<A, B> ToAnimatedZero for Either<A, B> @@ -2789,28 +2526,14 @@ impl Animatable for IntermediateRGBA { Ok(IntermediateRGBA::new(red, green, blue, alpha)) } } +} +impl ComputeSquaredDistance for IntermediateRGBA { #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(|sq| sq.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - let start = [ self.alpha, - self.red * self.alpha, - self.green * self.alpha, - self.blue * self.alpha ]; - let end = [ other.alpha, - other.red * other.alpha, - other.green * other.alpha, - other.blue * other.alpha ]; - let diff = start.iter().zip(&end) - .fold(0.0f64, |n, (&a, &b)| { - let diff = (a - b) as f64; - n + diff * diff - }); - Ok(diff) + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + let start = [ self.alpha, self.red * self.alpha, self.green * self.alpha, self.blue * self.alpha ]; + let end = [ other.alpha, other.red * other.alpha, other.green * other.alpha, other.blue * other.alpha ]; + start.iter().zip(&end).map(|(this, other)| this.compute_squared_distance(other)).sum() } } @@ -2930,31 +2653,35 @@ impl Animatable for IntermediateColor { }) } } +} +impl ComputeSquaredDistance for IntermediateColor { #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(|sq| sq.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { // All comments in add_weighted also applies here. if self.foreground_ratio == other.foreground_ratio { if self.is_currentcolor() { - Ok(0.) + Ok(SquaredDistance::Value(0.)) } else { self.color.compute_squared_distance(&other.color) } } else if self.is_currentcolor() && other.is_numeric() { - Ok(IntermediateRGBA::transparent().compute_squared_distance(&other.color)? + 1.) + Ok( + IntermediateRGBA::transparent().compute_squared_distance(&other.color)? + + SquaredDistance::Value(1.), + ) } else if self.is_numeric() && other.is_currentcolor() { - Ok(self.color.compute_squared_distance(&IntermediateRGBA::transparent())? + 1.) + Ok( + self.color.compute_squared_distance(&IntermediateRGBA::transparent())? + + SquaredDistance::Value(1.), + ) } else { let self_color = self.effective_intermediate_rgba(); let other_color = other.effective_intermediate_rgba(); - let dist = self_color.compute_squared_distance(&other_color)?; - let ratio_diff = (self.foreground_ratio - other.foreground_ratio) as f64; - Ok(dist + ratio_diff * ratio_diff) + Ok( + self_color.compute_squared_distance(&other_color)? + + self.foreground_ratio.compute_squared_distance(&other.foreground_ratio)?, + ) } } } @@ -2978,16 +2705,15 @@ impl Animatable for IntermediateSVGPaint { fallback: self.fallback.add_weighted(&other.fallback, self_portion, other_portion)?, }) } +} +impl ComputeSquaredDistance for IntermediateSVGPaint { #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(|sq| sq.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - Ok(self.kind.compute_squared_distance(&other.kind)? + - self.fallback.compute_squared_distance(&other.fallback)?) + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + Ok( + self.kind.compute_squared_distance(&other.kind)? + + self.fallback.compute_squared_distance(&other.fallback)?, + ) } } @@ -3016,16 +2742,20 @@ impl Animatable for IntermediateSVGPaintKind { _ => Err(()) } } +} +impl ComputeSquaredDistance for IntermediateSVGPaintKind { #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { match (self, other) { - (&SVGPaintKind::Color(ref self_color), &SVGPaintKind::Color(ref other_color)) => { - self_color.compute_distance(other_color) + (&SVGPaintKind::Color(ref this), &SVGPaintKind::Color(ref other)) => { + this.compute_squared_distance(other) } (&SVGPaintKind::None, &SVGPaintKind::None) | (&SVGPaintKind::ContextFill, &SVGPaintKind::ContextFill) | - (&SVGPaintKind::ContextStroke, &SVGPaintKind::ContextStroke)=> Ok(0.0), + (&SVGPaintKind::ContextStroke, &SVGPaintKind::ContextStroke) => { + Ok(SquaredDistance::Value(0.)) + }, _ => Err(()) } } @@ -3060,16 +2790,6 @@ impl<LengthType> Animatable for SVGLength<LengthType> } } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&SVGLength::Length(ref this), &SVGLength::Length(ref other)) => { - this.compute_distance(other) - } - _ => Err(()) - } - } } impl<LengthType> ToAnimatedZero for SVGLength<LengthType> where LengthType : ToAnimatedZero { @@ -3097,16 +2817,6 @@ impl<LengthType> Animatable for SVGStrokeDashArray<LengthType> } } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => { - this.compute_distance(other) - } - _ => Err(()) - } - } } impl<LengthType> ToAnimatedZero for SVGStrokeDashArray<LengthType> @@ -3138,16 +2848,6 @@ impl<OpacityType> Animatable for SVGOpacity<OpacityType> } } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&SVGOpacity::Opacity(ref this), &SVGOpacity::Opacity(ref other)) => { - this.compute_distance(other) - } - _ => Err(()) - } - } } impl<OpacityType> ToAnimatedZero for SVGOpacity<OpacityType> @@ -3246,9 +2946,7 @@ fn add_weighted_filter_function(from: Option<<&AnimatedFilter>, } } -fn compute_filter_square_distance(from: &AnimatedFilter, - to: &AnimatedFilter) - -> Result<f64, ()> { +fn compute_filter_square_distance(from: &AnimatedFilter, to: &AnimatedFilter) -> Result<SquaredDistance, ()> { match (from, to) { % for func in FILTER_FUNCTIONS : (&Filter::${func}(f), @@ -3296,29 +2994,24 @@ impl Animatable for AnimatedFilterList { fn add(&self, other: &Self) -> Result<Self, ()> { Ok(AnimatedFilterList(self.0.iter().chain(other.0.iter()).cloned().collect())) } +} +impl ComputeSquaredDistance for AnimatedFilterList { #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { use itertools::{EitherOrBoth, Itertools}; - let mut square_distance: f64 = 0.0; - for it in self.0.iter().zip_longest(other.0.iter()) { - square_distance += match it { + self.0.iter().zip_longest(other.0.iter()).map(|it| { + match it { EitherOrBoth::Both(from, to) => { - compute_filter_square_distance(&from, &to)? + compute_filter_square_distance(&from, &to) }, EitherOrBoth::Left(list) | EitherOrBoth::Right(list)=> { let none = add_weighted_filter_function(Some(list), Some(list), 0.0, 0.0)?; - compute_filter_square_distance(&none, &list)? + compute_filter_square_distance(&none, &list) }, - }; - } - Ok(square_distance) + } + }).sum() } } @@ -3382,11 +3075,6 @@ impl<T> Animatable for NonNegative<T> fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { self.0.add_weighted(&other.0, self_portion, other_portion).map(NonNegative::<T>) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.0.compute_distance(&other.0) - } } impl<T> ToAnimatedZero for NonNegative<T> @@ -3405,11 +3093,6 @@ impl<T> Animatable for GreaterThanOrEqualToOne<T> fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { self.0.add_weighted(&other.0, self_portion, other_portion).map(GreaterThanOrEqualToOne::<T>) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.0.compute_distance(&other.0) - } } impl<T> ToAnimatedZero for GreaterThanOrEqualToOne<T> diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 96ba9199bf2..0ebcf2db9a7 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -485,7 +485,7 @@ ${helpers.single_keyword_system("font-variant-caps", /// /// However, system fonts may provide other values. Pango /// may provide 350, 380, and 1000 (on top of the existing values), for example. - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, ToCss)] + #[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, Eq, Hash, ToCss)] #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] pub struct T(pub u16); @@ -1118,6 +1118,7 @@ ${helpers.single_keyword_system("font-variant-caps", use properties::animated_properties::Animatable; use values::CSSFloat; use values::animated::{ToAnimatedValue, ToAnimatedZero}; + use values::distance::{ComputeSquaredDistance, SquaredDistance}; #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Copy, Clone, Debug, PartialEq, ToCss)] @@ -1145,12 +1146,13 @@ ${helpers.single_keyword_system("font-variant-caps", _ => Err(()), } } + } + impl ComputeSquaredDistance for T { #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (*self, *other) { - (T::Number(ref number), T::Number(ref other)) => - number.compute_distance(other), + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + match (self, other) { + (&T::Number(ref this), &T::Number(ref other)) => this.compute_squared_distance(other), _ => Err(()), } } diff --git a/components/style/properties/longhand/inherited_table.mako.rs b/components/style/properties/longhand/inherited_table.mako.rs index 1be9de01bd2..1d646cc9410 100644 --- a/components/style/properties/longhand/inherited_table.mako.rs +++ b/components/style/properties/longhand/inherited_table.mako.rs @@ -31,7 +31,7 @@ ${helpers.single_keyword("caption-side", "top bottom", use values::computed::NonNegativeAu; #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - #[derive(Clone, Copy, Debug, PartialEq, ToCss)] + #[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToCss)] pub struct T { pub horizontal: NonNegativeAu, pub vertical: NonNegativeAu, @@ -49,17 +49,6 @@ ${helpers.single_keyword("caption-side", "top bottom", self_portion, other_portion)?, }) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - Ok(self.horizontal.compute_squared_distance(&other.horizontal)? + - self.vertical.compute_squared_distance(&other.vertical)?) - } } impl ToAnimatedZero for T { diff --git a/components/style/values/animated/effects.rs b/components/style/values/animated/effects.rs index 9bd3456d028..37cf926a1d7 100644 --- a/components/style/values/animated/effects.rs +++ b/components/style/values/animated/effects.rs @@ -14,6 +14,7 @@ use values::Impossible; use values::animated::{ToAnimatedValue, ToAnimatedZero}; use values::computed::{Angle, NonNegativeNumber}; use values::computed::length::{Length, NonNegativeLength}; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::effects::BoxShadow as GenericBoxShadow; use values::generics::effects::Filter as GenericFilter; use values::generics::effects::SimpleShadow as GenericSimpleShadow; @@ -102,6 +103,14 @@ where } } +impl<S> ComputeSquaredDistance for ShadowList<S> { + #[inline] + fn compute_squared_distance(&self, _other: &Self) -> Result<SquaredDistance, ()> { + // FIXME: This should be implemented. + Err(()) + } +} + impl<S> ToAnimatedZero for ShadowList<S> { #[inline] fn to_animated_zero(&self) -> Result<Self, ()> { @@ -140,14 +149,11 @@ impl Animatable for BoxShadow { inset: self.inset, }) } +} +impl ComputeSquaredDistance for BoxShadow { #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { if self.inset != other.inset { return Err(()); } @@ -219,21 +225,6 @@ impl Animatable for SimpleShadow { blur: blur, }) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - Ok( - self.color.compute_squared_distance(&other.color)? + - self.horizontal.compute_squared_distance(&other.horizontal)? + - self.vertical.compute_squared_distance(&other.vertical)? + - self.blur.compute_squared_distance(&other.blur)? - ) - } } impl ToAnimatedZero for SimpleShadow { diff --git a/components/style/values/computed/background.rs b/components/style/values/computed/background.rs index d2781ac1891..a2111a046e4 100644 --- a/components/style/values/computed/background.rs +++ b/components/style/values/computed/background.rs @@ -30,27 +30,6 @@ impl Animatable for BackgroundSize { _ => Err(()), } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - ( - &GenericBackgroundSize::Explicit { width: self_width, height: self_height }, - &GenericBackgroundSize::Explicit { width: other_width, height: other_height }, - ) => { - Ok( - self_width.compute_squared_distance(&other_width)? + - self_height.compute_squared_distance(&other_height)? - ) - } - _ => Err(()), - } - } } impl ToAnimatedZero for BackgroundSize { diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index ed4a38255d3..fbe3b56e724 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -12,6 +12,7 @@ use style_traits::values::specified::AllowedLengthType; use super::{Number, ToComputedValue, Context, Percentage}; use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified}; use values::computed::{NonNegativeAu, NonNegativeNumber}; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::NonNegative; use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength}; use values::specified::length::ViewportPercentageLength; @@ -71,6 +72,18 @@ pub struct CalcLengthOrPercentage { pub percentage: Option<Percentage>, } +impl ComputeSquaredDistance for CalcLengthOrPercentage { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + // FIXME(nox): This looks incorrect to me, to add a distance between lengths + // with a distance between percentages. + Ok( + self.unclamped_length().compute_squared_distance(&other.unclamped_length())? + + self.percentage().compute_squared_distance(&other.percentage())?, + ) + } +} + impl CalcLengthOrPercentage { /// Returns a new `CalcLengthOrPercentage`. #[inline] @@ -257,6 +270,23 @@ pub enum LengthOrPercentage { Calc(CalcLengthOrPercentage), } +impl ComputeSquaredDistance for LengthOrPercentage { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + match (self, other) { + (&LengthOrPercentage::Length(ref this), &LengthOrPercentage::Length(ref other)) => { + this.compute_squared_distance(other) + }, + (&LengthOrPercentage::Percentage(ref this), &LengthOrPercentage::Percentage(ref other)) => { + this.compute_squared_distance(other) + }, + (this, other) => { + CalcLengthOrPercentage::compute_squared_distance(&(*this).into(), &(*other).into()) + } + } + } +} + impl From<Au> for LengthOrPercentage { #[inline] fn from(length: Au) -> Self { @@ -382,6 +412,23 @@ pub enum LengthOrPercentageOrAuto { Calc(CalcLengthOrPercentage), } +impl ComputeSquaredDistance for LengthOrPercentageOrAuto { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + match (self, other) { + (&LengthOrPercentageOrAuto::Length(ref this), &LengthOrPercentageOrAuto::Length(ref other)) => { + this.compute_squared_distance(other) + }, + (&LengthOrPercentageOrAuto::Percentage(ref this), &LengthOrPercentageOrAuto::Percentage(ref other)) => { + this.compute_squared_distance(other) + }, + (this, other) => { + <Option<CalcLengthOrPercentage>>::compute_squared_distance(&(*this).into(), &(*other).into()) + } + } + } +} + impl LengthOrPercentageOrAuto { /// Returns true if the computed value is absolute 0 or 0%. /// @@ -460,6 +507,23 @@ pub enum LengthOrPercentageOrNone { None, } +impl ComputeSquaredDistance for LengthOrPercentageOrNone { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + match (self, other) { + (&LengthOrPercentageOrNone::Length(ref this), &LengthOrPercentageOrNone::Length(ref other)) => { + this.compute_squared_distance(other) + }, + (&LengthOrPercentageOrNone::Percentage(ref this), &LengthOrPercentageOrNone::Percentage(ref other)) => { + this.compute_squared_distance(other) + }, + (this, other) => { + <Option<CalcLengthOrPercentage>>::compute_squared_distance(&(*this).into(), &(*other).into()) + } + } + } +} + impl LengthOrPercentageOrNone { /// Returns the used value. pub fn to_used_value(&self, containing_length: Au) -> Option<Au> { @@ -607,6 +671,22 @@ pub enum MozLength { ExtremumLength(ExtremumLength), } +impl ComputeSquaredDistance for MozLength { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + match (self, other) { + (&MozLength::LengthOrPercentageOrAuto(ref this), &MozLength::LengthOrPercentageOrAuto(ref other)) => { + this.compute_squared_distance(other) + }, + _ => { + // FIXME(nox): Should this return `Ok(SquaredDistance::Value(1.))` + // when `self` and `other` are the same extremum value? + Err(()) + }, + } + } +} + impl MozLength { /// Returns the `auto` value. pub fn auto() -> Self { @@ -651,6 +731,22 @@ pub enum MaxLength { ExtremumLength(ExtremumLength), } +impl ComputeSquaredDistance for MaxLength { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + match (self, other) { + (&MaxLength::LengthOrPercentageOrNone(ref this), &MaxLength::LengthOrPercentageOrNone(ref other)) => { + this.compute_squared_distance(other) + }, + _ => { + // FIXME(nox): Should this return `Ok(SquaredDistance::Value(1.))` + // when `self` and `other` are the same extremum value? + Err(()) + }, + } + } +} + impl MaxLength { /// Returns the `none` value. pub fn none() -> Self { diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 75b9e3bb354..b963d37fab6 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -22,6 +22,7 @@ use std::fmt; use std::sync::Arc; use style_traits::ToCss; use super::{CSSFloat, CSSInteger}; +use super::distance::{ComputeSquaredDistance, SquaredDistance}; use super::generics::{GreaterThanOrEqualToOne, NonNegative}; use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize}; use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent; @@ -338,6 +339,15 @@ pub enum Angle { Turn(CSSFloat), } +impl ComputeSquaredDistance for Angle { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + // Use the formula for calculating the distance between angles defined in SVG: + // https://www.w3.org/TR/SVG/animate.html#complexDistances + self.radians64().compute_squared_distance(&other.radians64()) + } +} + impl Angle { /// Construct a computed `Angle` value from a radian amount. pub fn from_radians(radians: CSSFloat) -> Self { @@ -533,9 +543,9 @@ pub type LengthOrPercentageOrNumber = Either<Number, LengthOrPercentage>; /// NonNegativeLengthOrPercentage | NonNegativeNumber pub type NonNegativeLengthOrPercentageOrNumber = Either<NonNegativeNumber, NonNegativeLengthOrPercentage>; -#[derive(Clone, PartialEq, Eq, Copy, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, Eq, PartialEq)] /// A computed cliprect for clip and image-region pub struct ClipRect { pub top: Option<Au>, @@ -649,7 +659,7 @@ impl From<Au> for NonNegativeAu { } /// A computed `<percentage>` value. -#[derive(Clone, Copy, Debug, Default, PartialEq, HasViewportPercentage)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, Default, HasViewportPercentage, PartialEq)] #[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))] pub struct Percentage(pub CSSFloat); diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs index 7376616ba5e..fd39b4dd647 100644 --- a/components/style/values/computed/text.rs +++ b/components/style/values/computed/text.rs @@ -45,22 +45,6 @@ impl Animatable for LineHeight { _ => Err(()), } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (*self, *other) { - (GenericLineHeight::Length(ref this), GenericLineHeight::Length(ref other)) => { - this.compute_distance(other) - }, - (GenericLineHeight::Number(ref this), GenericLineHeight::Number(ref other)) => { - this.compute_distance(other) - }, - (GenericLineHeight::Normal, GenericLineHeight::Normal) => Ok(0.), - #[cfg(feature = "gecko")] - (GenericLineHeight::MozBlockHeight, GenericLineHeight::MozBlockHeight) => Ok(0.), - _ => Err(()), - } - } } impl ToAnimatedZero for LineHeight { diff --git a/components/style/values/computed/transform.rs b/components/style/values/computed/transform.rs index 5bf953d46be..70c543b9a7f 100644 --- a/components/style/values/computed/transform.rs +++ b/components/style/values/computed/transform.rs @@ -37,20 +37,6 @@ impl Animatable for TransformOrigin { self.depth.add_weighted(&other.depth, self_portion, other_portion)?, )) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.compute_squared_distance(other).map(f64::sqrt) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - Ok( - self.horizontal.compute_squared_distance(&other.horizontal)? + - self.vertical.compute_squared_distance(&other.vertical)? + - self.depth.compute_squared_distance(&other.depth)? - ) - } } impl ToAnimatedZero for TransformOrigin { diff --git a/components/style/values/distance.rs b/components/style/values/distance.rs new file mode 100644 index 00000000000..d4eb91f112f --- /dev/null +++ b/components/style/values/distance.rs @@ -0,0 +1,124 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Machinery to compute distances between animatable values. + +use app_units::Au; +use euclid::Size2D; +use std::iter::Sum; +use std::ops::Add; + +/// A trait to compute squared distances between two animatable values. +pub trait ComputeSquaredDistance { + /// Computes the squared distance between two animatable values. + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()>; +} + +/// A distance between two animatable values. +#[derive(Clone, Copy, Debug)] +pub enum SquaredDistance { + /// Represented as the square root of the squared distance. + Sqrt(f64), + /// Represented as the squared distance itself. + Value(f64), +} + +impl ComputeSquaredDistance for u16 { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + Ok(SquaredDistance::Sqrt(((*self as f64) - (*other as f64)).abs())) + } +} + +impl ComputeSquaredDistance for i32 { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + Ok(SquaredDistance::Sqrt((*self - *other).abs() as f64)) + } +} + +impl ComputeSquaredDistance for f32 { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + Ok(SquaredDistance::Sqrt((*self - *other).abs() as f64)) + } +} + +impl ComputeSquaredDistance for f64 { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + Ok(SquaredDistance::Sqrt((*self - *other).abs())) + } +} + +impl ComputeSquaredDistance for Au { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + self.0.compute_squared_distance(&other.0) + } +} + +impl<T> ComputeSquaredDistance for Option<T> + where T: ComputeSquaredDistance +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + match (self.as_ref(), other.as_ref()) { + (Some(this), Some(other)) => this.compute_squared_distance(other), + (None, None) => Ok(SquaredDistance::Value(0.)), + _ => Err(()), + } + } +} + +impl<T> ComputeSquaredDistance for Size2D<T> + where T: ComputeSquaredDistance +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + Ok(self.width.compute_squared_distance(&other.width)? + self.height.compute_squared_distance(&other.height)?) + } +} + +impl SquaredDistance { + /// Returns the square root of this squared distance. + pub fn sqrt(self) -> f64 { + match self { + SquaredDistance::Sqrt(this) => this, + SquaredDistance::Value(this) => this.sqrt(), + } + } +} + +impl From<SquaredDistance> for f64 { + #[inline] + fn from(distance: SquaredDistance) -> Self { + match distance { + SquaredDistance::Sqrt(this) => this * this, + SquaredDistance::Value(this) => this, + } + } +} + +impl Add for SquaredDistance { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self { + SquaredDistance::Value(f64::from(self) + f64::from(rhs)) + } +} + +impl Sum for SquaredDistance { + fn sum<I>(mut iter: I) -> Self + where + I: Iterator<Item = Self>, + { + let first = match iter.next() { + Some(first) => first, + None => return SquaredDistance::Value(0.), + }; + iter.fold(first, Add::add) + } +} diff --git a/components/style/values/generics/background.rs b/components/style/values/generics/background.rs index aa24454ef6d..8a79691f3b9 100644 --- a/components/style/values/generics/background.rs +++ b/components/style/values/generics/background.rs @@ -5,7 +5,7 @@ //! Generic types for CSS values related to backgrounds. /// A generic value for the `background-size` property. -#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum BackgroundSize<LengthOrPercentageOrAuto> { /// `<width> <height>` diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs index f218fd41ba8..cad038df0f1 100644 --- a/components/style/values/generics/basic_shape.rs +++ b/components/style/values/generics/basic_shape.rs @@ -10,6 +10,7 @@ use std::fmt; use style_traits::{HasViewportPercentage, ToCss}; use values::animated::ToAnimatedZero; use values::computed::ComputedValueAsSpecified; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::border::BorderRadius; use values::generics::position::Position; use values::generics::rect::Rect; @@ -54,7 +55,7 @@ pub enum ShapeSource<BasicShape, ReferenceBox, Url> { #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)] +#[derive(Clone, ComputeSquaredDistance, Debug, PartialEq, ToComputedValue, ToCss)] pub enum BasicShape<H, V, LengthOrPercentage> { Inset(InsetRect<LengthOrPercentage>), Circle(Circle<H, V, LengthOrPercentage>), @@ -65,7 +66,7 @@ pub enum BasicShape<H, V, LengthOrPercentage> { /// https://drafts.csswg.org/css-shapes/#funcdef-inset #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Debug, PartialEq, ToComputedValue)] +#[derive(Clone, ComputeSquaredDistance, Debug, PartialEq, ToComputedValue)] pub struct InsetRect<LengthOrPercentage> { pub rect: Rect<LengthOrPercentage>, pub round: Option<BorderRadius<LengthOrPercentage>>, @@ -74,7 +75,7 @@ pub struct InsetRect<LengthOrPercentage> { /// https://drafts.csswg.org/css-shapes/#funcdef-circle #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToComputedValue)] pub struct Circle<H, V, LengthOrPercentage> { pub position: Position<H, V>, pub radius: ShapeRadius<LengthOrPercentage>, @@ -83,7 +84,7 @@ pub struct Circle<H, V, LengthOrPercentage> { /// https://drafts.csswg.org/css-shapes/#funcdef-ellipse #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToComputedValue)] pub struct Ellipse<H, V, LengthOrPercentage> { pub position: Position<H, V>, pub semiaxis_x: ShapeRadius<LengthOrPercentage>, @@ -93,7 +94,7 @@ pub struct Ellipse<H, V, LengthOrPercentage> { /// https://drafts.csswg.org/css-shapes/#typedef-shape-radius #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToComputedValue, ToCss)] pub enum ShapeRadius<LengthOrPercentage> { Length(LengthOrPercentage), ClosestSide, @@ -144,20 +145,16 @@ where _ => Err(()), } } +} - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - ( - &ShapeSource::Shape(ref this, ref this_box), - &ShapeSource::Shape(ref other, ref other_box), - ) if this_box == other_box => { - this.compute_distance(other) - }, - _ => Err(()), - } - } - - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { +// FIXME(nox): Implement ComputeSquaredDistance for T types and stop +// using PartialEq here, this will let us derive this impl. +impl<B, T, U> ComputeSquaredDistance for ShapeSource<B, T, U> +where + B: ComputeSquaredDistance, + T: PartialEq, +{ + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { match (self, other) { ( &ShapeSource::Shape(ref this, ref this_box), @@ -209,42 +206,6 @@ where _ => Err(()), } } - - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&BasicShape::Circle(ref this), &BasicShape::Circle(ref other)) => { - this.compute_distance(other) - }, - (&BasicShape::Ellipse(ref this), &BasicShape::Ellipse(ref other)) => { - this.compute_distance(other) - }, - (&BasicShape::Inset(ref this), &BasicShape::Inset(ref other)) => { - this.compute_distance(other) - }, - (&BasicShape::Polygon(ref this), &BasicShape::Polygon(ref other)) => { - this.compute_distance(other) - }, - _ => Err(()), - } - } - - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&BasicShape::Circle(ref this), &BasicShape::Circle(ref other)) => { - this.compute_squared_distance(other) - }, - (&BasicShape::Ellipse(ref this), &BasicShape::Ellipse(ref other)) => { - this.compute_squared_distance(other) - }, - (&BasicShape::Inset(ref this), &BasicShape::Inset(ref other)) => { - this.compute_squared_distance(other) - }, - (&BasicShape::Polygon(ref this), &BasicShape::Polygon(ref other)) => { - this.compute_squared_distance(other) - }, - _ => Err(()), - } - } } impl<L> Animatable for InsetRect<L> @@ -261,17 +222,6 @@ where let round = self.round.add_weighted(&other.round, self_portion, other_portion)?; Ok(InsetRect { rect, round }) } - - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - Ok(self.compute_squared_distance(other)?.sqrt()) - } - - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - Ok( - self.rect.compute_squared_distance(&other.rect)? + - self.round.compute_squared_distance(&other.round)?, - ) - } } impl<L> ToCss for InsetRect<L> @@ -304,17 +254,6 @@ where let radius = self.radius.add_weighted(&other.radius, self_portion, other_portion)?; Ok(Circle { position, radius }) } - - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - Ok(self.compute_squared_distance(other)?.sqrt()) - } - - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - Ok( - self.position.compute_squared_distance(&other.position)? + - self.radius.compute_squared_distance(&other.radius)?, - ) - } } impl<H, V, L> Animatable for Ellipse<H, V, L> @@ -334,18 +273,6 @@ where let semiaxis_y = self.semiaxis_y.add_weighted(&other.semiaxis_y, self_portion, other_portion)?; Ok(Ellipse { position, semiaxis_x, semiaxis_y }) } - - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - Ok(self.compute_squared_distance(other)?.sqrt()) - } - - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - Ok( - self.position.compute_squared_distance(&other.position)? + - self.semiaxis_x.compute_squared_distance(&other.semiaxis_x)? + - self.semiaxis_y.compute_squared_distance(&other.semiaxis_y)?, - ) - } } impl<L> Animatable for ShapeRadius<L> @@ -365,24 +292,6 @@ where _ => Err(()), } } - - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&ShapeRadius::Length(ref this), &ShapeRadius::Length(ref other)) => { - this.compute_distance(other) - }, - _ => Err(()), - } - } - - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - match (self, other) { - (&ShapeRadius::Length(ref this), &ShapeRadius::Length(ref other)) => { - this.compute_squared_distance(other) - }, - _ => Err(()), - } - } } impl<L> Default for ShapeRadius<L> { @@ -413,12 +322,13 @@ where }).collect::<Result<Vec<_>, _>>()?; Ok(Polygon { fill: self.fill, coordinates }) } +} - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - Ok(self.compute_squared_distance(other)?.sqrt()) - } - - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { +impl<L> ComputeSquaredDistance for Polygon<L> +where + L: ComputeSquaredDistance, +{ + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { if self.fill != other.fill { return Err(()); } @@ -426,9 +336,10 @@ where return Err(()); } self.coordinates.iter().zip(other.coordinates.iter()).map(|(this, other)| { - let x = this.0.compute_squared_distance(&other.0)?; - let y = this.1.compute_squared_distance(&other.1)?; - Ok(x + y) + Ok( + this.0.compute_squared_distance(&other.0)? + + this.1.compute_squared_distance(&other.1)?, + ) }).sum() } } diff --git a/components/style/values/generics/border.rs b/components/style/values/generics/border.rs index e70504e7e87..78faf1de050 100644 --- a/components/style/values/generics/border.rs +++ b/components/style/values/generics/border.rs @@ -36,7 +36,7 @@ pub struct BorderImageSlice<NumberOrPercentage> { /// /// https://drafts.csswg.org/css-backgrounds-3/#border-radius #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] pub struct BorderRadius<LengthOrPercentage> { /// The top left radius. pub top_left: BorderCornerRadius<LengthOrPercentage>, @@ -48,9 +48,9 @@ pub struct BorderRadius<LengthOrPercentage> { pub bottom_left: BorderCornerRadius<LengthOrPercentage>, } -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] /// A generic value for `border-*-radius` longhand properties. +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] pub struct BorderCornerRadius<L>(pub Size2D<L>); impl<N> From<N> for BorderImageSlice<N> @@ -129,19 +129,6 @@ where let bl = self.bottom_left.add_weighted(&other.bottom_left, self_portion, other_portion)?; Ok(BorderRadius::new(tl, tr, br, bl)) } - - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - Ok(self.compute_squared_distance(other)?.sqrt()) - } - - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - Ok( - self.top_left.compute_squared_distance(&other.top_left)? + - self.top_right.compute_squared_distance(&other.top_right)? + - self.bottom_right.compute_squared_distance(&other.bottom_right)? + - self.bottom_left.compute_squared_distance(&other.bottom_left)?, - ) - } } impl<L> ToCss for BorderRadius<L> @@ -189,16 +176,6 @@ where ) -> Result<Self, ()> { Ok(BorderCornerRadius(self.0.add_weighted(&other.0, self_portion, other_portion)?)) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - self.0.compute_distance(&other.0) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - self.0.compute_squared_distance(&other.0) - } } impl<L> ToCss for BorderCornerRadius<L> diff --git a/components/style/values/generics/effects.rs b/components/style/values/generics/effects.rs index 9105123f5ab..cff8d87c078 100644 --- a/components/style/values/generics/effects.rs +++ b/components/style/values/generics/effects.rs @@ -65,7 +65,7 @@ pub enum Filter<Angle, Factor, Length, DropShadow> { /// Contrary to the canonical order from the spec, the color is serialised /// first, like in Gecko and Webkit. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToAnimatedValue, ToCss)] +#[derive(Clone, ComputeSquaredDistance, Debug, HasViewportPercentage, PartialEq, ToAnimatedValue, ToCss)] pub struct SimpleShadow<Color, SizeLength, ShapeLength> { /// Color. pub color: Color, diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index 075ce2c4715..b097b0f85de 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -267,11 +267,13 @@ impl ToCss for FontSettingTagFloat { } /// A wrapper of Non-negative values. -#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, PartialOrd, ToComputedValue, ToCss)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] +#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage)] +#[derive(PartialEq, PartialOrd, ToComputedValue, ToCss)] pub struct NonNegative<T>(pub T); /// A wrapper of greater-than-or-equal-to-one values. -#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, PartialOrd, ToComputedValue, ToCss)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] +#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage)] +#[derive(PartialEq, PartialOrd, ToComputedValue, ToCss)] pub struct GreaterThanOrEqualToOne<T>(pub T); diff --git a/components/style/values/generics/position.rs b/components/style/values/generics/position.rs index 4ce5b63ab30..503cb9a1563 100644 --- a/components/style/values/generics/position.rs +++ b/components/style/values/generics/position.rs @@ -5,9 +5,9 @@ //! Generic types for CSS handling of specified and computed values of //! [`position`](https://drafts.csswg.org/css-backgrounds-3/#position) -#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] /// A generic type for representing a CSS [position](https://drafts.csswg.org/css-values/#position). +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] pub struct Position<H, V> { /// The horizontal component of position. pub horizontal: H, diff --git a/components/style/values/generics/rect.rs b/components/style/values/generics/rect.rs index 3dc9e095890..89ce196267a 100644 --- a/components/style/values/generics/rect.rs +++ b/components/style/values/generics/rect.rs @@ -12,7 +12,7 @@ use style_traits::{ToCss, ParseError}; /// A CSS value made of four components, where its `ToCss` impl will try to /// serialize as few components as possible, like for example in `border-width`. -#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct Rect<T>(pub T, pub T, pub T, pub T); diff --git a/components/style/values/generics/svg.rs b/components/style/values/generics/svg.rs index 05e0e66248f..60f76950aee 100644 --- a/components/style/values/generics/svg.rs +++ b/components/style/values/generics/svg.rs @@ -97,7 +97,8 @@ impl<ColorType: Parse, UrlPaintServer: Parse> Parse for SVGPaint<ColorType, UrlP /// An SVG length value supports `context-value` in addition to length. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, Debug, PartialEq, HasViewportPercentage, ToAnimatedValue, ToComputedValue, ToCss)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq)] +#[derive(HasViewportPercentage, ToAnimatedValue, ToComputedValue, ToCss)] pub enum SVGLength<LengthType> { /// `<length> | <percentage> | <number>` Length(LengthType), @@ -107,7 +108,7 @@ pub enum SVGLength<LengthType> { /// Generic value for stroke-dasharray. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Debug, PartialEq, HasViewportPercentage, ToAnimatedValue, ToComputedValue)] +#[derive(Clone, ComputeSquaredDistance, Debug, PartialEq, HasViewportPercentage, ToAnimatedValue, ToComputedValue)] pub enum SVGStrokeDashArray<LengthType> { /// `[ <length> | <percentage> | <number> ]#` Values(Vec<LengthType>), @@ -141,7 +142,7 @@ impl<LengthType> ToCss for SVGStrokeDashArray<LengthType> where LengthType: ToCs /// An SVG opacity value accepts `context-{fill,stroke}-opacity` in /// addition to opacity value. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, Debug, PartialEq, HasViewportPercentage, ToComputedValue, ToCss)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, HasViewportPercentage, ToComputedValue, ToCss)] pub enum SVGOpacity<OpacityType> { /// `<opacity-value>` Opacity(OpacityType), diff --git a/components/style/values/generics/text.rs b/components/style/values/generics/text.rs index 94be4a6db8c..a3879602bdc 100644 --- a/components/style/values/generics/text.rs +++ b/components/style/values/generics/text.rs @@ -10,6 +10,7 @@ use parser::ParserContext; use properties::animated_properties::Animatable; use style_traits::ParseError; use values::animated::ToAnimatedZero; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; /// A generic value for the `initial-letter` property. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -84,13 +85,18 @@ impl<Value> Animatable for Spacing<Value> let other = other.value().unwrap_or(&zero); this.add_weighted(other, self_portion, other_portion).map(Spacing::Value) } +} +impl<V> ComputeSquaredDistance for Spacing<V> +where + V: ComputeSquaredDistance + From<Au>, +{ #[inline] - fn compute_distance(&self, other: &Self) -> Result<f64, ()> { - let zero = Value::from(Au(0)); + fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> { + let zero = V::from(Au(0)); let this = self.value().unwrap_or(&zero); let other = other.value().unwrap_or(&zero); - this.compute_distance(other) + this.compute_squared_distance(other) } } @@ -104,7 +110,7 @@ where /// A generic value for the `line-height` property. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToAnimatedValue, ToCss)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToAnimatedValue, ToCss)] pub enum LineHeight<Number, LengthOrPercentage> { /// `normal` Normal, diff --git a/components/style/values/generics/transform.rs b/components/style/values/generics/transform.rs index e00104f0c43..32f37caec12 100644 --- a/components/style/values/generics/transform.rs +++ b/components/style/values/generics/transform.rs @@ -24,7 +24,7 @@ pub struct Matrix<T, U = T> { /// A generic transform origin. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)] +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)] pub struct TransformOrigin<H, V, Depth> { /// The horizontal origin. pub horizontal: H, diff --git a/components/style/values/mod.rs b/components/style/values/mod.rs index 616aa5cb485..f03ab188a7a 100644 --- a/components/style/values/mod.rs +++ b/components/style/values/mod.rs @@ -19,6 +19,7 @@ use style_traits::{ToCss, ParseError, StyleParseError}; pub mod animated; pub mod computed; +pub mod distance; pub mod generics; pub mod specified; @@ -51,7 +52,8 @@ impl Parse for Impossible { /// A struct representing one of two kinds of values. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[derive(Clone, Copy, HasViewportPercentage, PartialEq, ToAnimatedValue, ToComputedValue, ToCss)] +#[derive(Clone, ComputeSquaredDistance, Copy, HasViewportPercentage, PartialEq)] +#[derive(ToAnimatedValue, ToComputedValue, ToCss)] pub enum Either<A, B> { /// The first value. First(A), diff --git a/components/style_derive/compute_squared_distance.rs b/components/style_derive/compute_squared_distance.rs new file mode 100644 index 00000000000..46aa376319d --- /dev/null +++ b/components/style_derive/compute_squared_distance.rs @@ -0,0 +1,117 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use quote; +use std::borrow::Cow; +use syn; +use synstructure; + +pub fn derive(input: syn::DeriveInput) -> quote::Tokens { + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let mut where_clause = where_clause.clone(); + for param in &input.generics.ty_params { + where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into()))) + } + + let variants = variants(&input); + let mut match_body = quote!(); + match_body.append_all(variants.iter().map(|variant| { + let name = match input.body { + syn::Body::Struct(_) => Cow::Borrowed(&input.ident), + syn::Body::Enum(_) => { + Cow::Owned(syn::Ident::from(format!("{}::{}", input.ident, variant.ident))) + }, + }; + let (this_pattern, this_info) = synstructure::match_pattern( + &name, + &variant.data, + &synstructure::BindOpts::with_prefix( + synstructure::BindStyle::Ref, + "this".to_owned(), + ), + ); + let (other_pattern, other_info) = synstructure::match_pattern( + &name, + &variant.data, + &synstructure::BindOpts::with_prefix( + synstructure::BindStyle::Ref, + "other".to_owned(), + ), + ); + let sum = if this_info.is_empty() { + quote! { ::values::distance::SquaredDistance::Value(0.) } + } else { + let mut sum = quote!(); + sum.append_separated(this_info.iter().zip(&other_info).map(|(this, other)| { + where_clause.predicates.push(where_predicate(this.field.ty.clone())); + quote! { + ::values::distance::ComputeSquaredDistance::compute_squared_distance(#this, #other)? + } + }), "+"); + sum + }; + quote! { + (&#this_pattern, &#other_pattern) => { + Ok(#sum) + } + } + })); + + if variants.len() > 1 { + match_body = quote! { #match_body, _ => Err(()), }; + } + + quote! { + impl #impl_generics ::values::distance::ComputeSquaredDistance for #name #ty_generics #where_clause { + #[allow(unused_variables, unused_imports)] + #[inline] + fn compute_squared_distance( + &self, + other: &Self, + ) -> Result<::values::distance::SquaredDistance, ()> { + match (self, other) { + #match_body + } + } + } + } +} + +fn variants(input: &syn::DeriveInput) -> Cow<[syn::Variant]> { + match input.body { + syn::Body::Enum(ref variants) => (&**variants).into(), + syn::Body::Struct(ref data) => { + vec![syn::Variant { + ident: input.ident.clone(), + attrs: input.attrs.clone(), + data: data.clone(), + discriminant: None, + }].into() + }, + } +} + +fn where_predicate(ty: syn::Ty) -> syn::WherePredicate { + syn::WherePredicate::BoundPredicate( + syn::WhereBoundPredicate { + bound_lifetimes: vec![], + bounded_ty: ty, + bounds: vec![syn::TyParamBound::Trait( + syn::PolyTraitRef { + bound_lifetimes: vec![], + trait_ref: syn::Path { + global: true, + segments: vec![ + "values".into(), + "distance".into(), + "ComputeSquaredDistance".into(), + ], + }, + }, + syn::TraitBoundModifier::None, + )], + }, + ) +} diff --git a/components/style_derive/lib.rs b/components/style_derive/lib.rs index 683d6c2c563..13a855eb6ba 100644 --- a/components/style_derive/lib.rs +++ b/components/style_derive/lib.rs @@ -9,11 +9,18 @@ extern crate synstructure; use proc_macro::TokenStream; +mod compute_squared_distance; mod has_viewport_percentage; mod to_animated_value; mod to_computed_value; mod to_css; +#[proc_macro_derive(ComputeSquaredDistance)] +pub fn derive_compute_squared_distance(stream: TokenStream) -> TokenStream { + let input = syn::parse_derive_input(&stream.to_string()).unwrap(); + compute_squared_distance::derive(input).to_string().parse().unwrap() +} + #[proc_macro_derive(HasViewportPercentage)] pub fn derive_has_viewport_percentage(stream: TokenStream) -> TokenStream { let input = syn::parse_derive_input(&stream.to_string()).unwrap(); diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index b8ab946de88..cdf76aa1407 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -131,6 +131,7 @@ use style::traversal_flags::{TraversalFlags, self}; use style::values::{CustomIdent, KeyframesName}; use style::values::animated::ToAnimatedZero; use style::values::computed::Context; +use style::values::distance::ComputeSquaredDistance; use style_traits::{PARSING_MODE_DEFAULT, ToCss}; use super::error_reporter::ErrorReporter; use super::stylesheet_loader::StylesheetLoader; @@ -380,7 +381,7 @@ pub extern "C" fn Servo_AnimationValues_ComputeDistance(from: RawServoAnimationV -> f64 { let from_value = AnimationValue::as_arc(&from); let to_value = AnimationValue::as_arc(&to); - from_value.compute_distance(to_value).unwrap_or(0.0) + from_value.compute_squared_distance(to_value).map(|d| d.sqrt()).unwrap_or(0.0) } #[no_mangle] |