Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
rect.h
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef FLUTTER_IMPELLER_GEOMETRY_RECT_H_
6#define FLUTTER_IMPELLER_GEOMETRY_RECT_H_
7
8#include <array>
9#include <optional>
10#include <ostream>
11#include <vector>
12
13#include "fml/logging.h"
19
20namespace impeller {
21
22#define ONLY_ON_FLOAT_M(Modifiers, Return) \
23 template <typename U = T> \
24 Modifiers std::enable_if_t<std::is_floating_point_v<U>, Return>
25#define ONLY_ON_FLOAT(Return) DL_ONLY_ON_FLOAT_M(, Return)
26
27/// Templated struct for holding an axis-aligned rectangle.
28///
29/// Rectangles are defined as 4 axis-aligned edges that might contain
30/// space. They can be viewed as 2 X coordinates that define the
31/// left and right edges and 2 Y coordinates that define the top and
32/// bottom edges; or they can be viewed as an origin and horizontal
33/// and vertical dimensions (width and height).
34///
35/// When the left and right edges are equal or reversed (right <= left)
36/// or the top and bottom edges are equal or reversed (bottom <= top),
37/// the rectangle is considered empty. Considering the rectangle in XYWH
38/// form, the width and/or the height would be negative or zero. Such
39/// reversed/empty rectangles contain no space and act as such in the
40/// methods that operate on them (Intersection, Union, IntersectsWithRect,
41/// Contains, Cutout, etc.)
42///
43/// Rectangles cannot be modified by any method and a new value can only
44/// be stored into an existing rect using assignment. This keeps the API
45/// clean compared to implementations that might have similar methods
46/// that produce the answer in place, or construct a new object with
47/// the answer, or place the result in an indicated result object.
48///
49/// Methods that might fail to produce an answer will use |std::optional|
50/// to indicate that success or failure (see |Intersection| and |CutOut|).
51/// For convenience, |Intersection| and |Union| both have overloaded
52/// variants that take |std::optional| arguments and treat them as if
53/// the argument was an empty rect to allow chaining multiple such methods
54/// and only needing to check the optional condition of the final result.
55/// The primary methods also provide |...OrEmpty| overloaded variants that
56/// translate an empty optional answer into a simple empty rectangle of the
57/// same type.
58///
59/// Rounding instance methods are not provided as the return value might
60/// be wanted as another floating point rectangle or sometimes as an integer
61/// rectangle. Instead a |RoundOut| factory, defined only for floating point
62/// input rectangles, is provided to provide control over the result type.
63///
64/// NaN and Infinity values
65///
66/// Constructing an LTRB rectangle using Infinity values should work as
67/// expected with either 0 or +Infinity returned as dimensions depending on
68/// which side the Infinity values are on and the sign.
69///
70/// Constructing an XYWH rectangle using Infinity values will usually
71/// not work if the math requires the object to compute a right or bottom
72/// edge from ([xy] -Infinity + [wh] +Infinity). Other combinations might
73/// work.
74///
75/// The special factory |MakeMaximum| is provided to construct a rectangle
76/// of the indicated coordinate type that covers all finite coordinates.
77/// It does not use infinity values, but rather the largest finite values
78/// to avoid math that might produce a NaN value from various getters.
79///
80/// Any rectangle that is constructed with, or computed to have a NaN value
81/// will be considered the same as any empty rectangle.
82///
83/// Empty Rectangle canonical results summary:
84///
85/// Union will ignore any empty rects and return the other rect
86/// Intersection will return nullopt if either rect is empty
87/// IntersectsWithRect will return false if either rect is empty
88/// Cutout will return the source rect if the argument is empty
89/// Cutout will return nullopt if the source rectangle is empty
90/// Contains(Point) will return false if the source rectangle is empty
91/// Contains(Rect) will return false if the source rectangle is empty
92/// Contains(Rect) will otherwise return true if the argument is empty
93/// Specifically, EmptyRect.Contains(EmptyRect) returns false
94///
95/// ---------------
96/// Special notes on problems using the XYWH form of specifying rectangles:
97///
98/// It is possible to have integer rectangles whose dimensions exceed
99/// the maximum number that their coordinates can represent since
100/// (MAX_INT - MIN_INT) overflows the representable positive numbers.
101/// Floating point rectangles technically have a similar issue in that
102/// overflow can occur, but it will be automatically converted into
103/// either an infinity, or a finite-overflow value and still be
104/// representable, just with little to no precision.
105///
106/// Secondly, specifying a rectangle using XYWH leads to cases where the
107/// math for (x+w) and/or (y+h) are also beyond the maximum representable
108/// coordinates. For N-bit integer rectangles declared as XYWH, the
109/// maximum right coordinate will require N+1 signed bits which cannot be
110/// stored in storage that uses N-bit integers.
111///
112/// Saturated math is used when constructing a rectangle from XYWH values
113/// and when returning the dimensions of the rectangle. Constructing an
114/// integer rectangle from values such that xy + wh is beyond the range
115/// of the integer type will place the right or bottom edges at the maximum
116/// value for the integer type. Similarly, constructing an integer rectangle
117/// such that the distance from the left to the right (or top to bottom) is
118/// greater than the range of the integer type will simply return the
119/// maximum integer value as the dimension. Floating point rectangles are
120/// naturally saturated by the rules of IEEE arithmetic.
121template <class T>
122struct TRect {
123 private:
124 using Type = T;
125
126 public:
127 constexpr TRect() : left_(0), top_(0), right_(0), bottom_(0) {}
128
129 constexpr static TRect MakeLTRB(Type left,
130 Type top,
131 Type right,
132 Type bottom) {
133 return TRect(left, top, right, bottom);
134 }
135
136 constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height) {
137 return TRect(x, y, saturated::Add(x, width), saturated::Add(y, height));
138 }
139
140 constexpr static TRect MakeWH(Type width, Type height) {
141 return TRect(0, 0, width, height);
142 }
143
144 constexpr static TRect MakeOriginSize(const TPoint<Type>& origin,
145 const TSize<Type>& size) {
146 return MakeXYWH(origin.x, origin.y, size.width, size.height);
147 }
148
149 template <class U>
150 constexpr static TRect MakeSize(const TSize<U>& size) {
151 return TRect(0.0, 0.0, size.width, size.height);
152 }
153
154 /// Construct the rectangular bounds of a circle with the supplied
155 /// center point and uniform radius.
156 constexpr static TRect MakeCircleBounds(const TPoint<Type>& center,
157 Type radius) {
158 return MakeLTRB(center.x - radius, center.y - radius, //
159 center.x + radius, center.y + radius);
160 }
161
162 /// Construct the rectangular bounds of a circle with the supplied
163 /// center point and non-uniform horizontal and vertical radii.
164 constexpr static TRect MakeEllipseBounds(const TPoint<Type>& center,
165 const TSize<Type>& radii) {
166 return MakeLTRB(center.x - radii.width, center.y - radii.height, //
167 center.x + radii.width, center.y + radii.height);
168 }
169
170 /// Construct the rectangular bounds of a circle with the supplied
171 /// center point and non-uniform horizontal and vertical radii.
172 constexpr static TRect MakeEllipseBounds(const TPoint<Type>& center,
173 const TPoint<Type>& radii) {
174 return MakeLTRB(center.x - radii.x, center.y - radii.y, //
175 center.x + radii.x, center.y + radii.y);
176 }
177
178 /// Construct a floating point rect |Rect| from another Rect of a
179 /// potentially different storage type (eg. |IRect|).
180 template <class U, class FT = T>
181 constexpr static std::enable_if_t<std::is_floating_point_v<FT>, TRect> Make(
182 const TRect<U>& rect) {
183 return MakeLTRB(
184 static_cast<FT>(rect.GetLeft()), static_cast<FT>(rect.GetTop()),
185 static_cast<FT>(rect.GetRight()), static_cast<FT>(rect.GetBottom()));
186 }
187
188 template <typename U>
189 constexpr static std::optional<TRect> MakePointBounds(const U& value) {
190 return MakePointBounds(value.begin(), value.end());
191 }
192
193 template <typename PointIter>
194 constexpr static std::optional<TRect> MakePointBounds(const PointIter first,
195 const PointIter last) {
196 if (first == last) {
197 return std::nullopt;
198 }
199 auto left = first->x;
200 auto top = first->y;
201 auto right = first->x;
202 auto bottom = first->y;
203 for (auto it = first + 1; it < last; ++it) {
204 left = std::min(left, it->x);
205 top = std::min(top, it->y);
206 right = std::max(right, it->x);
207 bottom = std::max(bottom, it->y);
208 }
209 return TRect::MakeLTRB(left, top, right, bottom);
210 }
211
212 [[nodiscard]] constexpr static TRect MakeMaximum() {
213 return TRect::MakeLTRB(std::numeric_limits<Type>::lowest(),
214 std::numeric_limits<Type>::lowest(),
215 std::numeric_limits<Type>::max(),
216 std::numeric_limits<Type>::max());
217 }
218
219 [[nodiscard]] constexpr bool operator==(const TRect& r) const {
220 return left_ == r.left_ && //
221 top_ == r.top_ && //
222 right_ == r.right_ && //
223 bottom_ == r.bottom_;
224 }
225
226 [[nodiscard]] constexpr TRect Scale(Type scale) const {
227 return TRect(left_ * scale, //
228 top_ * scale, //
229 right_ * scale, //
230 bottom_ * scale);
231 }
232
233 [[nodiscard]] constexpr TRect Scale(Type scale_x, Type scale_y) const {
234 return TRect(left_ * scale_x, //
235 top_ * scale_y, //
236 right_ * scale_x, //
237 bottom_ * scale_y);
238 }
239
240 [[nodiscard]] constexpr TRect Scale(TPoint<T> scale) const {
241 return Scale(scale.x, scale.y);
242 }
243
244 [[nodiscard]] constexpr TRect Scale(TSize<T> scale) const {
245 return Scale(scale.width, scale.height);
246 }
247
248 /// @brief Returns true iff the provided point |p| is inside the
249 /// half-open interior of this rectangle.
250 ///
251 /// For purposes of containment, a rectangle contains points
252 /// along the top and left edges but not points along the
253 /// right and bottom edges so that a point is only ever
254 /// considered inside one of two abutting rectangles.
255 [[nodiscard]] constexpr bool Contains(const TPoint<Type>& p) const {
256 return !this->IsEmpty() && //
257 p.x >= left_ && //
258 p.y >= top_ && //
259 p.x < right_ && //
260 p.y < bottom_;
261 }
262
263 /// @brief Returns true iff the provided point |p| is inside the
264 /// closed-range interior of this rectangle.
265 ///
266 /// Unlike the regular |Contains(TPoint)| method, this method
267 /// considers all points along the boundary of the rectangle
268 /// to be contained within the rectangle - useful for testing
269 /// if vertices that define a filled shape would carry the
270 /// interior of that shape outside the bounds of the rectangle.
271 /// Since both geometries are defining half-open spaces, their
272 /// defining geometry needs to consider their boundaries to
273 /// be equivalent with respect to interior and exterior.
274 [[nodiscard]] constexpr bool ContainsInclusive(const TPoint<Type>& p) const {
275 return !this->IsEmpty() && //
276 p.x >= left_ && //
277 p.y >= top_ && //
278 p.x <= right_ && //
279 p.y <= bottom_;
280 }
281
282 /// @brief Returns true iff this rectangle is not empty and it also
283 /// contains every point considered inside the provided
284 /// rectangle |o| (as determined by |Contains(TPoint)|).
285 ///
286 /// This is similar to a definition where the result is true iff
287 /// the union of the two rectangles is equal to this rectangle,
288 /// ignoring precision issues with performing those operations
289 /// and assuming that empty rectangles are never equal.
290 ///
291 /// An empty rectangle can contain no other rectangle.
292 ///
293 /// An empty rectangle is, however, contained within any
294 /// other non-empy rectangle as the set of points it contains
295 /// is an empty set and so there are no points to fail the
296 /// containment criteria.
297 [[nodiscard]] constexpr bool Contains(const TRect& o) const {
298 return !this->IsEmpty() && //
299 (o.IsEmpty() || (o.left_ >= left_ && //
300 o.top_ >= top_ && //
301 o.right_ <= right_ && //
302 o.bottom_ <= bottom_));
303 }
304
305 template <class U, class FT = T>
306 [[nodiscard]] constexpr std::enable_if_t<std::is_floating_point_v<FT>, bool>
307 Contains(const TRect<U>& o) const {
308 return !this->IsEmpty() && //
309 (o.IsEmpty() || (o.GetLeft() >= left_ && //
310 o.GetTop() >= top_ && //
311 o.GetRight() <= right_ && //
312 o.GetBottom() <= bottom_));
313 }
314
315 /// @brief Returns true if all of the fields of this floating point
316 /// rectangle are finite.
317 ///
318 /// Note that the results of |GetWidth()| and |GetHeight()| may
319 /// still be infinite due to overflow even if the fields themselves
320 /// are finite.
321 ONLY_ON_FLOAT_M([[nodiscard]] constexpr, bool)
322 IsFinite() const {
323 return std::isfinite(left_) && //
324 std::isfinite(top_) && //
325 std::isfinite(right_) && //
326 std::isfinite(bottom_);
327 }
328
329 /// @brief Returns true if either of the width or height are 0, negative,
330 /// or NaN.
331 [[nodiscard]] constexpr bool IsEmpty() const {
332 // Computing the non-empty condition and negating the result causes any
333 // NaN value to return true - i.e. is considered empty.
334 return !(left_ < right_ && top_ < bottom_);
335 }
336
337 /// @brief Returns true if width and height are equal and neither is NaN.
338 [[nodiscard]] constexpr bool IsSquare() const {
339 // empty rectangles can technically be "square", but would be
340 // misleading to most callers. Using |IsEmpty| also prevents
341 // "non-empty and non-overflowing" computations from happening
342 // to be equal to "empty and overflowing" results.
343 // (Consider LTRB(10, 15, MAX-2, MIN+2) which is empty, but both
344 // w/h subtractions equal "5").
345 return !IsEmpty() && (right_ - left_) == (bottom_ - top_);
346 }
347
348 [[nodiscard]] constexpr bool IsMaximum() const {
349 return *this == MakeMaximum();
350 }
351
352 /// @brief Returns the upper left corner of the rectangle as specified
353 /// by the left/top or x/y values when it was constructed.
354 [[nodiscard]] constexpr TPoint<Type> GetOrigin() const {
355 return {left_, top_};
356 }
357
358 /// @brief Returns the size of the rectangle which may be negative in
359 /// either width or height and may have been clipped to the
360 /// maximum integer values for integer rects whose size overflows.
361 [[nodiscard]] constexpr TSize<Type> GetSize() const {
362 return {GetWidth(), GetHeight()};
363 }
364
365 /// @brief Returns the X coordinate of the upper left corner, equivalent
366 /// to |GetOrigin().x|
367 [[nodiscard]] constexpr Type GetX() const { return left_; }
368
369 /// @brief Returns the Y coordinate of the upper left corner, equivalent
370 /// to |GetOrigin().y|
371 [[nodiscard]] constexpr Type GetY() const { return top_; }
372
373 /// @brief Returns the width of the rectangle, equivalent to
374 /// |GetSize().width|
375 [[nodiscard]] constexpr Type GetWidth() const {
376 return saturated::Sub(right_, left_);
377 }
378
379 /// @brief Returns the height of the rectangle, equivalent to
380 /// |GetSize().height|
381 [[nodiscard]] constexpr Type GetHeight() const {
382 return saturated::Sub(bottom_, top_);
383 }
384
385 [[nodiscard]] constexpr auto GetLeft() const { return left_; }
386
387 [[nodiscard]] constexpr auto GetTop() const { return top_; }
388
389 [[nodiscard]] constexpr auto GetRight() const { return right_; }
390
391 [[nodiscard]] constexpr auto GetBottom() const { return bottom_; }
392
393 [[nodiscard]] constexpr TPoint<T> GetLeftTop() const { //
394 return {left_, top_};
395 }
396
397 [[nodiscard]] constexpr TPoint<T> GetRightTop() const {
398 return {right_, top_};
399 }
400
401 [[nodiscard]] constexpr TPoint<T> GetLeftBottom() const {
402 return {left_, bottom_};
403 }
404
405 [[nodiscard]] constexpr TPoint<T> GetRightBottom() const {
406 return {right_, bottom_};
407 }
408
409 /// @brief Get the area of the rectangle, equivalent to |GetSize().Area()|
410 [[nodiscard]] constexpr T Area() const {
411 // TODO(141710): Use saturated math to avoid overflow.
412 return IsEmpty() ? 0 : (right_ - left_) * (bottom_ - top_);
413 }
414
415 /// @brief Get the center point as a |Point|.
416 [[nodiscard]] constexpr Point GetCenter() const {
417 return {saturated::AverageScalar(left_, right_),
418 saturated::AverageScalar(top_, bottom_)};
419 }
420
421 [[nodiscard]] constexpr std::array<T, 4> GetLTRB() const {
422 return {left_, top_, right_, bottom_};
423 }
424
425 /// @brief Get the x, y coordinates of the origin and the width and
426 /// height of the rectangle in an array.
427 [[nodiscard]] constexpr std::array<T, 4> GetXYWH() const {
428 return {left_, top_, GetWidth(), GetHeight()};
429 }
430
431 /// @brief Get a version of this rectangle that has a non-negative size.
432 [[nodiscard]] constexpr TRect GetPositive() const {
433 if (!IsEmpty()) {
434 return *this;
435 }
436 return {
437 std::min(left_, right_),
438 std::min(top_, bottom_),
439 std::max(left_, right_),
440 std::max(top_, bottom_),
441 };
442 }
443
444 /// @brief Get the points that represent the 4 corners of this rectangle
445 /// in a Z order that is compatible with triangle strips or a set
446 /// of all zero points if the rectangle is empty.
447 /// The order is: Top left, top right, bottom left, bottom right.
448 [[nodiscard]] constexpr std::array<TPoint<T>, 4> GetPoints() const {
449 if (IsEmpty()) {
450 return {};
451 }
452 return {
453 TPoint{left_, top_},
454 TPoint{right_, top_},
455 TPoint{left_, bottom_},
456 TPoint{right_, bottom_},
457 };
458 }
459
460 [[nodiscard]] constexpr std::array<TPoint<T>, 4> GetTransformedPoints(
461 const Matrix& transform) const {
462 auto points = GetPoints();
463 for (size_t i = 0; i < points.size(); i++) {
464 points[i] = transform * points[i];
465 }
466 return points;
467 }
468
469 /// @brief Creates a new bounding box that contains this transformed
470 /// rectangle, clipped against the near clipping plane if
471 /// necessary.
472 [[nodiscard]] constexpr TRect TransformAndClipBounds(
473 const Matrix& transform) const {
474 if (!transform.HasPerspective2D()) {
476 }
477
478 if (IsEmpty()) {
479 return {};
480 }
481
482 auto ul = transform.TransformHomogenous({left_, top_});
483 auto ur = transform.TransformHomogenous({right_, top_});
484 auto ll = transform.TransformHomogenous({left_, bottom_});
485 auto lr = transform.TransformHomogenous({right_, bottom_});
486
487 // It can probably be proven that we only ever have 5 points at most
488 // which happens when only 1 corner is clipped and we get 2 points
489 // in return for it as we interpolate against its neighbors.
490 Point points[8];
491 int index = 0;
492
493 // Process (clip and interpolate) each point against its 2 neighbors:
494 // left, pt, right
495 index = ClipAndInsert(points, index, ll, ul, ur);
496 index = ClipAndInsert(points, index, ul, ur, lr);
497 index = ClipAndInsert(points, index, ur, lr, ll);
498 index = ClipAndInsert(points, index, lr, ll, ul);
499
500 auto bounds = TRect::MakePointBounds(points, points + index);
501 return bounds.value_or(TRect{});
502 }
503
504 /// @brief Creates a new bounding box that contains this transformed
505 /// rectangle.
506 [[nodiscard]] constexpr TRect TransformBounds(const Matrix& transform) const {
507 if (IsEmpty()) {
508 return {};
509 }
511 auto bounds = TRect::MakePointBounds(points.begin(), points.end());
512 if (bounds.has_value()) {
513 return bounds.value();
514 }
516 }
517
518 /// @brief Constructs a Matrix that will map all points in the coordinate
519 /// space of the rectangle into a new normalized coordinate space
520 /// where the upper left corner of the rectangle maps to (0, 0)
521 /// and the lower right corner of the rectangle maps to (1, 1).
522 ///
523 /// Empty and non-finite rectangles will return a zero-scaling
524 /// transform that maps all points to (0, 0).
525 [[nodiscard]] constexpr Matrix GetNormalizingTransform() const {
526 if (!IsEmpty()) {
527 Scalar sx = 1.0 / GetWidth();
528 Scalar sy = 1.0 / GetHeight();
529 Scalar tx = left_ * -sx;
530 Scalar ty = top_ * -sy;
531
532 // Exclude NaN and infinities and either scale underflowing to zero
533 if (sx != 0.0 && sy != 0.0 && 0.0 * sx * sy * tx * ty == 0.0) {
534 // clang-format off
535 return Matrix( sx, 0.0f, 0.0f, 0.0f,
536 0.0f, sy, 0.0f, 0.0f,
537 0.0f, 0.0f, 1.0f, 0.0f,
538 tx, ty, 0.0f, 1.0f);
539 // clang-format on
540 }
541 }
542
543 // Map all coordinates to the origin.
544 return Matrix::MakeScale({0.0f, 0.0f, 1.0f});
545 }
546
547 [[nodiscard]] constexpr TRect Union(const TRect& o) const {
548 if (IsEmpty()) {
549 return o;
550 }
551 if (o.IsEmpty()) {
552 return *this;
553 }
554 return {
555 std::min(left_, o.left_),
556 std::min(top_, o.top_),
557 std::max(right_, o.right_),
558 std::max(bottom_, o.bottom_),
559 };
560 }
561
562 [[nodiscard]] constexpr std::optional<TRect> Intersection(
563 const TRect& o) const {
564 if (IntersectsWithRect(o)) {
565 return TRect{
566 std::max(left_, o.left_),
567 std::max(top_, o.top_),
568 std::min(right_, o.right_),
569 std::min(bottom_, o.bottom_),
570 };
571 } else {
572 return std::nullopt;
573 }
574 }
575
576 [[nodiscard]] constexpr TRect IntersectionOrEmpty(const TRect& o) const {
577 return Intersection(o).value_or(TRect());
578 }
579
580 [[nodiscard]] constexpr bool IntersectsWithRect(const TRect& o) const {
581 return !IsEmpty() && //
582 !o.IsEmpty() && //
583 left_ < o.right_ && //
584 top_ < o.bottom_ && //
585 right_ > o.left_ && //
586 bottom_ > o.top_;
587 }
588
589 /// @brief Returns the new boundary rectangle that would result from this
590 /// rectangle being cut out by the specified rectangle.
591 [[nodiscard]] constexpr std::optional<TRect<T>> Cutout(const TRect& o) const {
592 if (IsEmpty()) {
593 // This test isn't just a short-circuit, it also prevents the concise
594 // math below from returning the wrong answer on empty rects.
595 // Once we know that this rectangle is not empty, the math below can
596 // only succeed in computing a value if o is also non-empty and non-nan.
597 // Otherwise, the method returns *this by default.
598 return std::nullopt;
599 }
600
601 const auto& [a_left, a_top, a_right, a_bottom] = GetLTRB(); // Source rect.
602 const auto& [b_left, b_top, b_right, b_bottom] = o.GetLTRB(); // Cutout.
603 if (b_left <= a_left && b_right >= a_right) {
604 if (b_top <= a_top && b_bottom >= a_bottom) {
605 // Full cutout.
606 return std::nullopt;
607 }
608 if (b_top <= a_top && b_bottom > a_top) {
609 // Cuts off the top.
610 return TRect::MakeLTRB(a_left, b_bottom, a_right, a_bottom);
611 }
612 if (b_bottom >= a_bottom && b_top < a_bottom) {
613 // Cuts off the bottom.
614 return TRect::MakeLTRB(a_left, a_top, a_right, b_top);
615 }
616 }
617 if (b_top <= a_top && b_bottom >= a_bottom) {
618 if (b_left <= a_left && b_right > a_left) {
619 // Cuts off the left.
620 return TRect::MakeLTRB(b_right, a_top, a_right, a_bottom);
621 }
622 if (b_right >= a_right && b_left < a_right) {
623 // Cuts off the right.
624 return TRect::MakeLTRB(a_left, a_top, b_left, a_bottom);
625 }
626 }
627
628 return *this;
629 }
630
631 [[nodiscard]] constexpr TRect CutoutOrEmpty(const TRect& o) const {
632 return Cutout(o).value_or(TRect());
633 }
634
635 /// @brief Returns a new rectangle translated by the given offset.
636 [[nodiscard]] constexpr TRect<T> Shift(T dx, T dy) const {
637 return {
638 saturated::Add(left_, dx), //
639 saturated::Add(top_, dy), //
640 saturated::Add(right_, dx), //
641 saturated::Add(bottom_, dy), //
642 };
643 }
644
645 /// @brief Returns a new rectangle translated by the given offset.
646 [[nodiscard]] constexpr TRect<T> Shift(TPoint<T> offset) const {
647 return Shift(offset.x, offset.y);
648 }
649
650 /// @brief Returns a rectangle with expanded edges. Negative expansion
651 /// results in shrinking.
652 [[nodiscard]] constexpr TRect<T> Expand(T left,
653 T top,
654 T right,
655 T bottom) const {
656 return {
657 saturated::Sub(left_, left), //
658 saturated::Sub(top_, top), //
659 saturated::Add(right_, right), //
660 saturated::Add(bottom_, bottom), //
661 };
662 }
663
664 /// @brief Returns a rectangle with expanded edges in all directions.
665 /// Negative expansion results in shrinking.
666 [[nodiscard]] constexpr TRect<T> Expand(T amount) const {
667 return {
668 saturated::Sub(left_, amount), //
669 saturated::Sub(top_, amount), //
670 saturated::Add(right_, amount), //
671 saturated::Add(bottom_, amount), //
672 };
673 }
674
675 /// @brief Returns a rectangle with expanded edges in all directions.
676 /// Negative expansion results in shrinking.
677 [[nodiscard]] constexpr TRect<T> Expand(T horizontal_amount,
678 T vertical_amount) const {
679 return {
680 saturated::Sub(left_, horizontal_amount), //
681 saturated::Sub(top_, vertical_amount), //
682 saturated::Add(right_, horizontal_amount), //
683 saturated::Add(bottom_, vertical_amount), //
684 };
685 }
686
687 /// @brief Returns a rectangle with expanded edges in all directions.
688 /// Negative expansion results in shrinking.
689 [[nodiscard]] constexpr TRect<T> Expand(TPoint<T> amount) const {
690 return Expand(amount.x, amount.y);
691 }
692
693 /// @brief Returns a rectangle with expanded edges in all directions.
694 /// Negative expansion results in shrinking.
695 [[nodiscard]] constexpr TRect<T> Expand(TSize<T> amount) const {
696 return Expand(amount.width, amount.height);
697 }
698
699 /// @brief Returns a new rectangle that represents the projection of the
700 /// source rectangle onto this rectangle. In other words, the source
701 /// rectangle is redefined in terms of the coordinate space of this
702 /// rectangle.
703 [[nodiscard]] constexpr TRect<T> Project(TRect<T> source) const {
704 if (IsEmpty()) {
705 return {};
706 }
707 return source.Shift(-left_, -top_)
708 .Scale(1.0 / static_cast<Scalar>(GetWidth()),
709 1.0 / static_cast<Scalar>(GetHeight()));
710 }
711
712 ONLY_ON_FLOAT_M([[nodiscard]] constexpr static, TRect)
713 RoundOut(const TRect<U>& r) {
714 return TRect::MakeLTRB(saturated::Cast<U, Type>(floor(r.GetLeft())),
715 saturated::Cast<U, Type>(floor(r.GetTop())),
716 saturated::Cast<U, Type>(ceil(r.GetRight())),
717 saturated::Cast<U, Type>(ceil(r.GetBottom())));
718 }
719
720 ONLY_ON_FLOAT_M([[nodiscard]] constexpr static, TRect)
721 RoundIn(const TRect<U>& r) {
722 return TRect::MakeLTRB(saturated::Cast<U, Type>(ceil(r.GetLeft())),
723 saturated::Cast<U, Type>(ceil(r.GetTop())),
724 saturated::Cast<U, Type>(floor(r.GetRight())),
725 saturated::Cast<U, Type>(floor(r.GetBottom())));
726 }
727
728 ONLY_ON_FLOAT_M([[nodiscard]] constexpr static, TRect)
729 Round(const TRect<U>& r) {
730 return TRect::MakeLTRB(saturated::Cast<U, Type>(round(r.GetLeft())),
731 saturated::Cast<U, Type>(round(r.GetTop())),
732 saturated::Cast<U, Type>(round(r.GetRight())),
733 saturated::Cast<U, Type>(round(r.GetBottom())));
734 }
735
736 [[nodiscard]] constexpr static TRect Union(const TRect& a,
737 const std::optional<TRect> b) {
738 return b.has_value() ? a.Union(b.value()) : a;
739 }
740
741 [[nodiscard]] constexpr static TRect Union(const std::optional<TRect> a,
742 const TRect& b) {
743 return a.has_value() ? a->Union(b) : b;
744 }
745
746 [[nodiscard]] constexpr static std::optional<TRect> Union(
747 const std::optional<TRect> a,
748 const std::optional<TRect> b) {
749 return a.has_value() ? Union(a.value(), b) : b;
750 }
751
752 [[nodiscard]] constexpr static std::optional<TRect> Intersection(
753 const TRect& a,
754 const std::optional<TRect> b) {
755 return b.has_value() ? a.Intersection(b.value()) : a;
756 }
757
758 [[nodiscard]] constexpr static std::optional<TRect> Intersection(
759 const std::optional<TRect> a,
760 const TRect& b) {
761 return a.has_value() ? a->Intersection(b) : b;
762 }
763
764 [[nodiscard]] constexpr static std::optional<TRect> Intersection(
765 const std::optional<TRect> a,
766 const std::optional<TRect> b) {
767 return a.has_value() ? Intersection(a.value(), b) : b;
768 }
769
770 private:
771 constexpr TRect(Type left, Type top, Type right, Type bottom)
772 : left_(left), top_(top), right_(right), bottom_(bottom) {}
773
774 Type left_;
775 Type top_;
776 Type right_;
777 Type bottom_;
778
779 static constexpr Scalar kMinimumHomogenous = 1.0f / (1 << 14);
780
781 // Clip p against the near clipping plane (W = kMinimumHomogenous)
782 // and interpolate a crossing point against the nearby neighbors
783 // left and right if p is clipped and either of them is not.
784 // This method can produce 0, 1, or 2 points per call depending on
785 // how many of the points are clipped.
786 // 0 - all points are clipped
787 // 1 - p is unclipped OR
788 // p is clipped and exactly one of the neighbors is not
789 // 2 - p is clipped and both neighbors are not
790 static constexpr int ClipAndInsert(Point clipped[],
791 int index,
792 const Vector3& left,
793 const Vector3& p,
794 const Vector3& right) {
795 if (p.z >= kMinimumHomogenous) {
796 clipped[index++] = {p.x / p.z, p.y / p.z};
797 } else {
798 index = InterpolateAndInsert(clipped, index, p, left);
799 index = InterpolateAndInsert(clipped, index, p, right);
800 }
801 return index;
802 }
803
804 // Interpolate (a clipped) point p against one of its neighbors
805 // and insert the point into the array where the line between them
806 // veers from clipped space to unclipped, if such a point exists.
807 static constexpr int InterpolateAndInsert(Point clipped[],
808 int index,
809 const Vector3& p,
810 const Vector3& neighbor) {
811 if (neighbor.z >= kMinimumHomogenous) {
812 auto t = (kMinimumHomogenous - p.z) / (neighbor.z - p.z);
813 clipped[index++] = {
814 ((1.0f - t) * p.x + t * neighbor.x) / kMinimumHomogenous,
815 ((1.0f - t) * p.y + t * neighbor.y) / kMinimumHomogenous,
816 };
817 }
818 return index;
819 }
820};
821
826
827#undef ONLY_ON_FLOAT
828#undef ONLY_ON_FLOAT_M
829
830} // namespace impeller
831
832namespace std {
833
834template <class T>
835inline std::ostream& operator<<(std::ostream& out,
836 const impeller::TRect<T>& r) {
837 out << "(" << r.GetLeftTop() << " => " << r.GetRightBottom() << ")";
838 return out;
839}
840
841} // namespace std
842
843#endif // FLUTTER_IMPELLER_GEOMETRY_RECT_H_
int32_t value
int32_t x
#define FML_UNREACHABLE()
Definition logging.h:128
#define ONLY_ON_FLOAT_M(Modifiers, Return)
Definition point.h:22
double y
float Scalar
Definition scalar.h:19
TPoint< Scalar > Point
Definition point.h:426
TRect< int64_t > IRect64
Definition rect.h:824
Definition ref_ptr.h:261
std::ostream & operator<<(std::ostream &out, const impeller::Arc &a)
Definition arc.h:141
int32_t height
int32_t width
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
constexpr auto GetBottom() const
Definition rect.h:391
constexpr Type GetY() const
Returns the Y coordinate of the upper left corner, equivalent to |GetOrigin().y|.
Definition rect.h:371
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition rect.h:506
constexpr bool ContainsInclusive(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the closed-range interior of this rectangle.
Definition rect.h:274
static constexpr TRect MakeEllipseBounds(const TPoint< Type > &center, const TSize< Type > &radii)
Definition rect.h:164
static constexpr TRect MakeWH(Type width, Type height)
Definition rect.h:140
static constexpr TRect Union(const TRect &a, const std::optional< TRect > b)
Definition rect.h:736
constexpr auto GetTop() const
Definition rect.h:387
constexpr std::array< TPoint< T >, 4 > GetPoints() const
Get the points that represent the 4 corners of this rectangle in a Z order that is compatible with tr...
Definition rect.h:448
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition rect.h:562
constexpr TRect< T > Project(TRect< T > source) const
Returns a new rectangle that represents the projection of the source rectangle onto this rectangle....
Definition rect.h:703
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition rect.h:361
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition rect.h:381
static constexpr std::optional< TRect > Union(const std::optional< TRect > a, const std::optional< TRect > b)
Definition rect.h:746
constexpr std::optional< TRect< T > > Cutout(const TRect &o) const
Returns the new boundary rectangle that would result from this rectangle being cut out by the specifi...
Definition rect.h:591
constexpr TRect Scale(TPoint< T > scale) const
Definition rect.h:240
constexpr bool IsMaximum() const
Definition rect.h:348
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition rect.h:331
constexpr T Area() const
Get the area of the rectangle, equivalent to |GetSize().Area()|.
Definition rect.h:410
constexpr bool Contains(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the half-open interior of this rectangle.
Definition rect.h:255
static constexpr std::optional< TRect > MakePointBounds(const PointIter first, const PointIter last)
Definition rect.h:194
constexpr TRect Union(const TRect &o) const
Definition rect.h:547
constexpr TRect Scale(Type scale_x, Type scale_y) const
Definition rect.h:233
static constexpr std::enable_if_t< std::is_floating_point_v< FT >, TRect > Make(const TRect< U > &rect)
Definition rect.h:181
constexpr bool IntersectsWithRect(const TRect &o) const
Definition rect.h:580
constexpr auto GetLeft() const
Definition rect.h:385
RoundIn(const TRect< U > &r)
Definition rect.h:721
constexpr TRect CutoutOrEmpty(const TRect &o) const
Definition rect.h:631
constexpr TRect< T > Expand(T horizontal_amount, T vertical_amount) const
Returns a rectangle with expanded edges in all directions. Negative expansion results in shrinking.
Definition rect.h:677
constexpr TPoint< T > GetLeftTop() const
Definition rect.h:393
Round(const TRect< U > &r)
Definition rect.h:729
RoundOut(const TRect< U > &r)
Definition rect.h:713
constexpr TRect GetPositive() const
Get a version of this rectangle that has a non-negative size.
Definition rect.h:432
static constexpr std::optional< TRect > Intersection(const TRect &a, const std::optional< TRect > b)
Definition rect.h:752
constexpr TRect Scale(TSize< T > scale) const
Definition rect.h:244
constexpr Type GetX() const
Returns the X coordinate of the upper left corner, equivalent to |GetOrigin().x|.
Definition rect.h:367
constexpr std::array< T, 4 > GetLTRB() const
Definition rect.h:421
static constexpr TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition rect.h:144
constexpr auto GetRight() const
Definition rect.h:389
constexpr bool IsSquare() const
Returns true if width and height are equal and neither is NaN.
Definition rect.h:338
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
constexpr bool Contains(const TRect &o) const
Returns true iff this rectangle is not empty and it also contains every point considered inside the p...
Definition rect.h:297
IsFinite() const
Returns true if all of the fields of this floating point rectangle are finite.
Definition rect.h:322
constexpr Matrix GetNormalizingTransform() const
Constructs a Matrix that will map all points in the coordinate space of the rectangle into a new norm...
Definition rect.h:525
static constexpr TRect MakeCircleBounds(const TPoint< Type > &center, Type radius)
Definition rect.h:156
constexpr TRect Scale(Type scale) const
Definition rect.h:226
constexpr std::array< TPoint< T >, 4 > GetTransformedPoints(const Matrix &transform) const
Definition rect.h:460
static constexpr TRect MakeEllipseBounds(const TPoint< Type > &center, const TPoint< Type > &radii)
Definition rect.h:172
constexpr TRect TransformAndClipBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle, clipped against the near clippin...
Definition rect.h:472
constexpr TPoint< T > GetRightBottom() const
Definition rect.h:405
static constexpr std::optional< TRect > Intersection(const std::optional< TRect > a, const std::optional< TRect > b)
Definition rect.h:764
constexpr TPoint< T > GetLeftBottom() const
Definition rect.h:401
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:150
constexpr TRect< T > Shift(TPoint< T > offset) const
Returns a new rectangle translated by the given offset.
Definition rect.h:646
constexpr std::enable_if_t< std::is_floating_point_v< FT >, bool > Contains(const TRect< U > &o) const
Definition rect.h:307
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition rect.h:375
static constexpr std::optional< TRect > Intersection(const std::optional< TRect > a, const TRect &b)
Definition rect.h:758
constexpr TPoint< T > GetRightTop() const
Definition rect.h:397
static constexpr std::optional< TRect > MakePointBounds(const U &value)
Definition rect.h:189
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition rect.h:652
static constexpr TRect MakeMaximum()
Definition rect.h:212
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition rect.h:416
constexpr TRect IntersectionOrEmpty(const TRect &o) const
Definition rect.h:576
constexpr TRect()
Definition rect.h:127
constexpr TRect< T > Expand(TSize< T > amount) const
Returns a rectangle with expanded edges in all directions. Negative expansion results in shrinking.
Definition rect.h:695
constexpr bool operator==(const TRect &r) const
Definition rect.h:219
constexpr TRect< T > Expand(T amount) const
Returns a rectangle with expanded edges in all directions. Negative expansion results in shrinking.
Definition rect.h:666
constexpr std::array< T, 4 > GetXYWH() const
Get the x, y coordinates of the origin and the width and height of the rectangle in an array.
Definition rect.h:427
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition rect.h:636
constexpr TRect< T > Expand(TPoint< T > amount) const
Returns a rectangle with expanded edges in all directions. Negative expansion results in shrinking.
Definition rect.h:689
constexpr TPoint< Type > GetOrigin() const
Returns the upper left corner of the rectangle as specified by the left/top or x/y values when it was...
Definition rect.h:354
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
static constexpr TRect Union(const std::optional< TRect > a, const TRect &b)
Definition rect.h:741
Type height
Definition size.h:29
Type width
Definition size.h:28
std::vector< Point > points