From 51b740033baaeb5fd4ce7ae1ad0b8dad3b3b7d9e Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 12 Aug 2017 17:36:52 +0200 Subject: [PATCH] Introduce ComputeSquaredDistance This allows us to merge the former Animatable methods compute_distance and compute_squared_distance, reducing code size. --- components/style/macros.rs | 10 + components/style/properties/helpers.mako.rs | 10 +- .../helpers/animated_properties.mako.rs | 525 ++++-------------- .../style/properties/longhand/font.mako.rs | 19 +- .../longhand/inherited_table.mako.rs | 16 +- components/style/values/animated/effects.rs | 27 +- .../style/values/computed/background.rs | 10 +- components/style/values/computed/length.rs | 96 ++++ components/style/values/computed/mod.rs | 29 + components/style/values/computed/text.rs | 23 +- components/style/values/computed/transform.rs | 10 +- components/style/values/distance.rs | 124 +++++ .../style/values/generics/basic_shape.rs | 121 ++-- components/style/values/generics/border.rs | 24 +- components/style/values/generics/mod.rs | 21 + components/style/values/generics/position.rs | 16 + components/style/values/generics/rect.rs | 16 + components/style/values/generics/svg.rs | 59 ++ components/style/values/generics/text.rs | 12 +- components/style/values/mod.rs | 21 + ports/geckolib/glue.rs | 3 +- 21 files changed, 641 insertions(+), 551 deletions(-) create mode 100644 components/style/values/distance.rs diff --git a/components/style/macros.rs b/components/style/macros.rs index d72ecc73c06..3251b7e2d37 100644 --- a/components/style/macros.rs +++ b/components/style/macros.rs @@ -117,5 +117,15 @@ macro_rules! define_keyword_type { #[inline] fn to_animated_zero(&self) -> Result { Ok($name) } } + + impl $crate::values::distance::ComputeSquaredDistance for $name { + #[inline] + fn compute_squared_distance( + &self, + _other: &Self + ) -> Result<$crate::values::distance::SquaredDistance, ()> { + Ok($crate::values::distance::SquaredDistance::Value(0.)) + } + } }; } diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 540007298d5..d428cc20cdb 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -132,6 +132,7 @@ % if need_animatable or animation_value_type == "ComputedValue": use properties::animated_properties::Animatable; use values::animated::ToAnimatedZero; + use values::distance::{ComputeSquaredDistance, SquaredDistance}; impl Animatable for T { fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) @@ -142,14 +143,11 @@ fn add(&self, other: &Self) -> Result { self.0.add(&other.0).map(T) } + } + impl ComputeSquaredDistance for T { #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.0.compute_distance(&other.0) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { + fn compute_squared_distance(&self, other: &Self) -> Result { self.0.compute_squared_distance(&other.0) } } 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.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 { 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 { - 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 { +impl ComputeSquaredDistance for AnimationValue { + fn compute_squared_distance(&self, other: &Self) -> Result { 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 ComputeSquaredDistance for $ty + where + T: ComputeSquaredDistance + RepeatableListAnimatable, + { #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - // 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 { + 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 { 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 { - self.0.compute_distance(&other.0) - } } impl Animatable for Option @@ -852,28 +840,6 @@ impl Animatable for Option _ => Err(()), } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - 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 { - 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 { Ok((*self as f64 * self_portion + *other as f64 * other_portion) as f32) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - 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 { Ok(*self * self_portion + *other * other_portion) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - 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 { Ok((*self as f64 * self_portion + *other as f64 * other_portion).round() as i32) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - 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 { - // 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 { Ok(Percentage((self.0 as f64 * self_portion + other.0 as f64 * other_portion) as f32)) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - 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 { - if *self == *other { - Ok(0.0) - } else { - Ok(1.0) - } + fn compute_squared_distance(&self, other: &Self) -> Result { + Ok(SquaredDistance::Value(if *self == *other { 0. } else { 1. })) } } @@ -1003,16 +940,6 @@ impl Animatable for Size2D { Ok(Size2D::new(width, height)) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - Ok(self.compute_squared_distance(other)?.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - Ok(self.width.compute_squared_distance(&other.width)? + self.height.compute_squared_distance(&other.height)?) - } } impl Animatable for Point2D { @@ -1044,15 +971,20 @@ impl Animatable for VerticalAlign { _ => Err(()), } } +} +impl ComputeSquaredDistance for VerticalAlign { #[inline] - fn compute_distance(&self, other: &Self) -> Result { - match (*self, *other) { - (VerticalAlign::LengthOrPercentage(ref this), - VerticalAlign::LengthOrPercentage(ref other)) => { - this.compute_distance(other) + fn compute_squared_distance(&self, other: &Self) -> Result { + 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 { - self.compute_squared_distance(other).map(|sq| sq.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - 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 { - 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 { - 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 { - 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 = From::from(this); - let other: Option = From::from(other); - this.compute_distance(&other) - } - } - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - 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 = From::from(this); - let other: Option = 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 { - 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 = >::from(this); - let other = >::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 { - 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 { - 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 { - 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 { - let from = f64::from(*self); - let to = f64::from(*other); - from.compute_distance(&to) + fn compute_squared_distance(&self, other: &Self) -> Result { + f64::from(*self).compute_squared_distance(&(*other).into()) } } @@ -1500,17 +1282,6 @@ impl Animatable for generic_position::Position Result { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - Ok(self.horizontal.compute_squared_distance(&other.horizontal)? + - self.vertical.compute_squared_distance(&other.vertical)?) - } } impl ToAnimatedZero for generic_position::Position @@ -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 { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - 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 { + // FIXME: This should be implemented. + Err(()) + } +} + impl ToAnimatedZero for TransformList { #[inline] fn to_animated_zero(&self) -> Result { @@ -2666,32 +2429,6 @@ impl Animatable for Either } } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - 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 { - 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 ToAnimatedZero for Either @@ -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 { - self.compute_squared_distance(other).map(|sq| sq.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - 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 { + 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 { - self.compute_squared_distance(other).map(|sq| sq.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { + fn compute_squared_distance(&self, other: &Self) -> Result { // 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 { - self.compute_squared_distance(other).map(|sq| sq.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - Ok(self.kind.compute_squared_distance(&other.kind)? + - self.fallback.compute_squared_distance(&other.fallback)?) + fn compute_squared_distance(&self, other: &Self) -> Result { + 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 { + fn compute_squared_distance(&self, other: &Self) -> Result { 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 Animatable for SVGLength } } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - match (self, other) { - (&SVGLength::Length(ref this), &SVGLength::Length(ref other)) => { - this.compute_distance(other) - } - _ => Err(()) - } - } } impl ToAnimatedZero for SVGLength where LengthType : ToAnimatedZero { @@ -3097,16 +2817,6 @@ impl Animatable for SVGStrokeDashArray } } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - match (self, other) { - (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => { - this.compute_distance(other) - } - _ => Err(()) - } - } } impl ToAnimatedZero for SVGStrokeDashArray @@ -3138,16 +2848,6 @@ impl Animatable for SVGOpacity } } } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - match (self, other) { - (&SVGOpacity::Opacity(ref this), &SVGOpacity::Opacity(ref other)) => { - this.compute_distance(other) - } - _ => Err(()) - } - } } impl ToAnimatedZero for SVGOpacity @@ -3246,9 +2946,7 @@ fn add_weighted_filter_function(from: Option<<&AnimatedFilter>, } } -fn compute_filter_square_distance(from: &AnimatedFilter, - to: &AnimatedFilter) - -> Result { +fn compute_filter_square_distance(from: &AnimatedFilter, to: &AnimatedFilter) -> Result { 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 { Ok(AnimatedFilterList(self.0.iter().chain(other.0.iter()).cloned().collect())) } +} +impl ComputeSquaredDistance for AnimatedFilterList { #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { + fn compute_squared_distance(&self, other: &Self) -> Result { 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 Animatable for NonNegative fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { self.0.add_weighted(&other.0, self_portion, other_portion).map(NonNegative::) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.0.compute_distance(&other.0) - } } impl ToAnimatedZero for NonNegative @@ -3405,11 +3093,6 @@ impl Animatable for GreaterThanOrEqualToOne fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { self.0.add_weighted(&other.0, self_portion, other_portion).map(GreaterThanOrEqualToOne::) } - - #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.0.compute_distance(&other.0) - } } impl ToAnimatedZero for GreaterThanOrEqualToOne diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 96ba9199bf2..1eae098655e 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -480,6 +480,8 @@ ${helpers.single_keyword_system("font-variant-caps", } pub mod computed_value { + use values::distance::{ComputeSquaredDistance, SquaredDistance}; + /// As of CSS Fonts Module Level 3, only the following values are /// valid: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 /// @@ -489,6 +491,13 @@ ${helpers.single_keyword_system("font-variant-caps", #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] pub struct T(pub u16); + impl ComputeSquaredDistance for T { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + self.0.compute_squared_distance(&other.0) + } + } + impl T { /// Value for normal pub fn normal() -> Self { @@ -1118,6 +1127,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 +1155,13 @@ ${helpers.single_keyword_system("font-variant-caps", _ => Err(()), } } + } + impl ComputeSquaredDistance for T { #[inline] - fn compute_distance(&self, other: &Self) -> Result { - match (*self, *other) { - (T::Number(ref number), T::Number(ref other)) => - number.compute_distance(other), + fn compute_squared_distance(&self, other: &Self) -> Result { + 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..d6aa1369bb3 100644 --- a/components/style/properties/longhand/inherited_table.mako.rs +++ b/components/style/properties/longhand/inherited_table.mako.rs @@ -28,6 +28,7 @@ ${helpers.single_keyword("caption-side", "top bottom", pub mod computed_value { use properties::animated_properties::Animatable; use values::animated::{ToAnimatedValue, ToAnimatedZero}; + use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::computed::NonNegativeAu; #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -49,16 +50,15 @@ ${helpers.single_keyword("caption-side", "top bottom", self_portion, other_portion)?, }) } + } + impl ComputeSquaredDistance for T { #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { - Ok(self.horizontal.compute_squared_distance(&other.horizontal)? + - self.vertical.compute_squared_distance(&other.vertical)?) + fn compute_squared_distance(&self, other: &Self) -> Result { + Ok( + self.horizontal.compute_squared_distance(&other.horizontal)? + + self.vertical.compute_squared_distance(&other.vertical)?, + ) } } diff --git a/components/style/values/animated/effects.rs b/components/style/values/animated/effects.rs index 9bd3456d028..5c4a02b519a 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 ComputeSquaredDistance for ShadowList { + #[inline] + fn compute_squared_distance(&self, _other: &Self) -> Result { + // FIXME: This should be implemented. + Err(()) + } +} + impl ToAnimatedZero for ShadowList { #[inline] fn to_animated_zero(&self) -> Result { @@ -140,14 +149,11 @@ impl Animatable for BoxShadow { inset: self.inset, }) } +} +impl ComputeSquaredDistance for BoxShadow { #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { + fn compute_squared_distance(&self, other: &Self) -> Result { if self.inset != other.inset { return Err(()); } @@ -219,14 +225,11 @@ impl Animatable for SimpleShadow { blur: blur, }) } +} +impl ComputeSquaredDistance for SimpleShadow { #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { + fn compute_squared_distance(&self, other: &Self) -> Result { Ok( self.color.compute_squared_distance(&other.color)? + self.horizontal.compute_squared_distance(&other.horizontal)? + diff --git a/components/style/values/computed/background.rs b/components/style/values/computed/background.rs index d2781ac1891..9bfb77c1523 100644 --- a/components/style/values/computed/background.rs +++ b/components/style/values/computed/background.rs @@ -8,6 +8,7 @@ use properties::animated_properties::{Animatable, RepeatableListAnimatable}; use properties::longhands::background_size::computed_value::T as BackgroundSizeList; use values::animated::{ToAnimatedValue, ToAnimatedZero}; use values::computed::length::LengthOrPercentageOrAuto; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::background::BackgroundSize as GenericBackgroundSize; /// A computed value for the `background-size` property. @@ -30,14 +31,11 @@ impl Animatable for BackgroundSize { _ => Err(()), } } +} +impl ComputeSquaredDistance for BackgroundSize { #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.compute_squared_distance(other).map(|sd| sd.sqrt()) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { + fn compute_squared_distance(&self, other: &Self) -> Result { match (self, other) { ( &GenericBackgroundSize::Explicit { width: self_width, height: self_height }, 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, } +impl ComputeSquaredDistance for CalcLengthOrPercentage { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + // 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 { + 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 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 { + 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) => { + >::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 { + 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) => { + >::compute_squared_distance(&(*this).into(), &(*other).into()) + } + } + } +} + impl LengthOrPercentageOrNone { /// Returns the used value. pub fn to_used_value(&self, containing_length: Au) -> Option { @@ -607,6 +671,22 @@ pub enum MozLength { ExtremumLength(ExtremumLength), } +impl ComputeSquaredDistance for MozLength { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + 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 { + 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..42f37b4ab22 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 { + // 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 { @@ -544,6 +554,18 @@ pub struct ClipRect { pub left: Option, } +impl ComputeSquaredDistance for ClipRect { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + Ok( + self.top.compute_squared_distance(&other.top)? + + self.right.compute_squared_distance(&other.right)? + + self.bottom.compute_squared_distance(&other.bottom)? + + self.left.compute_squared_distance(&other.left)?, + ) + } +} + impl ToCss for ClipRect { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { dest.write_str("rect(")?; @@ -653,6 +675,13 @@ impl From for NonNegativeAu { #[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))] pub struct Percentage(pub CSSFloat); +impl ComputeSquaredDistance for Percentage { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + self.0.compute_squared_distance(&other.0) + } +} + impl Percentage { /// 0% #[inline] diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs index 7376616ba5e..7db57907391 100644 --- a/components/style/values/computed/text.rs +++ b/components/style/values/computed/text.rs @@ -9,6 +9,7 @@ use values::{CSSInteger, CSSFloat}; use values::animated::ToAnimatedZero; use values::computed::{NonNegativeAu, NonNegativeNumber}; use values::computed::length::{Length, LengthOrPercentage}; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::text::InitialLetter as GenericInitialLetter; use values::generics::text::LineHeight as GenericLineHeight; use values::generics::text::Spacing; @@ -45,19 +46,25 @@ impl Animatable for LineHeight { _ => Err(()), } } +} +impl ComputeSquaredDistance for LineHeight { #[inline] - fn compute_distance(&self, other: &Self) -> Result { - match (*self, *other) { - (GenericLineHeight::Length(ref this), GenericLineHeight::Length(ref other)) => { - this.compute_distance(other) + fn compute_squared_distance(&self, other: &Self) -> Result { + match (self, other) { + (&GenericLineHeight::Length(ref this), &GenericLineHeight::Length(ref other)) => { + this.compute_squared_distance(other) }, - (GenericLineHeight::Number(ref this), GenericLineHeight::Number(ref other)) => { - this.compute_distance(other) + (&GenericLineHeight::Number(ref this), &GenericLineHeight::Number(ref other)) => { + this.compute_squared_distance(other) + }, + (&GenericLineHeight::Normal, &GenericLineHeight::Normal) => { + Ok(SquaredDistance::Value(0.)) }, - (GenericLineHeight::Normal, GenericLineHeight::Normal) => Ok(0.), #[cfg(feature = "gecko")] - (GenericLineHeight::MozBlockHeight, GenericLineHeight::MozBlockHeight) => Ok(0.), + (&GenericLineHeight::MozBlockHeight, &GenericLineHeight::MozBlockHeight) => { + Ok(SquaredDistance::Value(0.)) + }, _ => Err(()), } } diff --git a/components/style/values/computed/transform.rs b/components/style/values/computed/transform.rs index 5bf953d46be..c57bef639df 100644 --- a/components/style/values/computed/transform.rs +++ b/components/style/values/computed/transform.rs @@ -7,6 +7,7 @@ use properties::animated_properties::Animatable; use values::animated::ToAnimatedZero; use values::computed::{Length, LengthOrPercentage, Number, Percentage}; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::transform::TimingFunction as GenericTimingFunction; use values::generics::transform::TransformOrigin as GenericTransformOrigin; @@ -37,14 +38,11 @@ impl Animatable for TransformOrigin { self.depth.add_weighted(&other.depth, self_portion, other_portion)?, )) } +} +impl ComputeSquaredDistance for TransformOrigin { #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.compute_squared_distance(other).map(f64::sqrt) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { + fn compute_squared_distance(&self, other: &Self) -> Result { Ok( self.horizontal.compute_squared_distance(&other.horizontal)? + self.vertical.compute_squared_distance(&other.vertical)? + 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; +} + +/// 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 { + Ok(SquaredDistance::Sqrt(((*self as f64) - (*other as f64)).abs())) + } +} + +impl ComputeSquaredDistance for i32 { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + Ok(SquaredDistance::Sqrt((*self - *other).abs() as f64)) + } +} + +impl ComputeSquaredDistance for f32 { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + Ok(SquaredDistance::Sqrt((*self - *other).abs() as f64)) + } +} + +impl ComputeSquaredDistance for f64 { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + Ok(SquaredDistance::Sqrt((*self - *other).abs())) + } +} + +impl ComputeSquaredDistance for Au { + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + self.0.compute_squared_distance(&other.0) + } +} + +impl ComputeSquaredDistance for Option + where T: ComputeSquaredDistance +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + match (self.as_ref(), other.as_ref()) { + (Some(this), Some(other)) => this.compute_squared_distance(other), + (None, None) => Ok(SquaredDistance::Value(0.)), + _ => Err(()), + } + } +} + +impl ComputeSquaredDistance for Size2D + where T: ComputeSquaredDistance +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + 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 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(mut iter: I) -> Self + where + I: Iterator, + { + 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/basic_shape.rs b/components/style/values/generics/basic_shape.rs index f218fd41ba8..cf1437ce8d0 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; @@ -144,20 +145,16 @@ where _ => Err(()), } } +} - fn compute_distance(&self, other: &Self) -> Result { - 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 { +// FIXME(nox): Implement ComputeSquaredDistance for T types and stop +// using PartialEq here. +impl ComputeSquaredDistance for ShapeSource +where + B: ComputeSquaredDistance, + T: PartialEq, +{ + fn compute_squared_distance(&self, other: &Self) -> Result { match (self, other) { ( &ShapeSource::Shape(ref this, ref this_box), @@ -209,26 +206,15 @@ where _ => Err(()), } } +} - fn compute_distance(&self, other: &Self) -> Result { - 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 { +impl ComputeSquaredDistance for BasicShape +where + H: ComputeSquaredDistance, + V: ComputeSquaredDistance, + L: ComputeSquaredDistance + Copy, +{ + fn compute_squared_distance(&self, other: &Self) -> Result { match (self, other) { (&BasicShape::Circle(ref this), &BasicShape::Circle(ref other)) => { this.compute_squared_distance(other) @@ -261,12 +247,13 @@ where let round = self.round.add_weighted(&other.round, self_portion, other_portion)?; Ok(InsetRect { rect, round }) } +} - fn compute_distance(&self, other: &Self) -> Result { - Ok(self.compute_squared_distance(other)?.sqrt()) - } - - fn compute_squared_distance(&self, other: &Self) -> Result { +impl ComputeSquaredDistance for InsetRect +where + L: ComputeSquaredDistance + Copy, +{ + fn compute_squared_distance(&self, other: &Self) -> Result { Ok( self.rect.compute_squared_distance(&other.rect)? + self.round.compute_squared_distance(&other.round)?, @@ -304,12 +291,15 @@ where let radius = self.radius.add_weighted(&other.radius, self_portion, other_portion)?; Ok(Circle { position, radius }) } +} - fn compute_distance(&self, other: &Self) -> Result { - Ok(self.compute_squared_distance(other)?.sqrt()) - } - - fn compute_squared_distance(&self, other: &Self) -> Result { +impl ComputeSquaredDistance for Circle +where + H: ComputeSquaredDistance, + V: ComputeSquaredDistance, + L: ComputeSquaredDistance, +{ + fn compute_squared_distance(&self, other: &Self) -> Result { Ok( self.position.compute_squared_distance(&other.position)? + self.radius.compute_squared_distance(&other.radius)?, @@ -334,12 +324,15 @@ 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 { - Ok(self.compute_squared_distance(other)?.sqrt()) - } - - fn compute_squared_distance(&self, other: &Self) -> Result { +impl ComputeSquaredDistance for Ellipse +where + H: ComputeSquaredDistance, + V: ComputeSquaredDistance, + L: ComputeSquaredDistance, +{ + fn compute_squared_distance(&self, other: &Self) -> Result { Ok( self.position.compute_squared_distance(&other.position)? + self.semiaxis_x.compute_squared_distance(&other.semiaxis_x)? + @@ -365,17 +358,13 @@ where _ => Err(()), } } +} - fn compute_distance(&self, other: &Self) -> Result { - match (self, other) { - (&ShapeRadius::Length(ref this), &ShapeRadius::Length(ref other)) => { - this.compute_distance(other) - }, - _ => Err(()), - } - } - - fn compute_squared_distance(&self, other: &Self) -> Result { +impl ComputeSquaredDistance for ShapeRadius +where + L: ComputeSquaredDistance, +{ + fn compute_squared_distance(&self, other: &Self) -> Result { match (self, other) { (&ShapeRadius::Length(ref this), &ShapeRadius::Length(ref other)) => { this.compute_squared_distance(other) @@ -413,12 +402,13 @@ where }).collect::, _>>()?; Ok(Polygon { fill: self.fill, coordinates }) } +} - fn compute_distance(&self, other: &Self) -> Result { - Ok(self.compute_squared_distance(other)?.sqrt()) - } - - fn compute_squared_distance(&self, other: &Self) -> Result { +impl ComputeSquaredDistance for Polygon +where + L: ComputeSquaredDistance, +{ + fn compute_squared_distance(&self, other: &Self) -> Result { if self.fill != other.fill { return Err(()); } @@ -426,9 +416,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..9b66ce23dc2 100644 --- a/components/style/values/generics/border.rs +++ b/components/style/values/generics/border.rs @@ -8,6 +8,7 @@ use euclid::Size2D; use properties::animated_properties::Animatable; use std::fmt; use style_traits::ToCss; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::rect::Rect; /// A generic value for a single side of a `border-image-width` property. @@ -129,12 +130,13 @@ 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 { - Ok(self.compute_squared_distance(other)?.sqrt()) - } - - fn compute_squared_distance(&self, other: &Self) -> Result { +impl ComputeSquaredDistance for BorderRadius +where + L: ComputeSquaredDistance + Copy, +{ + fn compute_squared_distance(&self, other: &Self) -> Result { Ok( self.top_left.compute_squared_distance(&other.top_left)? + self.top_right.compute_squared_distance(&other.top_right)? + @@ -189,14 +191,14 @@ where ) -> Result { Ok(BorderCornerRadius(self.0.add_weighted(&other.0, self_portion, other_portion)?)) } +} +impl ComputeSquaredDistance for BorderCornerRadius +where + L: ComputeSquaredDistance + Copy, +{ #[inline] - fn compute_distance(&self, other: &Self) -> Result { - self.0.compute_distance(&other.0) - } - - #[inline] - fn compute_squared_distance(&self, other: &Self) -> Result { + fn compute_squared_distance(&self, other: &Self) -> Result { self.0.compute_squared_distance(&other.0) } } diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index 075ce2c4715..689dd95f1d1 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -11,6 +11,7 @@ use parser::{Parse, ParserContext}; use std::fmt; use style_traits::{Comma, OneOrMoreSeparated, ParseError, StyleParseError, ToCss}; use super::CustomIdent; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; pub mod background; pub mod basic_shape; @@ -271,7 +272,27 @@ impl ToCss for FontSettingTagFloat { #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] pub struct NonNegative(pub T); +impl ComputeSquaredDistance for NonNegative +where + T: ComputeSquaredDistance, +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + self.0.compute_squared_distance(&other.0) + } +} + /// 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))] pub struct GreaterThanOrEqualToOne(pub T); + +impl ComputeSquaredDistance for GreaterThanOrEqualToOne +where + T: ComputeSquaredDistance, +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + self.0.compute_squared_distance(&other.0) + } +} diff --git a/components/style/values/generics/position.rs b/components/style/values/generics/position.rs index 4ce5b63ab30..0c641a62daa 100644 --- a/components/style/values/generics/position.rs +++ b/components/style/values/generics/position.rs @@ -5,6 +5,8 @@ //! Generic types for CSS handling of specified and computed values of //! [`position`](https://drafts.csswg.org/css-backgrounds-3/#position) +use values::distance::{ComputeSquaredDistance, SquaredDistance}; + #[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). @@ -24,3 +26,17 @@ impl Position { } } } + +impl ComputeSquaredDistance for Position +where + H: ComputeSquaredDistance, + V: ComputeSquaredDistance, +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + Ok( + self.horizontal.compute_squared_distance(&other.horizontal)? + + self.vertical.compute_squared_distance(&other.vertical)?, + ) + } +} diff --git a/components/style/values/generics/rect.rs b/components/style/values/generics/rect.rs index 3dc9e095890..b948cf88f82 100644 --- a/components/style/values/generics/rect.rs +++ b/components/style/values/generics/rect.rs @@ -9,6 +9,7 @@ use parser::{Parse, ParserContext}; use properties::animated_properties::Animatable; use std::fmt; use style_traits::{ToCss, ParseError}; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; /// 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`. @@ -70,6 +71,21 @@ where } } +impl ComputeSquaredDistance for Rect +where + L: ComputeSquaredDistance, +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + Ok( + self.0.compute_squared_distance(&other.0)? + + self.1.compute_squared_distance(&other.1)? + + self.2.compute_squared_distance(&other.2)? + + self.3.compute_squared_distance(&other.3)?, + ) + } +} + impl From for Rect where T: Clone { diff --git a/components/style/values/generics/svg.rs b/components/style/values/generics/svg.rs index 05e0e66248f..079bce1f1c1 100644 --- a/components/style/values/generics/svg.rs +++ b/components/style/values/generics/svg.rs @@ -6,8 +6,10 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; +use properties::animated_properties::RepeatableListAnimatable; use std::fmt; use style_traits::{ParseError, StyleParseError, ToCss}; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; /// An SVG paint value /// @@ -105,6 +107,25 @@ pub enum SVGLength { ContextValue, } +impl ComputeSquaredDistance for SVGLength +where + L: ComputeSquaredDistance, +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + match (self, other) { + (&SVGLength::Length(ref this), &SVGLength::Length(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(()) + }, + } + } +} + /// Generic value for stroke-dasharray. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Clone, Debug, PartialEq, HasViewportPercentage, ToAnimatedValue, ToComputedValue)] @@ -115,6 +136,25 @@ pub enum SVGStrokeDashArray { ContextValue, } +impl ComputeSquaredDistance for SVGStrokeDashArray +where + L: ComputeSquaredDistance + RepeatableListAnimatable, +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + match (self, other) { + (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(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(()) + }, + } + } +} + impl ToCss for SVGStrokeDashArray where LengthType: ToCss { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match self { @@ -150,3 +190,22 @@ pub enum SVGOpacity { /// `context-stroke-opacity` ContextStrokeOpacity, } + +impl ComputeSquaredDistance for SVGOpacity +where + L: ComputeSquaredDistance, +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + match (self, other) { + (&SVGOpacity::Opacity(ref this), &SVGOpacity::Opacity(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(()) + }, + } + } +} diff --git a/components/style/values/generics/text.rs b/components/style/values/generics/text.rs index 94be4a6db8c..3330d0dbceb 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 Animatable for Spacing let other = other.value().unwrap_or(&zero); this.add_weighted(other, self_portion, other_portion).map(Spacing::Value) } +} +impl ComputeSquaredDistance for Spacing +where + V: ComputeSquaredDistance + From, +{ #[inline] - fn compute_distance(&self, other: &Self) -> Result { - let zero = Value::from(Au(0)); + fn compute_squared_distance(&self, other: &Self) -> Result { + 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) } } diff --git a/components/style/values/mod.rs b/components/style/values/mod.rs index 616aa5cb485..4a2c20b3b18 100644 --- a/components/style/values/mod.rs +++ b/components/style/values/mod.rs @@ -16,9 +16,11 @@ use std::ascii::AsciiExt; use std::fmt::{self, Debug}; use std::hash; use style_traits::{ToCss, ParseError, StyleParseError}; +use values::distance::{ComputeSquaredDistance, SquaredDistance}; pub mod animated; pub mod computed; +pub mod distance; pub mod generics; pub mod specified; @@ -59,6 +61,25 @@ pub enum Either { Second(B), } +impl ComputeSquaredDistance for Either +where + A: ComputeSquaredDistance, + B: ComputeSquaredDistance, +{ + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result { + 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 Debug for Either { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index d20fce6fb13..821c73d6942 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -126,6 +126,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; @@ -375,7 +376,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]