Bug 1376495 - Part 2: Implement Animatable for ShapeSource. draft
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Thu, 10 Aug 2017 17:15:36 +0900
changeset 643918 657dda8ff2c674e1266c5c0caeafd05346b3fb9d
parent 643913 43b84f59c01484fc3766473fde0212edde24a7b9
child 643919 3c037c948d160f22572fbb11f3ee4faf609b0fec
push id73256
push userbmo:dakatsuka@mozilla.com
push dateThu, 10 Aug 2017 08:16:22 +0000
bugs1376495
milestone57.0a1
Bug 1376495 - Part 2: Implement Animatable for ShapeSource. We need to implement Animatable for clip-path and shape-outside. In this patch, make following things animatable. * ShapeSource * BasicShape * ShapeRadius * InsetRect * Circle * Ellipse * Polygon * Rect * BorderRadius MozReview-Commit-ID: DhJe9KCOEEI
servo/components/style/properties/helpers/animated_properties.mako.rs
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -42,22 +42,28 @@ use values::animated::effects::BoxShadow
 use values::animated::effects::Filter as AnimatedFilter;
 use values::animated::effects::FilterList as AnimatedFilterList;
 use values::animated::effects::TextShadowList as AnimatedTextShadowList;
 use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
 use values::computed::{BorderCornerRadius, ClipRect};
 use values::computed::{CalcLengthOrPercentage, Color, Context, ComputedValueAsSpecified};
 use values::computed::{LengthOrPercentage, MaxLength, MozLength, Percentage, ToComputedValue};
 use values::computed::{NonNegativeAu, NonNegativeNumber, PositiveIntegerOrAuto};
+use values::computed::basic_shape::{BasicShape, Circle, Ellipse, InsetRect, ShapeRadius};
+use values::computed::border::BorderRadius;
 use values::computed::length::{NonNegativeLengthOrAuto, NonNegativeLengthOrNormal};
 use values::computed::length::NonNegativeLengthOrPercentage;
 use values::generics::{GreaterThanOrEqualToOne, NonNegative};
+use values::generics::basic_shape::{Polygon, ShapeSource};
+use values::generics::basic_shape::BasicShape as GenericBasicShape;
+use values::generics::basic_shape::ShapeRadius as GenericShapeRadius;
 use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
 use values::generics::effects::Filter;
 use values::generics::position as generic_position;
+use values::generics::rect::Rect;
 use values::generics::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind, SVGStrokeDashArray};
 
 /// 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<Self, ()>;
@@ -3324,16 +3330,264 @@ impl Animatable for AnimatedFilterList {
                     compute_filter_square_distance(&none, &list)?
                 },
             };
         }
         Ok(square_distance)
     }
 }
 
