| // 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_ROUND_RECT_H_ |
| #define FLUTTER_IMPELLER_GEOMETRY_ROUND_RECT_H_ |
| |
| #include "flutter/impeller/geometry/point.h" |
| #include "flutter/impeller/geometry/rect.h" |
| #include "flutter/impeller/geometry/size.h" |
| |
| namespace impeller { |
| |
| struct RoundingRadii { |
| Size top_left; |
| Size top_right; |
| Size bottom_left; |
| Size bottom_right; |
| |
| constexpr static RoundingRadii MakeRadius(Scalar radius) { |
| return {Size(radius), Size(radius), Size(radius), Size(radius)}; |
| } |
| |
| constexpr static RoundingRadii MakeRadii(Size radii) { |
| return {radii, radii, radii, radii}; |
| } |
| |
| constexpr bool IsFinite() const { |
| return top_left.IsFinite() && // |
| top_right.IsFinite() && // |
| bottom_left.IsFinite() && // |
| bottom_right.IsFinite(); |
| } |
| |
| constexpr bool AreAllCornersEmpty() const { |
| return top_left.IsEmpty() && // |
| top_right.IsEmpty() && // |
| bottom_left.IsEmpty() && // |
| bottom_right.IsEmpty(); |
| } |
| |
| constexpr bool AreAllCornersSame(Scalar tolerance = kEhCloseEnough) const { |
| return ScalarNearlyEqual(top_left.width, top_right.width, tolerance) && |
| ScalarNearlyEqual(top_left.width, bottom_right.width, tolerance) && |
| ScalarNearlyEqual(top_left.width, bottom_left.width, tolerance) && |
| ScalarNearlyEqual(top_left.height, top_right.height, tolerance) && |
| ScalarNearlyEqual(top_left.height, bottom_right.height, tolerance) && |
| ScalarNearlyEqual(top_left.height, bottom_left.height, tolerance); |
| } |
| |
| constexpr inline RoundingRadii operator*(Scalar scale) { |
| return { |
| .top_left = top_left * scale, |
| .top_right = top_right * scale, |
| .bottom_left = bottom_left * scale, |
| .bottom_right = bottom_right * scale, |
| }; |
| } |
| |
| [[nodiscard]] constexpr bool operator==(const RoundingRadii& rr) const { |
| return top_left == rr.top_left && // |
| top_right == rr.top_right && // |
| bottom_left == rr.bottom_left && // |
| bottom_right == rr.bottom_right; |
| } |
| |
| [[nodiscard]] constexpr bool operator!=(const RoundingRadii& rr) const { |
| return !(*this == rr); |
| } |
| }; |
| |
| struct RoundRect { |
| RoundRect() = default; |
| |
| constexpr static RoundRect MakeRect(const Rect& rect) { |
| return MakeRectRadii(rect, RoundingRadii()); |
| } |
| |
| constexpr static RoundRect MakeOval(const Rect& rect) { |
| return MakeRectRadii(rect, RoundingRadii::MakeRadii(rect.GetSize() * 0.5f)); |
| } |
| |
| constexpr static RoundRect MakeRectRadius(const Rect& rect, Scalar radius) { |
| return MakeRectRadii(rect, RoundingRadii::MakeRadius(radius)); |
| } |
| |
| constexpr static RoundRect MakeRectXY(const Rect& rect, |
| Scalar x_radius, |
| Scalar y_radius) { |
| return MakeRectRadii(rect, |
| RoundingRadii::MakeRadii(Size(x_radius, y_radius))); |
| } |
| |
| constexpr static RoundRect MakeRectXY(const Rect& rect, Size corner_radii) { |
| return MakeRectRadii(rect, RoundingRadii::MakeRadii(corner_radii)); |
| } |
| |
| static RoundRect MakeRectRadii(const Rect& rect, const RoundingRadii& radii); |
| |
| constexpr const Rect& GetBounds() const { return bounds_; } |
| constexpr const RoundingRadii& GetRadii() const { return radii_; } |
| |
| [[nodiscard]] constexpr bool IsFinite() const { |
| return bounds_.IsFinite() && // |
| radii_.top_left.IsFinite() && // |
| radii_.top_right.IsFinite() && // |
| radii_.bottom_left.IsFinite() && // |
| radii_.bottom_right.IsFinite(); |
| } |
| |
| [[nodiscard]] constexpr bool IsEmpty() const { return bounds_.IsEmpty(); } |
| |
| [[nodiscard]] constexpr bool IsRect() const { |
| return !bounds_.IsEmpty() && radii_.AreAllCornersEmpty(); |
| } |
| |
| [[nodiscard]] constexpr bool IsOval() const { |
| return !bounds_.IsEmpty() && radii_.AreAllCornersSame() && |
| ScalarNearlyEqual(radii_.top_left.width, |
| bounds_.GetWidth() * 0.5f) && |
| ScalarNearlyEqual(radii_.top_left.height, |
| bounds_.GetHeight() * 0.5f); |
| } |
| |
| /// @brief Returns true iff the provided point |p| is inside the |
| /// half-open interior of this rectangle. |
| /// |
| /// For purposes of containment, a rectangle contains points |
| /// along the top and left edges but not points along the |
| /// right and bottom edges so that a point is only ever |
| /// considered inside one of two abutting rectangles. |
| [[nodiscard]] bool Contains(const Point& p) const; |
| |
| /// @brief Returns a new round rectangle translated by the given offset. |
| [[nodiscard]] constexpr RoundRect Shift(Scalar dx, Scalar dy) const { |
| // Just in case, use the factory rather than the internal constructor |
| // as shifting the rectangle may increase/decrease its bit precision |
| // so we should re-validate the radii to the newly located rectangle. |
| return MakeRectRadii(bounds_.Shift(dx, dy), radii_); |
| } |
| |
| /// @brief Returns a round rectangle with expanded edges. Negative expansion |
| /// results in shrinking. |
| [[nodiscard]] constexpr RoundRect Expand(Scalar left, |
| Scalar top, |
| Scalar right, |
| Scalar bottom) const { |
| // Use the factory rather than the internal constructor as the changing |
| // size of the rectangle requires that we re-validate the radii to the |
| // newly sized rectangle. |
| return MakeRectRadii(bounds_.Expand(left, top, right, bottom), radii_); |
| } |
| |
| /// @brief Returns a round rectangle with expanded edges. Negative expansion |
| /// results in shrinking. |
| [[nodiscard]] constexpr RoundRect Expand(Scalar horizontal, |
| Scalar vertical) const { |
| // Use the factory rather than the internal constructor as the changing |
| // size of the rectangle requires that we re-validate the radii to the |
| // newly sized rectangle. |
| return MakeRectRadii(bounds_.Expand(horizontal, vertical), radii_); |
| } |
| |
| /// @brief Returns a round rectangle with expanded edges. Negative expansion |
| /// results in shrinking. |
| [[nodiscard]] constexpr RoundRect Expand(Scalar amount) const { |
| // Use the factory rather than the internal constructor as the changing |
| // size of the rectangle requires that we re-validate the radii to the |
| // newly sized rectangle. |
| return MakeRectRadii(bounds_.Expand(amount), radii_); |
| } |
| |
| [[nodiscard]] constexpr bool operator==(const RoundRect& rr) const { |
| return bounds_ == rr.bounds_ && radii_ == rr.radii_; |
| } |
| |
| [[nodiscard]] constexpr bool operator!=(const RoundRect& r) const { |
| return !(*this == r); |
| } |
| |
| private: |
| constexpr RoundRect(const Rect& bounds, const RoundingRadii& radii) |
| : bounds_(bounds), radii_(radii) {} |
| |
| Rect bounds_; |
| RoundingRadii radii_; |
| }; |
| |
| } // namespace impeller |
| |
| namespace std { |
| |
| inline std::ostream& operator<<(std::ostream& out, |
| const impeller::RoundingRadii& rr) { |
| out << "(" // |
| << "ul: " << rr.top_left << ", " // |
| << "ur: " << rr.top_right << ", " // |
| << "ll: " << rr.bottom_left << ", " // |
| << "lr: " << rr.bottom_right // |
| << ")"; |
| return out; |
| } |
| |
| inline std::ostream& operator<<(std::ostream& out, |
| const impeller::RoundRect& rr) { |
| out << "(" // |
| << "rect: " << rr.GetBounds() << ", " // |
| << "radii: " << rr.GetRadii() // |
| << ")"; |
| return out; |
| } |
| |
| } // namespace std |
| |
| #endif // FLUTTER_IMPELLER_GEOMETRY_ROUND_RECT_H_ |