From 8e90d388dada953b4d84535a45500b1d9951a8e0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 4 Dec 2021 13:39:44 -0800 Subject: [PATCH] Rect union and intersection. --- impeller/aiks/aiks_unittests.cc | 18 +++++ impeller/geometry/geometry_unittests.cc | 103 ++++++++++++++++-------- impeller/geometry/rect.h | 55 +++++++------ impeller/geometry/size.h | 8 +- 4 files changed, 123 insertions(+), 61 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 36f0a3d1cb0a2..4d0089f0a54a1 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -106,6 +106,24 @@ TEST_F(AiksTest, CanRenderClips) { ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); } +TEST_F(AiksTest, CanSaveLayerStandalone) { + Canvas canvas; + + Paint red; + red.color = Color::Red(); + + Paint alpha; + alpha.color = Color::Red().WithAlpha(0.5); + + canvas.SaveLayer(alpha); + + canvas.DrawCircle({125, 125}, 125, red); + + canvas.Restore(); + + ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture())); +} + TEST_F(AiksTest, CanRenderGroupOpacity) { Canvas canvas; diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index f7f270dfa6420..60163c5b2e6fc 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -141,38 +141,6 @@ TEST(GeometryTest, QuaternionLerp) { ASSERT_QUATERNION_NEAR(q3, expected); } -TEST(GeometryTest, RectWithPoint) { - auto rect = Rect{}; - - auto expected = Rect{}; - - ASSERT_RECT_NEAR(rect, expected); - - rect = rect.WithPoint({100, 100}); - expected = Rect{0, 0, 100, 100}; - ASSERT_RECT_NEAR(rect, expected); - - rect = rect.WithPoint({-11, -12}); - expected = Rect{-11, -12, 111, 112}; - ASSERT_RECT_NEAR(rect, expected); - - rect = rect.WithPoint({55, 65}); - expected = Rect{-11, -12, 111, 112}; - ASSERT_RECT_NEAR(rect, expected); - - rect = rect.WithPoint({-25, 0}); - expected = Rect{-25, -12, 125, 112}; - ASSERT_RECT_NEAR(rect, expected); - - rect = rect.WithPoint({0, -25}); - expected = Rect{-25, -25, 125, 125}; - ASSERT_RECT_NEAR(rect, expected); - - rect = rect.WithPoint({125, 135}); - expected = Rect{-25, -25, 150, 160}; - ASSERT_RECT_NEAR(rect, expected); -} - TEST(GeometryTest, SimplePath) { Path path; @@ -216,7 +184,7 @@ TEST(GeometryTest, BoundingBoxCubic) { Path path; path.AddCubicComponent({120, 160}, {25, 200}, {220, 260}, {220, 40}); auto box = path.GetBoundingBox(); - Rect expected(0, 0, 220, 198.862); + Rect expected(93.9101, 40, 126.09, 158.862); ASSERT_RECT_NEAR(box, expected); } @@ -225,7 +193,7 @@ TEST(GeometryTest, BoundingBoxOfCompositePathIsCorrect) { builder.AddRoundedRect({{10, 10}, {300, 300}}, {50, 50, 50, 50}); auto path = builder.CreatePath(); auto actual = path.GetBoundingBox(); - Rect expected(0, 0, 310, 310); + Rect expected(10, 10, 300, 300); ASSERT_RECT_NEAR(actual, expected); } @@ -273,5 +241,72 @@ TEST(GeometryTest, CanConvertBetweenDegressAndRadians) { } } +TEST(GeometryTest, RectUnion) { + { + Rect a(100, 100, 100, 100); + Rect b(0, 0, 0, 0); + auto u = a.Union(b); + auto expected = Rect(0, 0, 200, 200); + ASSERT_RECT_NEAR(u, expected); + } + + { + Rect a(100, 100, 100, 100); + Rect b(10, 10, 0, 0); + auto u = a.Union(b); + auto expected = Rect(10, 10, 190, 190); + ASSERT_RECT_NEAR(u, expected); + } + + { + Rect a(0, 0, 100, 100); + Rect b(10, 10, 100, 100); + auto u = a.Union(b); + auto expected = Rect(0, 0, 110, 110); + ASSERT_RECT_NEAR(u, expected); + } + + { + Rect a(0, 0, 100, 100); + Rect b(100, 100, 100, 100); + auto u = a.Union(b); + auto expected = Rect(0, 0, 200, 200); + ASSERT_RECT_NEAR(u, expected); + } +} + +TEST(GeometryTest, RectIntersection) { + { + Rect a(100, 100, 100, 100); + Rect b(0, 0, 0, 0); + + auto u = a.Intersection(b); + ASSERT_FALSE(u.has_value()); + } + + { + Rect a(100, 100, 100, 100); + Rect b(10, 10, 0, 0); + auto u = a.Intersection(b); + ASSERT_FALSE(u.has_value()); + } + + { + Rect a(0, 0, 100, 100); + Rect b(10, 10, 100, 100); + auto u = a.Intersection(b); + ASSERT_TRUE(u.has_value()); + auto expected = Rect(10, 10, 90, 90); + ASSERT_RECT_NEAR(u.value(), expected); + } + + { + Rect a(0, 0, 100, 100); + Rect b(100, 100, 100, 100); + auto u = a.Intersection(b); + ASSERT_FALSE(u.has_value()); + } +} + } // namespace testing } // namespace impeller diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index c7477d806c9ee..4517eba06e5db 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -87,35 +88,41 @@ struct TRect { constexpr bool IsEmpty() const { return size.IsEmpty(); } - constexpr TRect WithPoint(const TPoint& p) const { - TRect copy = *this; - if (p.x < origin.x) { - copy.origin.x = p.x; - copy.size.width += (origin.x - p.x); - } - - if (p.y < origin.y) { - copy.origin.y = p.y; - copy.size.height += (origin.y - p.y); - } + constexpr std::array GetLTRB() const { + const auto left = std::min(origin.x, origin.x + size.width); + const auto top = std::min(origin.y, origin.y + size.height); + const auto right = std::max(origin.x, origin.x + size.width); + const auto bottom = std::max(origin.y, origin.y + size.height); + return {left, top, right, bottom}; + } - if (p.x > (size.width + origin.x)) { - copy.size.width += p.x - (size.width + origin.x); - } + constexpr TRect Union(const TRect& o) const { + auto this_ltrb = GetLTRB(); + auto other_ltrb = o.GetLTRB(); + return TRect::MakeLTRB(std::min(this_ltrb[0], other_ltrb[0]), // + std::min(this_ltrb[1], other_ltrb[1]), // + std::max(this_ltrb[2], other_ltrb[2]), // + std::max(this_ltrb[3], other_ltrb[3]) // + ); + } - if (p.y > (size.height + origin.y)) { - copy.size.height += p.y - (size.height + origin.y); + constexpr std::optional> Intersection(const TRect& o) const { + auto this_ltrb = GetLTRB(); + auto other_ltrb = o.GetLTRB(); + auto intersection = + TRect::MakeLTRB(std::max(this_ltrb[0], other_ltrb[0]), // + std::max(this_ltrb[1], other_ltrb[1]), // + std::min(this_ltrb[2], other_ltrb[2]), // + std::min(this_ltrb[3], other_ltrb[3]) // + ); + if (intersection.size.IsEmpty()) { + return std::nullopt; } - - return copy; + return intersection; } - constexpr TRect WithPoints(const std::vector>& points) const { - TRect box = *this; - for (const auto& point : points) { - box = box.WithPoint(point); - } - return box; + constexpr bool IntersectsWithRect(const TRect& o) const { + return Interesection(o).has_value(); } }; diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 526866060f27e..f56e682cdc7b6 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -79,11 +79,13 @@ struct TSize { constexpr Type Area() const { return width * height; } - constexpr bool IsZero() const { return width * height == 0.0; } + constexpr bool IsPositive() const { return width > 0 && height > 0; } - constexpr bool IsPositive() const { return width * height > 0.0; } + constexpr bool IsNegative() const { return width < 0 || height < 0; } - constexpr bool IsEmpty() const { return !IsPositive(); } + constexpr bool IsZero() const { return width == 0 || height == 0; } + + constexpr bool IsEmpty() const { return IsNegative() || IsZero(); } template static constexpr TSize Ceil(const TSize& other) {