+impl<RefBox: PartialEq> ShapeSource<BasicShape, RefBox> {
+    fn get_animatable_values<'a>(source1: &'a Self, source2: &'a Self)
+                                 -> Result<(&'a BasicShape,
+                                            &'a BasicShape, &'a Option<RefBox>), ()> {
+        if let (&ShapeSource::Shape(ref shape1, ref refbox1),
+                &ShapeSource::Shape(ref shape2, ref refbox2)) = (source1, source2) {
+            if refbox1 == refbox2 {
+                return Ok((shape1, shape2, refbox1));
+            };
+        };
+        Err(())
+    }
+}
+
+impl<RefBox: PartialEq + Clone> Animatable for ShapeSource<BasicShape, RefBox> {
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        if let Ok((self_shape, other_shape, refbox)) =
+            Self::get_animatable_values(self, other) {
+            if let Ok(shape) = self_shape.add_weighted(other_shape, self_portion, other_portion) {
+                return Ok(ShapeSource::Shape(shape, refbox.clone()));
+            };
+        };
+        Err(())
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        if let Ok((self_shape, other_shape, _)) =
+            Self::get_animatable_values(self, other) {
+            return self_shape.compute_distance(other_shape);
+        };
+        Err(())
+    }
+}
+
+impl Animatable for BasicShape  {
+    <% basic_shapes = ["Inset", "Circle", "Ellipse", "Polygon"] %>
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        match (self, other) {
+            % for basic_shape in basic_shapes:
+            (&GenericBasicShape::${basic_shape}(ref self_shape),
+             &GenericBasicShape::${basic_shape}(ref other_shape)) => {
+                if let Ok(shape) =
+                    self_shape.add_weighted(&other_shape, self_portion, other_portion) {
+                    Ok(GenericBasicShape::${basic_shape}(shape))
+                } else {
+                    Err(())
+                }
+            },
+            % endfor
+            _ => Err(())
+        }
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        match (self, other) {
+            % for basic_shape in basic_shapes:
+            (&GenericBasicShape::${basic_shape}(ref self_shape),
+             &GenericBasicShape::${basic_shape}(ref other_shape)) => {
+                self_shape.compute_distance(&other_shape)
+            },
+            % endfor
+            _ => Err(())
+        }
+    }
+}
+
+impl Animatable for ShapeRadius {
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        match (self, other) {
+            (&GenericShapeRadius::Length(self_length), &GenericShapeRadius::Length(other_length)) => {
+                let value =
+                    self_length.add_weighted(&other_length, self_portion, other_portion).unwrap();
+                Ok(GenericShapeRadius::Length(value))
+            },
+            _ => Err(())
+        }
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        match (self, other) {
+            (&GenericShapeRadius::Length(self_length), &GenericShapeRadius::Length(other_length)) =>
+                self_length.compute_distance(&other_length),
+            _ => Err(())
+        }
+    }
+}
+
+impl Animatable for InsetRect {
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        let round = match (self.round, other.round) {
+            (Some(self_round), Some(other_round)) => {
+                if let Ok(round) =
+                    self_round.add_weighted(&other_round, self_portion, other_portion) {
+                    Some(round)
+                } else {
+                    return Err(());
+                }
+            },
+            (None, None) => None,
+            _ => {
+                return Err(());
+            }
+        };
+        let rect = self.rect.add_weighted(&other.rect, self_portion, other_portion).unwrap();
+        Ok(InsetRect{rect, round})
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        let mut distance = self.rect.compute_distance(&other.rect).unwrap();
+        if let (Some(self_round), Some(other_round)) = (self.round, other.round) {
+            distance += self_round.compute_distance(&other_round).unwrap();
+        }
+        Ok(distance)
+    }
+}
+
+impl Animatable for Circle {
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        if let Ok(radius) =
+            self.radius.add_weighted(&other.radius, self_portion, other_portion) {
+            let position =
+                self.position.add_weighted(&other.position, self_portion, other_portion).unwrap();
+            Ok(Circle{position, radius})
+        } else {
+            Err(())
+        }
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        self.radius.compute_distance(&other.radius)
+    }
+}
+
+impl Animatable for Ellipse {
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        if let (Ok(semiaxis_x), Ok(semiaxis_y)) =
+                  (self.semiaxis_x.add_weighted(&other.semiaxis_x, self_portion, other_portion),
+                   self.semiaxis_y.add_weighted(&other.semiaxis_y, self_portion, other_portion)) {
+            let position =
+                self.position.add_weighted(&other.position, self_portion, other_portion).unwrap();
+            Ok(Ellipse{position, semiaxis_x, semiaxis_y})
+        } else {
+            Err(())
+        }
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        if let (Ok(semiaxis_x), Ok(semiaxis_y)) =
+                  (self.semiaxis_x.compute_distance(&other.semiaxis_x),
+                   self.semiaxis_y.compute_distance(&other.semiaxis_y)) {
+            Ok(semiaxis_x + semiaxis_y)
+        } else {
+            Err(())
+        }
+
+    }
+}
+
+impl Animatable for Polygon<LengthOrPercentage> {
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        if self.fill == other.fill && self.coordinates.len() == other.coordinates.len() {
+            let fill = self.fill;
+            let coordinates: Vec<(LengthOrPercentage, LengthOrPercentage)> =
+                    self.coordinates.iter().zip(other.coordinates.iter())
+                        .map(|(&(self_x, self_y), &(other_x, other_y))| {
+                    let x = self_x.add_weighted(&other_x, self_portion, other_portion).unwrap();
+                    let y = self_y.add_weighted(&other_y, self_portion, other_portion).unwrap();
+                    (x, y)
+                }).collect();
+            Ok(Polygon{fill, coordinates})
+        } else {
+            Err(())
+        }
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        if self.fill == other.fill && self.coordinates.len() == other.coordinates.len() {
+            let mut distance = 0_f64;
+            for (&(self_x, self_y), &(other_x, other_y)) in
+                self.coordinates.iter().zip(other.coordinates.iter()) {
+                distance += self_x.compute_distance(&other_x).unwrap();
+                distance += self_y.compute_distance(&other_y).unwrap();
+            }
+            Ok(distance)
+        } else {
+            Err(())
+        }
+    }
+}
+
+impl Animatable for BorderRadius {
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        let top_left =
+            self.top_left.add_weighted(&other.top_left, self_portion, other_portion).unwrap();
+        let top_right =
+            self.top_right.add_weighted(&other.top_right, self_portion, other_portion).unwrap();
+        let bottom_right =
+            self.bottom_right.add_weighted(&other.bottom_right, self_portion, other_portion).unwrap();
+        let bottom_left =
+            self.bottom_left.add_weighted(&other.bottom_left, self_portion, other_portion).unwrap();
+        Ok(BorderRadius{top_left, top_right, bottom_right, bottom_left})
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        let mut distance = self.top_left.compute_distance(&other.top_left).unwrap();
+        distance += self.top_right.compute_distance(&other.top_right).unwrap();
+        distance += self.bottom_right.compute_distance(&other.bottom_right).unwrap();
+        distance += self.bottom_left.compute_distance(&other.bottom_left).unwrap();
+        Ok(distance)
+    }
+}
+
+impl Animatable for Rect<LengthOrPercentage> {
+    #[inline]
+    fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
+        let x = self.0.add_weighted(&other.0, self_portion, other_portion).unwrap();
+        let y = self.1.add_weighted(&other.1, self_portion, other_portion).unwrap();
+        let w = self.2.add_weighted(&other.2, self_portion, other_portion).unwrap();
+        let h = self.3.add_weighted(&other.3, self_portion, other_portion).unwrap();
+        Ok(Rect(x, y, w, h))
+    }
+
+    #[inline]
+    fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
+        let mut distance = self.0.compute_distance(&other.0).unwrap();
+        distance += self.1.compute_distance(&other.1).unwrap();
+        distance += self.2.compute_distance(&other.2).unwrap();
+        distance += self.3.compute_distance(&other.3).unwrap();
+        Ok(distance)
+    }
+}
+
 /// A comparator to sort PropertyIds such that longhands are sorted before shorthands,
 /// shorthands with fewer components are sorted before shorthands with more components,
 /// and otherwise shorthands are sorted by IDL name as defined by [Web Animations][property-order].
 ///
 /// Using this allows us to prioritize values specified by longhands (or smaller
 /// shorthand subsets) when longhands and shorthands are both specified on the one keyframe.
 ///
 /// Example orderings that result from this: