| // 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. | |
| #pragma once | |
| #include <array> | |
| #include <optional> | |
| #include <ostream> | |
| #include <vector> | |
| #include "impeller/geometry/matrix.h" | |
| #include "impeller/geometry/point.h" | |
| #include "impeller/geometry/scalar.h" | |
| #include "impeller/geometry/size.h" | |
| namespace impeller { | |
| template <class T> | |
| struct TRect { | |
| using Type = T; | |
| TPoint<Type> origin; | |
| TSize<Type> size; | |
| constexpr TRect() : origin({0, 0}), size({0, 0}) {} | |
| constexpr TRect(TSize<Type> size) : origin({0.0, 0.0}), size(size) {} | |
| constexpr TRect(TPoint<Type> origin, TSize<Type> size) | |
| : origin(origin), size(size) {} | |
| constexpr TRect(const Type components[4]) | |
| : origin(components[0], components[1]), | |
| size(components[2], components[3]) {} | |
| constexpr TRect(Type x, Type y, Type width, Type height) | |
| : origin(x, y), size(width, height) {} | |
| constexpr static TRect MakeLTRB(Type left, | |
| Type top, | |
| Type right, | |
| Type bottom) { | |
| return TRect(left, top, right - left, bottom - top); | |
| } | |
| constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height) { | |
| return TRect(x, y, width, height); | |
| } | |
| template <class U> | |
| constexpr static TRect MakeSize(const TSize<U>& size) { | |
| return TRect(0.0, 0.0, size.width, size.height); | |
| } | |
| constexpr static std::optional<TRect> MakePointBounds( | |
| const std::vector<TPoint<Type>>& points) { | |
| if (points.empty()) { | |
| return std::nullopt; | |
| } | |
| auto left = points[0].x; | |
| auto top = points[0].y; | |
| auto right = points[0].x; | |
| auto bottom = points[0].y; | |
| if (points.size() > 1) { | |
| for (size_t i = 1; i < points.size(); i++) { | |
| left = std::min(left, points[i].x); | |
| top = std::min(top, points[i].y); | |
| right = std::max(right, points[i].x); | |
| bottom = std::max(bottom, points[i].y); | |
| } | |
| } | |
| return TRect::MakeLTRB(left, top, right, bottom); | |
| } | |
| constexpr static TRect MakeMaximum() { | |
| return TRect::MakeLTRB(-std::numeric_limits<Scalar>::infinity(), | |
| -std::numeric_limits<Scalar>::infinity(), | |
| std::numeric_limits<Scalar>::infinity(), | |
| std::numeric_limits<Scalar>::infinity()); | |
| } | |
| template <class U> | |
| constexpr explicit TRect(const TRect<U>& other) | |
| : origin(static_cast<TPoint<Type>>(other.origin)), | |
| size(static_cast<TSize<Type>>(other.size)) {} | |
| constexpr TRect operator+(const TRect& r) const { | |
| return TRect({origin.x + r.origin.x, origin.y + r.origin.y}, | |
| {size.width + r.size.width, size.height + r.size.height}); | |
| } | |
| constexpr TRect operator-(const TRect& r) const { | |
| return TRect({origin.x - r.origin.x, origin.y - r.origin.y}, | |
| {size.width - r.size.width, size.height - r.size.height}); | |
| } | |
| constexpr TRect operator*(Type scale) const { | |
| return TRect({origin.x * scale, origin.y * scale}, | |
| {size.width * scale, size.height * scale}); | |
| } | |
| constexpr TRect operator*(const TRect& r) const { | |
| return TRect({origin.x * r.origin.x, origin.y * r.origin.y}, | |
| {size.width * r.size.width, size.height * r.size.height}); | |
| } | |
| constexpr bool operator==(const TRect& r) const { | |
| return origin == r.origin && size == r.size; | |
| } | |
| constexpr bool Contains(const TPoint<Type>& p) const { | |
| return p.x >= origin.x && p.x < origin.x + size.width && p.y >= origin.y && | |
| p.y < origin.y + size.height; | |
| } | |
| constexpr bool Contains(const TRect& o) const { | |
| return Union(o).size == size; | |
| } | |
| constexpr bool IsZero() const { return size.IsZero(); } | |
| constexpr bool IsEmpty() const { return size.IsEmpty(); } | |
| constexpr auto GetLeft() const { | |
| return std::min(origin.x, origin.x + size.width); | |
| } | |
| constexpr auto GetTop() const { | |
| return std::min(origin.y, origin.y + size.height); | |
| } | |
| constexpr auto GetRight() const { | |
| return std::max(origin.x, origin.x + size.width); | |
| } | |
| constexpr auto GetBottom() const { | |
| return std::max(origin.y, origin.y + size.height); | |
| } | |
| constexpr std::array<T, 4> 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}; | |
| } | |
| /// @brief Get a version of this rectangle that has a non-negative size. | |
| constexpr TRect GetPositive() const { | |
| auto ltrb = GetLTRB(); | |
| return MakeLTRB(ltrb[0], ltrb[1], ltrb[2], ltrb[3]); | |
| } | |
| /// @brief Get the points that represent the 4 corners of this rectangle. The | |
| /// order is: Top left, top right, bottom left, bottom right. | |
| constexpr std::array<TPoint<T>, 4> GetPoints() const { | |
| auto [left, top, right, bottom] = GetLTRB(); | |
| return {TPoint(left, top), TPoint(right, top), TPoint(left, bottom), | |
| TPoint(right, bottom)}; | |
| } | |
| constexpr std::array<TPoint<T>, 4> GetTransformedPoints( | |
| const Matrix& transform) const { | |
| auto points = GetPoints(); | |
| for (size_t i = 0; i < points.size(); i++) { | |
| points[i] = transform * points[i]; | |
| } | |
| return points; | |
| } | |
| /// @brief Creates a new bounding box that contains this transformed | |
| /// rectangle. | |
| constexpr TRect TransformBounds(const Matrix& transform) const { | |
| auto points = GetTransformedPoints(transform); | |
| return TRect::MakePointBounds({points.begin(), points.end()}).value(); | |
| } | |
| 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]) // | |
| ); | |
| } | |
| constexpr std::optional<TRect<T>> 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 intersection; | |
| } | |
| constexpr bool IntersectsWithRect(const TRect& o) const { | |
| return Intersection(o).has_value(); | |
| } | |
| }; | |
| using Rect = TRect<Scalar>; | |
| using IRect = TRect<int64_t>; | |
| } // namespace impeller | |
| namespace std { | |
| template <class T> | |
| inline std::ostream& operator<<(std::ostream& out, | |
| const impeller::TRect<T>& r) { | |
| out << "(" << r.origin << ", " << r.size << ")"; | |
| return out; | |
| } | |
| } // namespace std |