From 2f07b29296e06708f22d749b05ef43c29fc14168 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Mon, 15 May 2017 12:26:21 +0900 Subject: [PATCH] Rewrite interpolate() in terms of a more general add_weighted() function Generalizing the procedure like this will allow us to re-use it for addition of most types. --- components/style/properties/helpers.mako.rs | 14 +- .../helpers/animated_properties.mako.rs | 409 ++++++++++-------- .../properties/longhand/background.mako.rs | 9 +- .../style/properties/longhand/box.mako.rs | 11 +- .../style/properties/longhand/font.mako.rs | 6 +- .../longhand/inherited_table.mako.rs | 9 +- components/style/values/mod.rs | 3 +- 7 files changed, 261 insertions(+), 200 deletions(-) diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 6adce11d9c1..efad8cdc196 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -113,8 +113,9 @@ % if delegate_animate: use properties::animated_properties::Animatable; impl Animatable for T { - fn interpolate(&self, other: &Self, progress: f64) -> Result { - self.0.interpolate(&other.0, progress).map(T) + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result { + self.0.add_weighted(&other.0, self_portion, other_portion).map(T) } #[inline] @@ -976,16 +977,17 @@ <%def name="impl_animatable_for_option_tuple(value_for_none)"> impl Animatable for T { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result { match (self, other) { (&T(Some(ref this)), &T(Some(ref other))) => { - Ok(T(this.interpolate(other, progress).ok())) + Ok(T(this.add_weighted(other, self_portion, other_portion).ok())) }, (&T(Some(ref this)), &T(None)) => { - Ok(T(this.interpolate(&${value_for_none}, progress).ok())) + Ok(T(this.add_weighted(&${value_for_none}, self_portion, other_portion).ok())) }, (&T(None), &T(Some(ref other))) => { - Ok(T(${value_for_none}.interpolate(other, progress).ok())) + Ok(T(${value_for_none}.add_weighted(other, self_portion, other_portion).ok())) }, (&T(None), &T(None)) => { Ok(T(None)) diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 9d75c63d7eb..322155e8bce 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -581,27 +581,28 @@ impl AnimationValue { } impl Animatable for AnimationValue { - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result { match (self, other) { % for prop in data.longhands: % if prop.animatable: (&AnimationValue::${prop.camel_case}(ref from), &AnimationValue::${prop.camel_case}(ref to)) => { - // https://w3c.github.io/web-animations/#discrete-animation-type % if prop.animation_value_type == "discrete": - if progress < 0.5 { + if self_portion > other_portion { Ok(AnimationValue::${prop.camel_case}(*from)) } else { Ok(AnimationValue::${prop.camel_case}(*to)) } % else: - from.interpolate(to, progress).map(AnimationValue::${prop.camel_case}) + from.add_weighted(to, self_portion, other_portion) + .map(AnimationValue::${prop.camel_case}) % endif } % endif % endfor _ => { - panic!("Expected interpolation of computed values of the same \ + panic!("Expected weighted addition of computed values of the same \ property, got: {:?}, {:?}", self, other); } } @@ -635,10 +636,17 @@ impl Animatable for AnimationValue { /// A trait used to implement various procedures used during animation. pub trait Animatable: Sized { + /// Performs a weighted sum of this value and |other|. This is used for + /// interpolation and addition of animation values. + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result; + /// [Interpolates][interpolation] a value with another for a given property. /// /// [interpolation]: https://w3c.github.io/web-animations/#animation-interpolation - fn interpolate(&self, other: &Self, progress: f64) -> Result; + fn interpolate(&self, other: &Self, progress: f64) -> Result { + self.add_weighted(other, 1.0 - progress, progress) + } /// Compute distance between a value and another for a given property. fn compute_distance(&self, _other: &Self) -> Result { Err(()) } @@ -658,11 +666,12 @@ impl RepeatableListAnimatable for LengthOrPercentage {} impl RepeatableListAnimatable for Either {} impl Animatable for SmallVec<[T; 1]> { - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result { use num_integer::lcm; let len = lcm(self.len(), other.len()); self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(me, you)| { - me.interpolate(you, progress) + me.add_weighted(you, self_portion, other_portion) }).collect() } @@ -684,8 +693,8 @@ impl Animatable for SmallVec<[T; 1]> { /// https://drafts.csswg.org/css-transitions/#animtype-number impl Animatable for Au { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { - Ok(Au((self.0 as f64 + (other.0 as f64 - self.0 as f64) * progress).round() as i32)) + 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] @@ -698,10 +707,10 @@ impl Animatable for Option where T: Animatable, { #[inline] - fn interpolate(&self, other: &Option, progress: f64) -> Result, ()> { + fn add_weighted(&self, other: &Option, self_portion: f64, other_portion: f64) -> Result, ()> { match (self, other) { (&Some(ref this), &Some(ref other)) => { - Ok(this.interpolate(other, progress).ok()) + Ok(this.add_weighted(other, self_portion, other_portion).ok()) } _ => Err(()), } @@ -731,8 +740,8 @@ impl Animatable for Option /// https://drafts.csswg.org/css-transitions/#animtype-number impl Animatable for f32 { #[inline] - fn interpolate(&self, other: &f32, progress: f64) -> Result { - Ok(((*self as f64) + ((*other as f64) - (*self as f64)) * progress) as 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] @@ -744,8 +753,8 @@ impl Animatable for f32 { /// https://drafts.csswg.org/css-transitions/#animtype-number impl Animatable for f64 { #[inline] - fn interpolate(&self, other: &f64, progress: f64) -> Result { - Ok(*self + (*other - *self) * progress) + fn add_weighted(&self, other: &f64, self_portion: f64, other_portion: f64) -> Result { + Ok(*self * self_portion + *other * other_portion) } #[inline] @@ -757,10 +766,8 @@ impl Animatable for f64 { /// https://drafts.csswg.org/css-transitions/#animtype-integer impl Animatable for i32 { #[inline] - fn interpolate(&self, other: &i32, progress: f64) -> Result { - let a = *self as f64; - let b = *other as f64; - Ok((a + (b - a) * progress).round() as 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] @@ -772,20 +779,23 @@ impl Animatable for i32 { /// https://drafts.csswg.org/css-transitions/#animtype-number impl Animatable for Angle { #[inline] - fn interpolate(&self, other: &Angle, progress: f64) -> Result { - self.radians().interpolate(&other.radians(), progress).map(Angle::from_radians) + fn add_weighted(&self, other: &Angle, self_portion: f64, other_portion: f64) -> Result { + self.radians() + .add_weighted(&other.radians(), self_portion, other_portion) + .map(Angle::from_radians) } } /// https://drafts.csswg.org/css-transitions/#animtype-visibility impl Animatable for Visibility { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { match (*self, *other) { (Visibility::visible, _) | (_, Visibility::visible) => { - Ok(if progress >= 0.0 && progress <= 1.0 { + Ok(if self_portion >= 0.0 && self_portion <= 1.0 && + other_portion >= 0.0 && other_portion <= 1.0 { Visibility::visible - } else if progress < 0.0 { + } else if self_portion > other_portion { *self } else { *other @@ -807,9 +817,9 @@ impl Animatable for Visibility { impl Animatable for Size2D { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { - let width = try!(self.width.interpolate(&other.width, progress)); - let height = try!(self.height.interpolate(&other.height, progress)); + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + let width = try!(self.width.add_weighted(&other.width, self_portion, other_portion)); + let height = try!(self.height.add_weighted(&other.height, self_portion, other_portion)); Ok(Size2D::new(width, height)) } @@ -817,9 +827,9 @@ impl Animatable for Size2D { impl Animatable for Point2D { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { - let x = try!(self.x.interpolate(&other.x, progress)); - let y = try!(self.y.interpolate(&other.y, progress)); + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + let x = try!(self.x.add_weighted(&other.x, self_portion, other_portion)); + let y = try!(self.y.add_weighted(&other.y, self_portion, other_portion)); Ok(Point2D::new(x, y)) } @@ -827,8 +837,8 @@ impl Animatable for Point2D { impl Animatable for BorderRadiusSize { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { - self.0.interpolate(&other.0, progress).map(generics::BorderRadiusSize) + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + self.0.add_weighted(&other.0, self_portion, other_portion).map(generics::BorderRadiusSize) } #[inline] @@ -846,11 +856,11 @@ impl Animatable for BorderRadiusSize { /// https://drafts.csswg.org/css-transitions/#animtype-length impl Animatable for VerticalAlign { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { match (*self, *other) { (VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref this)), VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(ref other))) => { - this.interpolate(other, progress).map(|value| { + this.add_weighted(other, self_portion, other_portion).map(|value| { VerticalAlign::LengthOrPercentage(LengthOrPercentage::Length(value)) }) } @@ -872,8 +882,8 @@ impl Animatable for VerticalAlign { impl Animatable for BackgroundSizeList { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { - self.0.interpolate(&other.0, progress).map(BackgroundSizeList) + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + self.0.add_weighted(&other.0, self_portion, other_portion).map(BackgroundSizeList) } #[inline] @@ -890,24 +900,28 @@ impl Animatable for BackgroundSizeList { /// https://drafts.csswg.org/css-transitions/#animtype-color impl Animatable for RGBA { #[inline] - fn interpolate(&self, other: &RGBA, progress: f64) -> Result { + fn add_weighted(&self, other: &RGBA, self_portion: f64, other_portion: f64) -> Result { fn clamp(val: f32) -> f32 { val.max(0.).min(1.) } - let alpha = clamp(try!(self.alpha_f32().interpolate(&other.alpha_f32(), progress))); + let alpha = clamp(try!(self.alpha_f32().add_weighted(&other.alpha_f32(), + self_portion, other_portion))); if alpha == 0. { Ok(RGBA::transparent()) } else { // NB: We rely on RGBA::from_floats clamping already. let red = try!((self.red_f32() * self.alpha_f32()) - .interpolate(&(other.red_f32() * other.alpha_f32()), progress)) + .add_weighted(&(other.red_f32() * other.alpha_f32()), + self_portion, other_portion)) * 1. / alpha; let green = try!((self.green_f32() * self.alpha_f32()) - .interpolate(&(other.green_f32() * other.alpha_f32()), progress)) + .add_weighted(&(other.green_f32() * other.alpha_f32()), + self_portion, other_portion)) * 1. / alpha; let blue = try!((self.blue_f32() * self.alpha_f32()) - .interpolate(&(other.blue_f32() * other.alpha_f32()), progress)) + .add_weighted(&(other.blue_f32() * other.alpha_f32()), + self_portion, other_portion)) * 1. / alpha; Ok(RGBA::from_floats(red, green, blue, alpha)) } @@ -948,10 +962,10 @@ impl Animatable for RGBA { /// https://drafts.csswg.org/css-transitions/#animtype-color impl Animatable for CSSParserColor { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { match (*self, *other) { (CSSParserColor::RGBA(ref this), CSSParserColor::RGBA(ref other)) => { - this.interpolate(other, progress).map(CSSParserColor::RGBA) + this.add_weighted(other, self_portion, other_portion).map(CSSParserColor::RGBA) } _ => Err(()), } @@ -976,11 +990,12 @@ impl Animatable for CSSParserColor { /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc impl Animatable for CalcLengthOrPercentage { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { - fn interpolate_half(this: Option, - other: Option, - progress: f64) - -> Result, ()> + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { + fn add_weighted_half(this: Option, + other: Option, + self_portion: f64, + other_portion: f64) + -> Result, ()> where T: Default + Animatable, { match (this, other) { @@ -988,14 +1003,15 @@ impl Animatable for CalcLengthOrPercentage { (this, other) => { let this = this.unwrap_or(T::default()); let other = other.unwrap_or(T::default()); - this.interpolate(&other, progress).map(Some) + this.add_weighted(&other, self_portion, other_portion).map(Some) } } } Ok(CalcLengthOrPercentage { - length: try!(self.length.interpolate(&other.length, progress)), - percentage: try!(interpolate_half(self.percentage, other.percentage, progress)), + length: try!(self.length.add_weighted(&other.length, self_portion, other_portion)), + percentage: try!(add_weighted_half(self.percentage, other.percentage, + self_portion, other_portion)), }) } @@ -1015,20 +1031,22 @@ impl Animatable for CalcLengthOrPercentage { /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc impl Animatable for LengthOrPercentage { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { match (*self, *other) { (LengthOrPercentage::Length(ref this), LengthOrPercentage::Length(ref other)) => { - this.interpolate(other, progress).map(LengthOrPercentage::Length) + this.add_weighted(other, self_portion, other_portion) + .map(LengthOrPercentage::Length) } (LengthOrPercentage::Percentage(ref this), LengthOrPercentage::Percentage(ref other)) => { - this.interpolate(other, progress).map(LengthOrPercentage::Percentage) + this.add_weighted(other, self_portion, other_portion) + .map(LengthOrPercentage::Percentage) } (this, other) => { let this: CalcLengthOrPercentage = From::from(this); let other: CalcLengthOrPercentage = From::from(other); - this.interpolate(&other, progress) + this.add_weighted(&other, self_portion, other_portion) .map(LengthOrPercentage::Calc) } } @@ -1080,15 +1098,17 @@ impl Animatable for LengthOrPercentage { /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc impl Animatable for LengthOrPercentageOrAuto { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { match (*self, *other) { (LengthOrPercentageOrAuto::Length(ref this), LengthOrPercentageOrAuto::Length(ref other)) => { - this.interpolate(other, progress).map(LengthOrPercentageOrAuto::Length) + this.add_weighted(other, self_portion, other_portion) + .map(LengthOrPercentageOrAuto::Length) } (LengthOrPercentageOrAuto::Percentage(ref this), LengthOrPercentageOrAuto::Percentage(ref other)) => { - this.interpolate(other, progress).map(LengthOrPercentageOrAuto::Percentage) + this.add_weighted(other, self_portion, other_portion) + .map(LengthOrPercentageOrAuto::Percentage) } (LengthOrPercentageOrAuto::Auto, LengthOrPercentageOrAuto::Auto) => { Ok(LengthOrPercentageOrAuto::Auto) @@ -1096,7 +1116,7 @@ impl Animatable for LengthOrPercentageOrAuto { (this, other) => { let this: Option = From::from(this); let other: Option = From::from(other); - match this.interpolate(&other, progress) { + match this.add_weighted(&other, self_portion, other_portion) { Ok(Some(result)) => Ok(LengthOrPercentageOrAuto::Calc(result)), _ => Err(()), } @@ -1155,15 +1175,17 @@ impl Animatable for LengthOrPercentageOrAuto { /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc impl Animatable for LengthOrPercentageOrNone { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { match (*self, *other) { (LengthOrPercentageOrNone::Length(ref this), LengthOrPercentageOrNone::Length(ref other)) => { - this.interpolate(other, progress).map(LengthOrPercentageOrNone::Length) + this.add_weighted(other, self_portion, other_portion) + .map(LengthOrPercentageOrNone::Length) } (LengthOrPercentageOrNone::Percentage(ref this), LengthOrPercentageOrNone::Percentage(ref other)) => { - this.interpolate(other, progress).map(LengthOrPercentageOrNone::Percentage) + this.add_weighted(other, self_portion, other_portion) + .map(LengthOrPercentageOrNone::Percentage) } (LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => { Ok(LengthOrPercentageOrNone::None) @@ -1191,11 +1213,12 @@ impl Animatable for LengthOrPercentageOrNone { /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc impl Animatable for MinLength { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { match (*self, *other) { (MinLength::LengthOrPercentage(ref this), MinLength::LengthOrPercentage(ref other)) => { - this.interpolate(other, progress).map(MinLength::LengthOrPercentage) + this.add_weighted(other, self_portion, other_portion) + .map(MinLength::LengthOrPercentage) } _ => Err(()), } @@ -1216,11 +1239,12 @@ impl Animatable for MinLength { /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc impl Animatable for MaxLength { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { match (*self, *other) { (MaxLength::LengthOrPercentage(ref this), MaxLength::LengthOrPercentage(ref other)) => { - this.interpolate(other, progress).map(MaxLength::LengthOrPercentage) + this.add_weighted(other, self_portion, other_portion) + .map(MaxLength::LengthOrPercentage) } _ => Err(()), } @@ -1242,15 +1266,15 @@ impl Animatable for MaxLength { /// https://drafts.csswg.org/css-transitions/#animtype-length impl Animatable for LineHeight { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { match (*self, *other) { (LineHeight::Length(ref this), LineHeight::Length(ref other)) => { - this.interpolate(other, progress).map(LineHeight::Length) + this.add_weighted(other, self_portion, other_portion).map(LineHeight::Length) } (LineHeight::Number(ref this), LineHeight::Number(ref other)) => { - this.interpolate(other, progress).map(LineHeight::Number) + this.add_weighted(other, self_portion, other_portion).map(LineHeight::Number) } (LineHeight::Normal, LineHeight::Normal) => { Ok(LineHeight::Normal) @@ -1278,10 +1302,10 @@ impl Animatable for LineHeight { /// http://dev.w3.org/csswg/css-transitions/#animtype-font-weight impl Animatable for FontWeight { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { let a = (*self as u32) as f64; let b = (*other as u32) as f64; - let weight = a + (b - a) * progress; + let weight = a * self_portion + b * other_portion; Ok(if weight < 150. { FontWeight::Weight100 } else if weight < 250. { @@ -1314,10 +1338,12 @@ impl Animatable for FontWeight { /// https://drafts.csswg.org/css-transitions/#animtype-simple-list impl Animatable for generic_position::Position { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { Ok(generic_position::Position { - horizontal: try!(self.horizontal.interpolate(&other.horizontal, progress)), - vertical: try!(self.vertical.interpolate(&other.vertical, progress)), + horizontal: try!(self.horizontal.add_weighted(&other.horizontal, + self_portion, other_portion)), + vertical: try!(self.vertical.add_weighted(&other.vertical, + self_portion, other_portion)), }) } @@ -1339,12 +1365,13 @@ impl RepeatableListAnimatable for generic_position::Position /// https://drafts.csswg.org/css-transitions/#animtype-rect impl Animatable for ClipRect { #[inline] - fn interpolate(&self, other: &Self, time: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result { Ok(ClipRect { - top: try!(self.top.interpolate(&other.top, time)), - right: try!(self.right.interpolate(&other.right, time)), - bottom: try!(self.bottom.interpolate(&other.bottom, time)), - left: try!(self.left.interpolate(&other.left, time)), + top: try!(self.top.add_weighted(&other.top, self_portion, other_portion)), + right: try!(self.right.add_weighted(&other.right, self_portion, other_portion)), + bottom: try!(self.bottom.add_weighted(&other.bottom, self_portion, other_portion)), + left: try!(self.left.add_weighted(&other.left, self_portion, other_portion)), }) } @@ -1366,7 +1393,7 @@ impl Animatable for ClipRect { <%def name="impl_animatable_for_shadow(item, transparent_color)"> impl Animatable for ${item} { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { % if "Box" in item: // It can't be interpolated if inset does not match. if self.inset != other.inset { @@ -1374,12 +1401,14 @@ impl Animatable for ClipRect { } % endif - let x = try!(self.offset_x.interpolate(&other.offset_x, progress)); - let y = try!(self.offset_y.interpolate(&other.offset_y, progress)); - let color = try!(self.color.interpolate(&other.color, progress)); - let blur = try!(self.blur_radius.interpolate(&other.blur_radius, progress)); + let x = try!(self.offset_x.add_weighted(&other.offset_x, self_portion, other_portion)); + let y = try!(self.offset_y.add_weighted(&other.offset_y, self_portion, other_portion)); + let color = try!(self.color.add_weighted(&other.color, self_portion, other_portion)); + let blur = try!(self.blur_radius.add_weighted(&other.blur_radius, + self_portion, other_portion)); % if "Box" in item: - let spread = try!(self.spread_radius.interpolate(&other.spread_radius, progress)); + let spread = try!(self.spread_radius.add_weighted(&other.spread_radius, + self_portion, other_portion)); % endif Ok(${item} { @@ -1421,7 +1450,7 @@ impl Animatable for ClipRect { /// https://drafts.csswg.org/css-transitions/#animtype-shadow-list impl Animatable for ${item}List { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { // The inset value must change % if "Box" in item: let mut zero = ${item} { @@ -1449,18 +1478,18 @@ impl Animatable for ClipRect { for i in 0..max_len { let shadow = match (self.0.get(i), other.0.get(i)) { (Some(shadow), Some(other)) - => try!(shadow.interpolate(other, progress)), + => try!(shadow.add_weighted(other, self_portion, other_portion)), (Some(shadow), None) => { % if "Box" in item: zero.inset = shadow.inset; % endif - shadow.interpolate(&zero, progress).unwrap() + shadow.add_weighted(&zero, self_portion, other_portion).unwrap() } (None, Some(shadow)) => { % if "Box" in item: zero.inset = shadow.inset; % endif - zero.interpolate(&shadow, progress).unwrap() + zero.add_weighted(&shadow, self_portion, other_portion).unwrap() } (None, None) => unreachable!(), }; @@ -1541,11 +1570,12 @@ fn build_identity_transform_list(list: &[TransformOperation]) -> Vec TransformList { +fn add_weighted_transform_lists(from_list: &[TransformOperation], + to_list: &[TransformOperation], + self_portion: f64, + other_portion: f64) -> TransformList { let mut result = vec![]; if can_interpolate_list(from_list, to_list) { @@ -1553,33 +1583,33 @@ fn interpolate_transform_list(from_list: &[TransformOperation], match (from, to) { (&TransformOperation::Matrix(from), &TransformOperation::Matrix(_to)) => { - let interpolated = from.interpolate(&_to, progress).unwrap(); - result.push(TransformOperation::Matrix(interpolated)); + let sum = from.add_weighted(&_to, self_portion, other_portion).unwrap(); + result.push(TransformOperation::Matrix(sum)); } (&TransformOperation::MatrixWithPercents(_), &TransformOperation::MatrixWithPercents(_)) => { - // We don't interpolate `-moz-transform` matrices yet. + // We don't add_weighted `-moz-transform` matrices yet. // They contain percentage values. {} } (&TransformOperation::Skew(fx, fy), &TransformOperation::Skew(tx, ty)) => { - let ix = fx.interpolate(&tx, progress).unwrap(); - let iy = fy.interpolate(&ty, progress).unwrap(); + let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap(); + let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap(); result.push(TransformOperation::Skew(ix, iy)); } (&TransformOperation::Translate(fx, fy, fz), &TransformOperation::Translate(tx, ty, tz)) => { - let ix = fx.interpolate(&tx, progress).unwrap(); - let iy = fy.interpolate(&ty, progress).unwrap(); - let iz = fz.interpolate(&tz, progress).unwrap(); + let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap(); + let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap(); + let iz = fz.add_weighted(&tz, self_portion, other_portion).unwrap(); result.push(TransformOperation::Translate(ix, iy, iz)); } (&TransformOperation::Scale(fx, fy, fz), &TransformOperation::Scale(tx, ty, tz)) => { - let ix = fx.interpolate(&tx, progress).unwrap(); - let iy = fy.interpolate(&ty, progress).unwrap(); - let iz = fz.interpolate(&tz, progress).unwrap(); + let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap(); + let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap(); + let iz = fz.add_weighted(&tz, self_portion, other_portion).unwrap(); result.push(TransformOperation::Scale(ix, iy, iz)); } (&TransformOperation::Rotate(fx, fy, fz, fa), @@ -1589,14 +1619,15 @@ fn interpolate_transform_list(from_list: &[TransformOperation], let (fx, fy, fz) = (fx / norm_f, fy / norm_f, fz / norm_f); let (tx, ty, tz) = (tx / norm_t, ty / norm_t, tz / norm_t); if fx == tx && fy == ty && fz == tz { - let ia = fa.interpolate(&ta, progress).unwrap(); + let ia = fa.add_weighted(&ta, self_portion, other_portion).unwrap(); result.push(TransformOperation::Rotate(fx, fy, fz, ia)); } else { let matrix_f = rotate_to_matrix(fx, fy, fz, fa); let matrix_t = rotate_to_matrix(tx, ty, tz, ta); - let interpolated = matrix_f.interpolate(&matrix_t, progress).unwrap(); + let sum = matrix_f.add_weighted(&matrix_t, self_portion, other_portion) + .unwrap(); - result.push(TransformOperation::Matrix(interpolated)); + result.push(TransformOperation::Matrix(sum)); } } (&TransformOperation::Perspective(fd), @@ -1605,8 +1636,9 @@ fn interpolate_transform_list(from_list: &[TransformOperation], let mut td_matrix = ComputedMatrix::identity(); fd_matrix.m43 = -1. / fd.to_f32_px(); td_matrix.m43 = -1. / _td.to_f32_px(); - let interpolated = fd_matrix.interpolate(&td_matrix, progress).unwrap(); - result.push(TransformOperation::Matrix(interpolated)); + let sum = fd_matrix.add_weighted(&td_matrix, self_portion, other_portion) + .unwrap(); + result.push(TransformOperation::Matrix(sum)); } _ => { // This should be unreachable due to the can_interpolate_list() call. @@ -1685,37 +1717,37 @@ pub struct MatrixDecomposed2D { } impl Animatable for InnerMatrix2D { - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { Ok(InnerMatrix2D { - m11: try!(self.m11.interpolate(&other.m11, progress)), - m12: try!(self.m12.interpolate(&other.m12, progress)), - m21: try!(self.m21.interpolate(&other.m21, progress)), - m22: try!(self.m22.interpolate(&other.m22, progress)), + m11: try!(self.m11.add_weighted(&other.m11, self_portion, other_portion)), + m12: try!(self.m12.add_weighted(&other.m12, self_portion, other_portion)), + m21: try!(self.m21.add_weighted(&other.m21, self_portion, other_portion)), + m22: try!(self.m22.add_weighted(&other.m22, self_portion, other_portion)), }) } } impl Animatable for Translate2D { - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { Ok(Translate2D( - try!(self.0.interpolate(&other.0, progress)), - try!(self.1.interpolate(&other.1, progress)) + try!(self.0.add_weighted(&other.0, self_portion, other_portion)), + try!(self.1.add_weighted(&other.1, self_portion, other_portion)) )) } } impl Animatable for Scale2D { - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { Ok(Scale2D( - try!(self.0.interpolate(&other.0, progress)), - try!(self.1.interpolate(&other.1, progress)) + try!(self.0.add_weighted(&other.0, self_portion, other_portion)), + try!(self.1.add_weighted(&other.1, self_portion, other_portion)) )) } } impl Animatable for MatrixDecomposed2D { /// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-2d-matrix-values - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { // If x-axis of one is flipped, and y-axis of the other, // convert to an unflipped rotation. let mut scale = self.scale; @@ -1745,10 +1777,11 @@ impl Animatable for MatrixDecomposed2D { } // Interpolate all values. - let translate = try!(self.translate.interpolate(&other.translate, progress)); - let scale = try!(scale.interpolate(&other.scale, progress)); - let angle = try!(angle.interpolate(&other_angle, progress)); - let matrix = try!(self.matrix.interpolate(&other.matrix, progress)); + let translate = try!(self.translate.add_weighted(&other.translate, + self_portion, other_portion)); + let scale = try!(scale.add_weighted(&other.scale, self_portion, other_portion)); + let angle = try!(angle.add_weighted(&other_angle, self_portion, other_portion)); + let matrix = try!(self.matrix.add_weighted(&other.matrix, self_portion, other_portion)); Ok(MatrixDecomposed2D { translate: translate, @@ -1760,25 +1793,26 @@ impl Animatable for MatrixDecomposed2D { } impl Animatable for ComputedMatrix { - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { if self.is_3d() || other.is_3d() { let decomposed_from = decompose_3d_matrix(*self); let decomposed_to = decompose_3d_matrix(*other); match (decomposed_from, decomposed_to) { (Ok(from), Ok(to)) => { - let interpolated = try!(from.interpolate(&to, progress)); - Ok(ComputedMatrix::from(interpolated)) + let sum = try!(from.add_weighted(&to, self_portion, other_portion)); + Ok(ComputedMatrix::from(sum)) }, _ => { - let interpolated = if progress < 0.5 {*self} else {*other}; - Ok(interpolated) + let result = if self_portion > other_portion {*self} else {*other}; + Ok(result) } } } else { let decomposed_from = MatrixDecomposed2D::from(*self); let decomposed_to = MatrixDecomposed2D::from(*other); - let interpolated = try!(decomposed_from.interpolate(&decomposed_to, progress)); - Ok(ComputedMatrix::from(interpolated)) + let sum = try!(decomposed_from.add_weighted(&decomposed_to, + self_portion, other_portion)); + Ok(ComputedMatrix::from(sum)) } } } @@ -2094,58 +2128,64 @@ fn cross(row1: [f32; 3], row2: [f32; 3]) -> [f32; 3] { } impl Animatable for Translate3D { - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { Ok(Translate3D( - try!(self.0.interpolate(&other.0, progress)), - try!(self.1.interpolate(&other.1, progress)), - try!(self.2.interpolate(&other.2, progress)) + try!(self.0.add_weighted(&other.0, self_portion, other_portion)), + try!(self.1.add_weighted(&other.1, self_portion, other_portion)), + try!(self.2.add_weighted(&other.2, self_portion, other_portion)) )) } } impl Animatable for Scale3D { - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { Ok(Scale3D( - try!(self.0.interpolate(&other.0, progress)), - try!(self.1.interpolate(&other.1, progress)), - try!(self.2.interpolate(&other.2, progress)) + try!(self.0.add_weighted(&other.0, self_portion, other_portion)), + try!(self.1.add_weighted(&other.1, self_portion, other_portion)), + try!(self.2.add_weighted(&other.2, self_portion, other_portion)) )) } } impl Animatable for Skew { - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { Ok(Skew( - try!(self.0.interpolate(&other.0, progress)), - try!(self.1.interpolate(&other.1, progress)), - try!(self.2.interpolate(&other.2, progress)) + try!(self.0.add_weighted(&other.0, self_portion, other_portion)), + try!(self.1.add_weighted(&other.1, self_portion, other_portion)), + try!(self.2.add_weighted(&other.2, self_portion, other_portion)) )) } } impl Animatable for Perspective { - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { Ok(Perspective( - try!(self.0.interpolate(&other.0, progress)), - try!(self.1.interpolate(&other.1, progress)), - try!(self.2.interpolate(&other.2, progress)), - try!(self.3.interpolate(&other.3, progress)) + try!(self.0.add_weighted(&other.0, self_portion, other_portion)), + try!(self.1.add_weighted(&other.1, self_portion, other_portion)), + try!(self.2.add_weighted(&other.2, self_portion, other_portion)), + try!(self.3.add_weighted(&other.3, self_portion, other_portion)) )) } } impl Animatable for MatrixDecomposed3D { /// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-3d-matrix-values - fn interpolate(&self, other: &Self, progress: f64) -> Result { - let mut interpolated = *self; + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result { + assert!(self_portion + other_portion == 1.0f64, + "add_weighted should only be used for interpolating transforms"); - // Interpolate translate, scale, skew and perspective components. - interpolated.translate = try!(self.translate.interpolate(&other.translate, progress)); - interpolated.scale = try!(self.scale.interpolate(&other.scale, progress)); - interpolated.skew = try!(self.skew.interpolate(&other.skew, progress)); - interpolated.perspective = try!(self.perspective.interpolate(&other.perspective, progress)); + let mut sum = *self; - // Interpolate quaternions using spherical linear interpolation (Slerp). + // Add translate, scale, skew and perspective components. + sum.translate = try!(self.translate.add_weighted(&other.translate, + self_portion, other_portion)); + sum.scale = try!(self.scale.add_weighted(&other.scale, self_portion, other_portion)); + sum.skew = try!(self.skew.add_weighted(&other.skew, self_portion, other_portion)); + sum.perspective = try!(self.perspective.add_weighted(&other.perspective, + self_portion, other_portion)); + + // Add quaternions using spherical linear interpolation (Slerp). let mut product = self.quaternion.0 * other.quaternion.0 + self.quaternion.1 * other.quaternion.1 + self.quaternion.2 * other.quaternion.2 + @@ -2156,21 +2196,21 @@ impl Animatable for MatrixDecomposed3D { product = product.max(-1.0); if product == 1.0 { - return Ok(interpolated); + return Ok(sum); } let theta = product.acos(); - let w = (progress as f32 * theta).sin() * 1.0 / (1.0 - product * product).sqrt(); + let w = (other_portion as f32 * theta).sin() * 1.0 / (1.0 - product * product).sqrt(); let mut a = *self; let mut b = *other; % for i in range(4): - a.quaternion.${i} *= (progress as f32 * theta).cos() - product * w; + a.quaternion.${i} *= (other_portion as f32 * theta).cos() - product * w; b.quaternion.${i} *= w; - interpolated.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i}; + sum.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i}; % endfor - Ok(interpolated) + Ok(sum) } } @@ -2374,22 +2414,22 @@ impl ComputedMatrix { /// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms impl Animatable for TransformList { #[inline] - fn interpolate(&self, other: &TransformList, progress: f64) -> Result { + fn add_weighted(&self, other: &TransformList, self_portion: f64, other_portion: f64) -> Result { // http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms let result = match (&self.0, &other.0) { (&Some(ref from_list), &Some(ref to_list)) => { // Two lists of transforms - interpolate_transform_list(from_list, &to_list, progress) + add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion) } (&Some(ref from_list), &None) => { // http://dev.w3.org/csswg/css-transforms/#none-transform-animation let to_list = build_identity_transform_list(from_list); - interpolate_transform_list(from_list, &to_list, progress) + add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion) } (&None, &Some(ref to_list)) => { // http://dev.w3.org/csswg/css-transforms/#none-transform-animation let from_list = build_identity_transform_list(to_list); - interpolate_transform_list(&from_list, to_list, progress) + add_weighted_transform_lists(&from_list, to_list, self_portion, other_portion) } _ => { // http://dev.w3.org/csswg/css-transforms/#none-none-animation @@ -2405,17 +2445,17 @@ impl Animatable for Either where T: Animatable + Copy, U: Animatable + Copy, { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { match (*self, *other) { (Either::First(ref this), Either::First(ref other)) => { - this.interpolate(&other, progress).map(Either::First) + this.add_weighted(&other, self_portion, other_portion).map(Either::First) }, (Either::Second(ref this), Either::Second(ref other)) => { - this.interpolate(&other, progress).map(Either::Second) + this.add_weighted(&other, self_portion, other_portion).map(Either::Second) }, _ => { - let interpolated = if progress < 0.5 { *self } else { *other }; - Ok(interpolated) + let result = if self_portion > other_portion {*self} else {*other}; + Ok(result) } } } @@ -2497,21 +2537,26 @@ impl IntermediateRGBA { /// Unlike Animatable for RGBA we don't clamp any component values. impl Animatable for IntermediateRGBA { #[inline] - fn interpolate(&self, other: &IntermediateRGBA, progress: f64) -> Result { - let alpha = try!(self.alpha.interpolate(&other.alpha, progress)); - if alpha == 0. { + fn add_weighted(&self, other: &IntermediateRGBA, self_portion: f64, other_portion: f64) + -> Result { + let mut alpha = try!(self.alpha.add_weighted(&other.alpha, self_portion, other_portion)); + if alpha <= 0. { // Ideally we should return color value that only alpha component is // 0, but this is what current gecko does. Ok(IntermediateRGBA::transparent()) } else { + alpha = alpha.min(1.); let red = try!((self.red * self.alpha) - .interpolate(&(other.red * other.alpha), progress)) + .add_weighted(&(other.red * other.alpha), + self_portion, other_portion)) * 1. / alpha; let green = try!((self.green * self.alpha) - .interpolate(&(other.green * other.alpha), progress)) + .add_weighted(&(other.green * other.alpha), + self_portion, other_portion)) * 1. / alpha; let blue = try!((self.blue * self.alpha) - .interpolate(&(other.blue * other.alpha), progress)) + .add_weighted(&(other.blue * other.alpha), + self_portion, other_portion)) * 1. / alpha; Ok(IntermediateRGBA::new(red, green, blue, alpha)) } @@ -2588,10 +2633,12 @@ pub enum IntermediateColor { impl Animatable for IntermediateColor { #[inline] - fn interpolate(&self, other: &Self, progress: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result { match (*self, *other) { - (IntermediateColor::IntermediateRGBA(ref this), IntermediateColor::IntermediateRGBA(ref other)) => { - this.interpolate(other, progress).map(IntermediateColor::IntermediateRGBA) + (IntermediateColor::IntermediateRGBA(ref this), + IntermediateColor::IntermediateRGBA(ref other)) => { + this.add_weighted(other, self_portion, other_portion) + .map(IntermediateColor::IntermediateRGBA) } // FIXME: Bug 1345709: Implement currentColor animations. _ => Err(()), diff --git a/components/style/properties/longhand/background.mako.rs b/components/style/properties/longhand/background.mako.rs index b0c998d59fd..3b50cbae01a 100644 --- a/components/style/properties/longhand/background.mako.rs +++ b/components/style/properties/longhand/background.mako.rs @@ -190,13 +190,16 @@ ${helpers.single_keyword("background-origin", impl RepeatableListAnimatable for T {} impl Animatable for T { - fn interpolate(&self, other: &Self, time: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result { use properties::longhands::background_size::single_value::computed_value::ExplicitSize; match (self, other) { (&T::Explicit(ref me), &T::Explicit(ref other)) => { Ok(T::Explicit(ExplicitSize { - width: try!(me.width.interpolate(&other.width, time)), - height: try!(me.height.interpolate(&other.height, time)), + width: try!(me.width.add_weighted(&other.width, + self_portion, other_portion)), + height: try!(me.height.add_weighted(&other.height, + self_portion, other_portion)), })) } _ => Err(()), diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index ac0ac739a5e..eeb2100b11e 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -2198,11 +2198,14 @@ ${helpers.single_keyword("transform-style", impl Animatable for T { #[inline] - fn interpolate(&self, other: &Self, time: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result { Ok(T { - horizontal: try!(self.horizontal.interpolate(&other.horizontal, time)), - vertical: try!(self.vertical.interpolate(&other.vertical, time)), - depth: try!(self.depth.interpolate(&other.depth, time)), + horizontal: try!(self.horizontal.add_weighted(&other.horizontal, + self_portion, other_portion)), + vertical: try!(self.vertical.add_weighted(&other.vertical, + self_portion, other_portion)), + depth: try!(self.depth.add_weighted(&other.depth, self_portion, other_portion)), }) } diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 2b5c89b958f..44e49ac1fb7 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -1073,10 +1073,12 @@ ${helpers.single_keyword_system("font-variant-caps", } impl Animatable for T { - fn interpolate(&self, other: &Self, time: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result { match (*self, *other) { (T::Number(ref number), T::Number(ref other)) => - Ok(T::Number(try!(number.interpolate(other, time)))), + Ok(T::Number(try!(number.add_weighted(other, + self_portion, other_portion)))), _ => Err(()), } } diff --git a/components/style/properties/longhand/inherited_table.mako.rs b/components/style/properties/longhand/inherited_table.mako.rs index aba56b125a2..b6c1ad3d604 100644 --- a/components/style/properties/longhand/inherited_table.mako.rs +++ b/components/style/properties/longhand/inherited_table.mako.rs @@ -42,10 +42,13 @@ ${helpers.single_keyword("caption-side", "top bottom", /// https://drafts.csswg.org/css-transitions/#animtype-simple-list impl Animatable for T { #[inline] - fn interpolate(&self, other: &Self, time: f64) -> Result { + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) + -> Result { Ok(T { - horizontal: try!(self.horizontal.interpolate(&other.horizontal, time)), - vertical: try!(self.vertical.interpolate(&other.vertical, time)), + horizontal: try!(self.horizontal.add_weighted(&other.horizontal, + self_portion, other_portion)), + vertical: try!(self.vertical.add_weighted(&other.vertical, + self_portion, other_portion)), }) } diff --git a/components/style/values/mod.rs b/components/style/values/mod.rs index b21ab50e93b..4ce3d1f8dc1 100644 --- a/components/style/values/mod.rs +++ b/components/style/values/mod.rs @@ -129,7 +129,8 @@ macro_rules! define_keyword_type { impl Animatable for $name { #[inline] - fn interpolate(&self, _other: &Self, _progress: f64) -> Result { + fn add_weighted(&self, _other: &Self, _self_progress: f64, _other_progress: f64) + -> Result { Ok($name) } }