[Impeller] Move SeparatedVector2 to impeller/geometry. (#53264)

Follow-up for https://github.com/flutter/engine/pull/53210.

Also rename from `Ray` to `SeparatedVector2`, which is a more accurate name for what it is.
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index d3d4103..24bd093 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -42151,6 +42151,8 @@
 ORIGIN: ../../../flutter/impeller/geometry/rect.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/impeller/geometry/saturated_math.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/impeller/geometry/scalar.h + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/impeller/geometry/separated_vector.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/impeller/geometry/separated_vector.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/impeller/geometry/shear.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/impeller/geometry/shear.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/impeller/geometry/sigma.cc + ../../../flutter/LICENSE
@@ -45017,6 +45019,8 @@
 FILE: ../../../flutter/impeller/geometry/rect.h
 FILE: ../../../flutter/impeller/geometry/saturated_math.h
 FILE: ../../../flutter/impeller/geometry/scalar.h
+FILE: ../../../flutter/impeller/geometry/separated_vector.cc
+FILE: ../../../flutter/impeller/geometry/separated_vector.h
 FILE: ../../../flutter/impeller/geometry/shear.cc
 FILE: ../../../flutter/impeller/geometry/shear.h
 FILE: ../../../flutter/impeller/geometry/sigma.cc
diff --git a/impeller/entity/geometry/stroke_path_geometry.cc b/impeller/entity/geometry/stroke_path_geometry.cc
index c4220a2..5956adf 100644
--- a/impeller/entity/geometry/stroke_path_geometry.cc
+++ b/impeller/entity/geometry/stroke_path_geometry.cc
@@ -10,6 +10,7 @@
 #include "impeller/geometry/constants.h"
 #include "impeller/geometry/path_builder.h"
 #include "impeller/geometry/path_component.h"
+#include "impeller/geometry/separated_vector.h"
 
 namespace impeller {
 using VS = SolidFillVertexShader;
@@ -163,45 +164,13 @@
     }
   }
 
-  /// @brief  Represents a ray in 2D space.
-  ///
-  ///         This is a simple convenience struct for handling polyline offset
-  ///         values when generating stroke geometry. For performance reasons,
-  ///         it's sometimes adventageous to track the direction and magnitude
-  ///         for offsets separately.
-  struct Ray {
-    /// The normalized direction of the ray.
-    Vector2 direction;
-
-    /// The magnitude of the ray.
-    Scalar magnitude = 0.0;
-
-    /// Returns the vector representation of the ray.
-    Vector2 GetVector() const { return direction * magnitude; }
-
-    /// Returns the scalar alignment of the two rays.
-    ///
-    /// Domain: [-1, 1]
-    /// A value of 1 indicates the rays are parallel and pointing in the same
-    /// direction. A value of -1 indicates the rays are parallel and pointing in
-    /// opposite directions. A value of 0 indicates the rays are perpendicular.
-    Scalar GetAlignment(const Ray& other) const {
-      return direction.Dot(other.direction);
-    }
-
-    /// Returns the scalar angle between the two rays.
-    Radians AngleTo(const Ray& other) const {
-      return direction.AngleTo(other.direction);
-    }
-  };
-
   /// Computes offset by calculating the direction from point_i - 1 to point_i
   /// if point_i is within `contour_start_point_i` and `contour_end_point_i`;
   /// Otherwise, it uses direction from contour.
-  Ray ComputeOffset(const size_t point_i,
-                    const size_t contour_start_point_i,
-                    const size_t contour_end_point_i,
-                    const Path::PolylineContour& contour) const {
+  SeparatedVector2 ComputeOffset(const size_t point_i,
+                                 const size_t contour_start_point_i,
+                                 const size_t contour_end_point_i,
+                                 const Path::PolylineContour& contour) const {
     Point direction;
     if (point_i >= contour_end_point_i) {
       direction = contour.end_direction;
@@ -211,8 +180,8 @@
       direction = (polyline.GetPoint(point_i) - polyline.GetPoint(point_i - 1))
                       .Normalize();
     }
-    return {.direction = Vector2{-direction.y, direction.x},
-            .magnitude = stroke_width * 0.5f};
+    return SeparatedVector2(Vector2{-direction.y, direction.x},
+                            stroke_width * 0.5f);
   }
 
   void AddVerticesForLinearComponent(VertexWriter& vtx_builder,
@@ -338,8 +307,8 @@
   const CapProc<VertexWriter>& cap_proc;
   const Scalar scale;
 
-  Ray previous_offset;
-  Ray offset;
+  SeparatedVector2 previous_offset;
+  SeparatedVector2 offset;
   SolidFillVertexShader::PerVertexData vtx;
 };
 
diff --git a/impeller/geometry/BUILD.gn b/impeller/geometry/BUILD.gn
index 7697702..e9bfdad 100644
--- a/impeller/geometry/BUILD.gn
+++ b/impeller/geometry/BUILD.gn
@@ -31,6 +31,8 @@
     "rect.h",
     "saturated_math.h",
     "scalar.h",
+    "separated_vector.cc",
+    "separated_vector.h",
     "shear.cc",
     "shear.h",
     "sigma.cc",
diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc
index 124f74b..697471e 100644
--- a/impeller/geometry/geometry_unittests.cc
+++ b/impeller/geometry/geometry_unittests.cc
@@ -19,6 +19,7 @@
 #include "impeller/geometry/point.h"
 #include "impeller/geometry/rect.h"
 #include "impeller/geometry/scalar.h"
+#include "impeller/geometry/separated_vector.h"
 #include "impeller/geometry/size.h"
 
 // TODO(zanderso): https://github.com/flutter/flutter/issues/127701
@@ -1141,6 +1142,56 @@
   ASSERT_VECTOR4_NEAR(result, expected);
 }
 
