Bug 1390039 - Part 2: Use DirectionVector as an alias of euclid::Vector3D<f32>. draft
authorBoris Chiou <boris.chiou@gmail.com>
Thu, 24 Aug 2017 13:41:09 +0800
changeset 651943 e7ed1536ba167a6d9038b4e154f1bc3565b81f0a
parent 651942 bed53edd13fb1a75554f605a1b499593b1aface2
child 651944 497d3a8d41d8bb512613a2325eba971597efd703
push id75885
push userbmo:boris.chiou@gmail.com
push dateThu, 24 Aug 2017 07:55:20 +0000
bugs1390039
milestone57.0a1
Bug 1390039 - Part 2: Use DirectionVector as an alias of euclid::Vector3D<f32>. Therefore, we can reuse the methods of Vector3D, instead of implementing similar ones. MozReview-Commit-ID: BNqjiPyhMxB
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/values/computed/transform.rs
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -1136,17 +1136,17 @@ impl ToAnimatedZero for TransformOperati
                     ty.to_animated_zero()?,
                     tz.to_animated_zero()?,
                 ))
             },
             TransformOperation::Scale(..) => {
                 Ok(TransformOperation::Scale(1.0, 1.0, 1.0))
             },
             TransformOperation::Rotate(x, y, z, a) => {
-                let (x, y, z, _) = DirectionVector::get_normalized_vector_and_angle(x, y, z, a);
+                let (x, y, z, _) = TransformList::get_normalized_vector_and_angle(x, y, z, a);
                 Ok(TransformOperation::Rotate(x, y, z, Angle::zero()))
             },
             TransformOperation::Perspective(..) |
             TransformOperation::AccumulateMatrix { .. } |
             TransformOperation::InterpolateMatrix { .. } => {
                 // Perspective: We convert a perspective function into an equivalent
                 //     ComputedMatrix, and then decompose/interpolate/recompose these matrices.
                 // AccumulateMatrix/InterpolateMatrix: We do interpolation on
@@ -1214,19 +1214,19 @@ impl Animate for TransformOperation {
                     animate_multiplicative_factor(*fz, *tz, procedure)?,
                 ))
             },
             (
                 &TransformOperation::Rotate(fx, fy, fz, fa),
                 &TransformOperation::Rotate(tx, ty, tz, ta),
             ) => {
                 let (fx, fy, fz, fa) =
-                    DirectionVector::get_normalized_vector_and_angle(fx, fy, fz, fa);
+                    TransformList::get_normalized_vector_and_angle(fx, fy, fz, fa);
                 let (tx, ty, tz, ta) =
-                    DirectionVector::get_normalized_vector_and_angle(tx, ty, tz, ta);
+                    TransformList::get_normalized_vector_and_angle(tx, ty, tz, ta);
                 if (fx, fy, fz) == (tx, ty, tz) {
                     let ia = fa.animate(&ta, procedure)?;
                     Ok(TransformOperation::Rotate(fx, fy, fz, ia))
                 } else {
                     let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
                     let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
                     Ok(TransformOperation::Matrix(
                         matrix_f.animate(&matrix_t, procedure)?,
@@ -1633,30 +1633,30 @@ pub struct MatrixDecomposed3D {
     /// The quaternion used to represent the rotation.
     pub quaternion: Quaternion,
 }
 
 impl Quaternion {
     /// Return a quaternion from a unit direction vector and angle (unit: radian).
     #[inline]
     fn from_direction_and_angle(vector: &DirectionVector, angle: f64) -> Self {
-        debug_assert!((vector.length() - 1.).abs() < 0.0001f64,
-                       "Only accept an unit direction vector to create a quaternion");
+        debug_assert!((vector.length() - 1.).abs() < 0.0001,
+                      "Only accept an unit direction vector to create a quaternion");
         // Reference:
         // https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
         //
         // if the direction axis is (x, y, z) = xi + yj + zk,
         // and the angle is |theta|, this formula can be done using
         // an extension of Euler's formula:
         //   q = cos(theta/2) + (xi + yj + zk)(sin(theta/2))
         //     = cos(theta/2) +
         //       x*sin(theta/2)i + y*sin(theta/2)j + z*sin(theta/2)k
-        Quaternion(vector.0.x * (angle / 2.).sin(),
-                   vector.0.y * (angle / 2.).sin(),
-                   vector.0.z * (angle / 2.).sin(),
+        Quaternion(vector.x as f64 * (angle / 2.).sin(),
+                   vector.y as f64 * (angle / 2.).sin(),
+                   vector.z as f64 * (angle / 2.).sin(),
                    (angle / 2.).cos())
     }
 
     /// Calculate the dot product.
     #[inline]
     fn dot(&self, other: &Self) -> f64 {
         self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3
     }
@@ -2333,19 +2333,19 @@ impl ComputeSquaredDistance for Transfor
                     fz.compute_squared_distance(&tz)?,
                 )
             },
             (
                 &TransformOperation::Rotate(fx, fy, fz, fa),
                 &TransformOperation::Rotate(tx, ty, tz, ta),
             ) => {
                 let (fx, fy, fz, angle1) =
-                    DirectionVector::get_normalized_vector_and_angle(fx, fy, fz, fa);
+                    TransformList::get_normalized_vector_and_angle(fx, fy, fz, fa);
                 let (tx, ty, tz, angle2) =
-                    DirectionVector::get_normalized_vector_and_angle(tx, ty, tz, ta);
+                    TransformList::get_normalized_vector_and_angle(tx, ty, tz, ta);
                 if (fx, fy, fz) == (tx, ty, tz) {
                     angle1.compute_squared_distance(&angle2)
                 } else {
                     let v1 = DirectionVector::new(fx, fy, fz);
                     let v2 = DirectionVector::new(tx, ty, tz);
                     let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64());
                     let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64());
                     q1.compute_squared_distance(&q2)
--- a/servo/components/style/values/computed/transform.rs
+++ b/servo/components/style/values/computed/transform.rs
@@ -1,89 +1,46 @@
 /* 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/. */
 
 //! Computed types for CSS values that are related to transformations.
 
 use app_units::Au;
-use euclid::{Point3D, Radians, Rect, Transform3D};
+use euclid::{Radians, Rect, Transform3D, Vector3D};
 use properties::longhands::transform::computed_value::{ComputedOperation, ComputedMatrix};
 use properties::longhands::transform::computed_value::T as TransformList;
 use std::f32;
 use super::CSSFloat;
 use values::computed::{Angle, Length, LengthOrPercentage, Number, Percentage};
 use values::generics::transform::TimingFunction as GenericTimingFunction;
 use values::generics::transform::TransformOrigin as GenericTransformOrigin;
 
 
 /// The computed value of a CSS `<transform-origin>`
 pub type TransformOrigin = GenericTransformOrigin<LengthOrPercentage, LengthOrPercentage, Length>;
 
 /// A computed timing function.
 pub type TimingFunction = GenericTimingFunction<u32, Number>;
 
+/// A vector to represent the direction vector (rotate axis) for Rotate3D.
+pub type DirectionVector = Vector3D<CSSFloat>;
+
 impl TransformOrigin {
     /// Returns the initial computed value for `transform-origin`.
     #[inline]
     pub fn initial_value() -> Self {
         Self::new(
             LengthOrPercentage::Percentage(Percentage(0.5)),
             LengthOrPercentage::Percentage(Percentage(0.5)),
             Length::from_px(0),
         )
     }
 }
 
-/// A wrapper of Point3D to represent the direction vector (rotate axis) for Rotate3D.
-#[derive(Clone, Copy, Debug, PartialEq)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-pub struct DirectionVector(pub Point3D<f64>);
-
-impl DirectionVector {
-    /// Create a DirectionVector.
-    #[inline]
-    pub fn new(x: f32, y: f32, z: f32) -> Self {
-        DirectionVector(Point3D::new(x as f64, y as f64, z as f64))
-    }
-
-    /// Return the normalized direction vector.
-    #[inline]
-    pub fn normalize(&mut self) -> bool {
-        let len = self.length();
-        if len > 0. {
-            self.0.x = self.0.x / len;
-            self.0.y = self.0.y / len;
-            self.0.z = self.0.z / len;
-            true
-        } else {
-            false
-        }
-    }
-
-    /// Get the length of this vector.
-    #[inline]
-    pub fn length(&self) -> f64 {
-        self.0.to_array().iter().fold(0f64, |sum, v| sum + v * v).sqrt()
-    }
-
-    /// Return the normalized direction vector and its angle.
-    // A direction vector that cannot be normalized, such as [0,0,0], will cause the
-    // rotation to not be applied. i.e. Use an identity matrix or rotate3d(0, 0, 1, 0).
-    pub fn get_normalized_vector_and_angle(x: f32, y: f32, z: f32, angle: Angle)
-                                           -> (f32, f32, f32, Angle) {
-        let mut vector = DirectionVector::new(x, y, z);
-        if vector.normalize() {
-            (vector.0.x as f32, vector.0.y as f32, vector.0.z as f32, angle)
-        } else {
-            (0., 0., 1., Angle::zero())
-        }
-    }
-}
-
 impl From<ComputedMatrix> for Transform3D<CSSFloat> {
     #[inline]
     fn from(m: ComputedMatrix) -> Self {
         Transform3D::row_major(
             m.m11, m.m12, m.m13, m.m14,
             m.m21, m.m22, m.m23, m.m24,
             m.m31, m.m32, m.m33, m.m34,
             m.m41, m.m42, m.m43, m.m44)
@@ -118,22 +75,19 @@ impl TransformList {
                 LengthOrPercentage::Percentage(_) => 0.,
                 LengthOrPercentage::Calc(calc) => calc.length().to_f32_px(),
             }
         };
 
         for operation in list {
             let matrix = match *operation {
                 ComputedOperation::Rotate(ax, ay, az, theta) => {
-                    // https://www.w3.org/TR/css-transforms-1/#funcdef-rotate3d
-                    // A direction vector that cannot be normalized, such as [0, 0, 0], will cause
-                    // the rotation to not be applied, so we use identity matrix in this case.
                     let theta = Angle::from_radians(2.0f32 * f32::consts::PI - theta.radians());
                     let (ax, ay, az, theta) =
-                        DirectionVector::get_normalized_vector_and_angle(ax, ay, az, theta);
+                        Self::get_normalized_vector_and_angle(ax, ay, az, theta);
                     Transform3D::create_rotation(ax, ay, az, Radians::new(theta.radians()))
                 }
                 ComputedOperation::Perspective(d) => {
                     let d = d.to_f32_px();
                     if d > 0. {
                         Transform3D::create_perspective(d)
                     } else {
                         Transform3D::identity()
@@ -180,9 +134,26 @@ impl TransformList {
                 }
             };
 
             transform = transform.pre_mul(&matrix);
         }
 
         Some(transform)
     }
+
+    /// Return the normalized direction vector and its angle for Rotate3D.
+    pub fn get_normalized_vector_and_angle(x: f32, y: f32, z: f32, angle: Angle)
+                                           -> (f32, f32, f32, Angle) {
+        use euclid::approxeq::ApproxEq;
+        use euclid::num::Zero;
+        let vector = DirectionVector::new(x, y, z);
+        if vector.square_length().approx_eq(&f32::zero()) {
+            // https://www.w3.org/TR/css-transforms-1/#funcdef-rotate3d
+            // A direction vector that cannot be normalized, such as [0, 0, 0], will cause the
+            // rotation to not be applied, so we use identity matrix (i.e. rotate3d(0, 0, 1, 0)).
+            (0., 0., 1., Angle::zero())
+        } else {
+            let vector = vector.normalize();
+            (vector.x, vector.y, vector.z, angle)
+        }
+    }
 }