diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 111447feb7a90..96e76e39ad392 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -42151,6 +42151,8 @@ ORIGIN: ../../../flutter/impeller/geometry/rect.cc + ../../../flutter/LICENSE 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.cc 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 c4220a21ef674..5956adff1c34f 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 @@ class StrokeGenerator { } } - /// @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 @@ class StrokeGenerator { 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 @@ class StrokeGenerator { const CapProc& 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 769770217fb05..e9bfdad69d5f5 100644 --- a/impeller/geometry/BUILD.gn +++ b/impeller/geometry/BUILD.gn @@ -31,6 +31,8 @@ impeller_component("geometry") { "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 124f74bb892c9..697471e286fc1 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 @@ TEST(GeometryTest, Vector4Lerp) { 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 0000000000000..dbf0da62ced36 --- /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 0000000000000..304029d2d6aa0 --- /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