+TEST(GeometryTest, SeparatedVector2NormalizesWithConstructor) {
+  SeparatedVector2 v(Vector2(10, 0));
+  ASSERT_POINT_NEAR(v.direction, Vector2(1, 0));
+  ASSERT_NEAR(v.magnitude, 10, kEhCloseEnough);
+}
+
+TEST(GeometryTest, SeparatedVector2GetVector) {
+  SeparatedVector2 v(Vector2(10, 0));
+  ASSERT_POINT_NEAR(v.GetVector(), Vector2(10, 0));
+}
+
+TEST(GeometryTest, SeparatedVector2GetAlignment) {
+  // Parallel
+  {
+    SeparatedVector2 v(Vector2(10, 0));
+    Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(5, 0)));
+    ASSERT_NEAR(actual, 1, kEhCloseEnough);
+  }
+
+  // Perpendicular
+  {
+    SeparatedVector2 v(Vector2(10, 0));
+    Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(0, 5)));
+    ASSERT_NEAR(actual, 0, kEhCloseEnough);
+  }
+
+  // Opposite parallel
+  {
+    SeparatedVector2 v(Vector2(0, 10));
+    Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(0, -5)));
+    ASSERT_NEAR(actual, -1, kEhCloseEnough);
+  }
+}
+
+TEST(GeometryTest, SeparatedVector2AngleTo) {
+  {
+    SeparatedVector2 v(Vector2(10, 0));
+    Radians actual = v.AngleTo(SeparatedVector2(Vector2(5, 0)));
+    Radians expected = Radians{0};
+    ASSERT_NEAR(actual.radians, expected.radians, kEhCloseEnough);
+  }
+
+  {
+    SeparatedVector2 v(Vector2(10, 0));
+    Radians actual = v.AngleTo(SeparatedVector2(Vector2(0, -5)));
+    Radians expected = Radians{-kPi / 2};
+    ASSERT_NEAR(actual.radians, expected.radians, kEhCloseEnough);
+  }
+}
+
 TEST(GeometryTest, CanUseVector3AssignmentOperators) {
   {
     Vector3 p(1, 2, 4);
diff --git a/impeller/geometry/separated_vector.cc b/impeller/geometry/separated_vector.cc
new file mode 100644
index 0000000..dbf0da6
--- /dev/null
+++ b/impeller/geometry/separated_vector.cc
@@ -0,0 +1,29 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "separated_vector.h"
+
+namespace impeller {
+
+SeparatedVector2::SeparatedVector2() = default;
+
+SeparatedVector2::SeparatedVector2(Vector2 direction, Scalar magnitude)
+    : direction(direction), magnitude(magnitude){};
+
+SeparatedVector2::SeparatedVector2(Vector2 vector)
+    : direction(vector.Normalize()), magnitude(vector.GetLength()){};
+
+Vector2 SeparatedVector2::GetVector() const {
+  return direction * magnitude;
+}
+
+Scalar SeparatedVector2::GetAlignment(const SeparatedVector2& other) const {
+  return direction.Dot(other.direction);
+}
+
+Radians SeparatedVector2::AngleTo(const SeparatedVector2& other) const {
+  return direction.AngleTo(other.direction);
+}
+
+}  // namespace impeller
diff --git a/impeller/geometry/separated_vector.h b/impeller/geometry/separated_vector.h
new file mode 100644
index 0000000..304029d
--- /dev/null
+++ b/impeller/geometry/separated_vector.h
@@ -0,0 +1,51 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_IMPELLER_GEOMETRY_SEPARATED_VECTOR_H_
+#define FLUTTER_IMPELLER_GEOMETRY_SEPARATED_VECTOR_H_
+
+#include "impeller/geometry/point.h"
+
+#include "impeller/geometry/scalar.h"
+
+namespace impeller {
+
+/// @brief  A Vector2, broken down as a separate magnitude and direction.
+///         Assumes that the direction given is normalized.
+///
+///         This is a simple convenience struct for handling polyline offset
+///         values when generating stroke geometry. For performance reasons,
+///         it's sometimes adventageous to track the direction and magnitude
+///         for offsets separately.
+struct SeparatedVector2 {
+  /// The normalized direction of the vector.
+  Vector2 direction;
+
+  /// The magnitude of the vector.
+  Scalar magnitude = 0.0;
+
+  SeparatedVector2();
+  SeparatedVector2(Vector2 direction, Scalar magnitude);
+  explicit SeparatedVector2(Vector2 vector);
+
+  /// Returns the vector representation of the vector.
+  Vector2 GetVector() const;
+
+  /// Returns the scalar alignment of the two vectors.
+  /// In other words, the dot product of the two normalized vectors.
+  ///
+  /// Range: [-1, 1]
+  /// A value of 1 indicates the directions are parallel and pointing in the
+  /// same direction. A value of -1 indicates the vectors are parallel and
+  /// pointing in opposite directions. A value of 0 indicates the vectors are
+  /// perpendicular.
+  Scalar GetAlignment(const SeparatedVector2& other) const;
+
+  /// Returns the scalar angle between the two rays.
+  Radians AngleTo(const SeparatedVector2& other) const;
+};
+
+#endif  // FLUTTER_IMPELLER_GEOMETRY_SEPARATED_VECTOR_H_
+
+}  // namespace impeller