| Flutter Impeller |

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"
14 #include "impeller/geometry/matrix.h"
15 #include "impeller/geometry/point.h"
16 #include "impeller/geometry/saturated_math.h"
17 #include "impeller/geometry/scalar.h"
18 #include "impeller/geometry/size.h"
19
20 namespace 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.
121 template <class T>
122 struct 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 template <typename U>
155 constexpr static std::optional<TRect> MakePointBounds(const U& value) {
156 return MakePointBounds(value.begin(), value.end());
157 }
158
159 template <typename PointIter>
160 constexpr static std::optional<TRect> MakePointBounds(const PointIter first,
161 const PointIter last) {
162 if (first == last) {
163 return std::nullopt;
164 }
165 auto left = first->x;
166 auto top = first->y;
167 auto right = first->x;
168 auto bottom = first->y;
169 for (auto it = first + 1; it < last; ++it) {
170 left = std::min(left, it->x);
171 top = std::min(top, it->y);
172 right = std::max(right, it->x);
173 bottom = std::max(bottom, it->y);
174 }
175 return TRect::MakeLTRB(left, top, right, bottom);
176 }
177
178 [[nodiscard]] constexpr static TRect MakeMaximum() {
179 return TRect::MakeLTRB(std::numeric_limits<Type>::lowest(),
180 std::numeric_limits<Type>::lowest(),
181 std::numeric_limits<Type>::max(),
182 std::numeric_limits<Type>::max());
183 }
184
185 [[nodiscard]] constexpr bool operator==(const TRect& r) const {
186 return left_ == r.left_ && //
187 top_ == r.top_ && //
188 right_ == r.right_ && //
189 bottom_ == r.bottom_;
190 }
191
192 [[nodiscard]] constexpr bool operator!=(const TRect& r) const {
193 return !(*this == r);
194 }
195
196 [[nodiscard]] constexpr TRect Scale(Type scale) const {
197 return TRect(left_ * scale, //
198 top_ * scale, //
199 right_ * scale, //
200 bottom_ * scale);
201 }
202
203 [[nodiscard]] constexpr TRect Scale(Type scale_x, Type scale_y) const {
204 return TRect(left_ * scale_x, //
205 top_ * scale_y, //
206 right_ * scale_x, //
207 bottom_ * scale_y);
208 }
209
210 [[nodiscard]] constexpr TRect Scale(TPoint<T> scale) const {
211 return Scale(scale.x, scale.y);
212 }
213
214 [[nodiscard]] constexpr TRect Scale(TSize<T> scale) const {
215 return Scale(scale.width, scale.height);
216 }
217
218 /// @brief Returns true iff the provided point |p| is inside the
219 /// half-open interior of this rectangle.
220 ///
221 /// For purposes of containment, a rectangle contains points
222 /// along the top and left edges but not points along the
223 /// right and bottom edges so that a point is only ever
224 /// considered inside one of two abutting rectangles.
225 [[nodiscard]] constexpr bool Contains(const TPoint<Type>& p) const {
226 return !this->IsEmpty() && //
227 p.x >= left_ && //
228 p.y >= top_ && //
229 p.x < right_ && //
230 p.y < bottom_;
231 }
232
233 /// @brief Returns true iff the provided point |p| is inside the
234 /// closed-range interior of this rectangle.
235 ///
236 /// Unlike the regular |Contains(TPoint)| method, this method
237 /// considers all points along the boundary of the rectangle
238 /// to be contained within the rectangle - useful for testing
239 /// if vertices that define a filled shape would carry the
240 /// interior of that shape outside the bounds of the rectangle.
241 /// Since both geometries are defining half-open spaces, their
242 /// defining geometry needs to consider their boundaries to
243 /// be equivalent with respect to interior and exterior.
244 [[nodiscard]] constexpr bool ContainsInclusive(const TPoint<Type>& p) const {
245 return !this->IsEmpty() && //
246 p.x >= left_ && //
247 p.y >= top_ && //
248 p.x <= right_ && //
249 p.y <= bottom_;
250 }
251
252 /// @brief Returns true iff this rectangle is not empty and it also
253 /// contains every point considered inside the provided
254 /// rectangle |o| (as determined by |Contains(TPoint)|).
255 ///
256 /// This is similar to a definition where the result is true iff
257 /// the union of the two rectangles is equal to this rectangle,
258 /// ignoring precision issues with performing those operations
259 /// and assuming that empty rectangles are never equal.
260 ///
261 /// An empty rectangle can contain no other rectangle.
262 ///
263 /// An empty rectangle is, however, contained within any
264 /// other non-empy rectangle as the set of points it contains
265 /// is an empty set and so there are no points to fail the
266 /// containment criteria.
267 [[nodiscard]] constexpr bool Contains(const TRect& o) const {
268 return !this->IsEmpty() && //
269 (o.IsEmpty() || (o.left_ >= left_ && //
270 o.top_ >= top_ && //
271 o.right_ <= right_ && //
272 o.bottom_ <= bottom_));
273 }
274
275 /// @brief Returns true if all of the fields of this floating point
276 /// rectangle are finite.
277 ///
278 /// Note that the results of |GetWidth()| and |GetHeight()| may
279 /// still be infinite due to overflow even if the fields themselves
280 /// are finite.
281 ONLY_ON_FLOAT_M([[nodiscard]] constexpr, bool)
282 IsFinite() const {
283 return std::isfinite(left_) && //
284 std::isfinite(top_) && //
285 std::isfinite(right_) && //
286 std::isfinite(bottom_);
287 }
288
289 /// @brief Returns true if either of the width or height are 0, negative,
290 /// or NaN.
291 [[nodiscard]] constexpr bool IsEmpty() const {
292 // Computing the non-empty condition and negating the result causes any
293 // NaN value to return true - i.e. is considered empty.
294 return !(left_ < right_ && top_ < bottom_);
295 }
296
297 /// @brief Returns true if width and height are equal and neither is NaN.
298 [[nodiscard]] constexpr bool IsSquare() const {
299 // empty rectangles can technically be "square", but would be
300 // misleading to most callers. Using |IsEmpty| also prevents
301 // "non-empty and non-overflowing" computations from happening
302 // to be equal to "empty and overflowing" results.
303 // (Consider LTRB(10, 15, MAX-2, MIN+2) which is empty, but both
304 // w/h subtractions equal "5").
305 return !IsEmpty() && (right_ - left_) == (bottom_ - top_);
306 }
307
308 [[nodiscard]] constexpr bool IsMaximum() const {
309 return *this == MakeMaximum();
310 }
311
312 /// @brief Returns the upper left corner of the rectangle as specified
313 /// by the left/top or x/y values when it was constructed.
314 [[nodiscard]] constexpr TPoint<Type> GetOrigin() const {
315 return {left_, top_};
316 }
317
318 /// @brief Returns the size of the rectangle which may be negative in
319 /// either width or height and may have been clipped to the
320 /// maximum integer values for integer rects whose size overflows.
321 [[nodiscard]] constexpr TSize<Type> GetSize() const {
322 return {GetWidth(), GetHeight()};
323 }
324
325 /// @brief Returns the X coordinate of the upper left corner, equivalent
326 /// to |GetOrigin().x|
327 [[nodiscard]] constexpr Type GetX() const { return left_; }
328
329 /// @brief Returns the Y coordinate of the upper left corner, equivalent
330 /// to |GetOrigin().y|
331 [[nodiscard]] constexpr Type GetY() const { return top_; }
332
333 /// @brief Returns the width of the rectangle, equivalent to
334 /// |GetSize().width|
335 [[nodiscard]] constexpr Type GetWidth() const {
336 return saturated::Sub(right_, left_);
337 }
338
339 /// @brief Returns the height of the rectangle, equivalent to
340 /// |GetSize().height|
341 [[nodiscard]] constexpr Type GetHeight() const {
342 return saturated::Sub(bottom_, top_);
343 }
344
345 [[nodiscard]] constexpr auto GetLeft() const { return left_; }
346
347 [[nodiscard]] constexpr auto GetTop() const { return top_; }
348
349 [[nodiscard]] constexpr auto GetRight() const { return right_; }
350
351 [[nodiscard]] constexpr auto GetBottom() const { return bottom_; }
352
353 [[nodiscard]] constexpr TPoint<T> GetLeftTop() const { //
354 return {left_, top_};
355 }
356
357 [[nodiscard]] constexpr TPoint<T> GetRightTop() const {
358 return {right_, top_};
359 }
360
361 [[nodiscard]] constexpr TPoint<T> GetLeftBottom() const {
362 return {left_, bottom_};
363 }
364
365 [[nodiscard]] constexpr TPoint<T> GetRightBottom() const {
366 return {right_, bottom_};
367 }
368
369 /// @brief Get the area of the rectangle, equivalent to |GetSize().Area()|
370 [[nodiscard]] constexpr T Area() const {
371 // TODO(141710): Use saturated math to avoid overflow.
372 return IsEmpty() ? 0 : (right_ - left_) * (bottom_ - top_);
373 }
374
375 /// @brief Get the center point as a |Point|.
376 [[nodiscard]] constexpr Point GetCenter() const {
377 return {saturated::AverageScalar(left_, right_),
378 saturated::AverageScalar(top_, bottom_)};
379 }
380
381 [[nodiscard]] constexpr std::array<T, 4> GetLTRB() const {
382 return {left_, top_, right_, bottom_};
383 }
384
385 /// @brief Get the x, y coordinates of the origin and the width and
386 /// height of the rectangle in an array.
387 [[nodiscard]] constexpr std::array<T, 4> GetXYWH() const {
388 return {left_, top_, GetWidth(), GetHeight()};
389 }
390
391 /// @brief Get a version of this rectangle that has a non-negative size.
392 [[nodiscard]] constexpr TRect GetPositive() const {
393 if (!IsEmpty()) {
394 return *this;
395 }
396 return {
397 std::min(left_, right_),
398 std::min(top_, bottom_),
399 std::max(left_, right_),
400 std::max(top_, bottom_),
401 };
402 }
403
404 /// @brief Get the points that represent the 4 corners of this rectangle
405 /// in a Z order that is compatible with triangle strips or a set
406 /// of all zero points if the rectangle is empty.
407 /// The order is: Top left, top right, bottom left, bottom right.
408 [[nodiscard]] constexpr std::array<TPoint<T>, 4> GetPoints() const {
409 if (IsEmpty()) {
410 return {};
411 }
412 return {
413 TPoint{left_, top_},
414 TPoint{right_, top_},
415 TPoint{left_, bottom_},
416 TPoint{right_, bottom_},
417 };
418 }
419
420 [[nodiscard]] constexpr std::array<TPoint<T>, 4> GetTransformedPoints(
421 const Matrix& transform) const {
422 auto points = GetPoints();
423 for (size_t i = 0; i < points.size(); i++) {
424 points[i] = transform * points[i];
425 }
426 return points;
427 }
428
429 /// @brief Creates a new bounding box that contains this transformed
430 /// rectangle, clipped against the near clipping plane if
431 /// necessary.
432 [[nodiscard]] constexpr TRect TransformAndClipBounds(
433 const Matrix& transform) const {
434 if (!transform.HasPerspective2D()) {
435 return TransformBounds(transform);
436 }
437
438 if (IsEmpty()) {
439 return {};
440 }
441
442 auto ul = transform.TransformHomogenous({left_, top_});
443 auto ur = transform.TransformHomogenous({right_, top_});
444 auto ll = transform.TransformHomogenous({left_, bottom_});
445 auto lr = transform.TransformHomogenous({right_, bottom_});
446
447 // It can probably be proven that we only ever have 5 points at most
448 // which happens when only 1 corner is clipped and we get 2 points
449 // in return for it as we interpolate against its neighbors.
450 Point points[8];
451 int index = 0;
452
453 // Process (clip and interpolate) each point against its 2 neighbors:
454 // left, pt, right
455 index = ClipAndInsert(points, index, ll, ul, ur);
456 index = ClipAndInsert(points, index, ul, ur, lr);
457 index = ClipAndInsert(points, index, ur, lr, ll);
458 index = ClipAndInsert(points, index, lr, ll, ul);
459
460 auto bounds = TRect::MakePointBounds(points, points + index);
461 return bounds.value_or(TRect{});
462 }
463
464 /// @brief Creates a new bounding box that contains this transformed
465 /// rectangle.
466 [[nodiscard]] constexpr TRect TransformBounds(const Matrix& transform) const {
467 if (IsEmpty()) {
468 return {};
469 }
470 auto points = GetTransformedPoints(transform);
471 auto bounds = TRect::MakePointBounds(points.begin(), points.end());
472 if (bounds.has_value()) {
473 return bounds.value();
474 }
475 FML_UNREACHABLE();
476 }
477
478 /// @brief Constructs a Matrix that will map all points in the coordinate
479 /// space of the rectangle into a new normalized coordinate space
480 /// where the upper left corner of the rectangle maps to (0, 0)
481 /// and the lower right corner of the rectangle maps to (1, 1).
482 ///
483 /// Empty and non-finite rectangles will return a zero-scaling
484 /// transform that maps all points to (0, 0).
485 [[nodiscard]] constexpr Matrix GetNormalizingTransform() const {
486 if (!IsEmpty()) {
487 Scalar sx = 1.0 / GetWidth();
488 Scalar sy = 1.0 / GetHeight();
489 Scalar tx = left_ * -sx;
490 Scalar ty = top_ * -sy;
491
492 // Exclude NaN and infinities and either scale underflowing to zero
493 if (sx != 0.0 && sy != 0.0 && 0.0 * sx * sy * tx * ty == 0.0) {
494 // clang-format off
495 return Matrix( sx, 0.0f, 0.0f, 0.0f,
496 0.0f, sy, 0.0f, 0.0f,
497 0.0f, 0.0f, 1.0f, 0.0f,
498 tx, ty, 0.0f, 1.0f);
499 // clang-format on
500 }
501 }
502
503 // Map all coordinates to the origin.
504 return Matrix::MakeScale({0.0f, 0.0f, 1.0f});
505 }
506
507 [[nodiscard]] constexpr TRect Union(const TRect& o) const {
508 if (IsEmpty()) {
509 return o;
510 }
511 if (o.IsEmpty()) {
512 return *this;
513 }
514 return {
515 std::min(left_, o.left_),
516 std::min(top_, o.top_),
517 std::max(right_, o.right_),
518 std::max(bottom_, o.bottom_),
519 };
520 }
521
522 [[nodiscard]] constexpr std::optional<TRect> Intersection(
523 const TRect& o) const {
524 if (IntersectsWithRect(o)) {
525 return TRect{
526 std::max(left_, o.left_),
527 std::max(top_, o.top_),
528 std::min(right_, o.right_),
529 std::min(bottom_, o.bottom_),
530 };
531 } else {
532 return std::nullopt;
533 }
534 }
535
536 [[nodiscard]] constexpr TRect IntersectionOrEmpty(const TRect& o) const {
537 return Intersection(o).value_or(TRect());
538 }
539
540 [[nodiscard]] constexpr bool IntersectsWithRect(const TRect& o) const {
541 return !IsEmpty() && //
542 !o.IsEmpty() && //
543 left_ < o.right_ && //
544 top_ < o.bottom_ && //
545 right_ > o.left_ && //
546 bottom_ > o.top_;
547 }
548
549 /// @brief Returns the new boundary rectangle that would result from this
550 /// rectangle being cut out by the specified rectangle.
551 [[nodiscard]] constexpr std::optional<TRect<T>> Cutout(const TRect& o) const {
552 if (IsEmpty()) {
553 // This test isn't just a short-circuit, it also prevents the concise
554 // math below from returning the wrong answer on empty rects.
555 // Once we know that this rectangle is not empty, the math below can
556 // only succeed in computing a value if o is also non-empty and non-nan.
557 // Otherwise, the method returns *this by default.
558 return std::nullopt;
559 }
560
561 const auto& [a_left, a_top, a_right, a_bottom] = GetLTRB(); // Source rect.
562 const auto& [b_left, b_top, b_right, b_bottom] = o.GetLTRB(); // Cutout.
563 if (b_left <= a_left && b_right >= a_right) {
564 if (b_top <= a_top && b_bottom >= a_bottom) {
565 // Full cutout.
566 return std::nullopt;
567 }
568 if (b_top <= a_top && b_bottom > a_top) {
569 // Cuts off the top.
570 return TRect::MakeLTRB(a_left, b_bottom, a_right, a_bottom);
571 }
572 if (b_bottom >= a_bottom && b_top < a_bottom) {
573 // Cuts off the bottom.
574 return TRect::MakeLTRB(a_left, a_top, a_right, b_top);
575 }
576 }
577 if (b_top <= a_top && b_bottom >= a_bottom) {
578 if (b_left <= a_left && b_right > a_left) {
579 // Cuts off the left.
580 return TRect::MakeLTRB(b_right, a_top, a_right, a_bottom);
581 }
582 if (b_right >= a_right && b_left < a_right) {
583 // Cuts off the right.
584 return TRect::MakeLTRB(a_left, a_top, b_left, a_bottom);
585 }
586 }
587
588 return *this;
589 }
590
591 [[nodiscard]] constexpr TRect CutoutOrEmpty(const TRect& o) const {
592 return Cutout(o).value_or(TRect());
593 }
594
595 /// @brief Returns a new rectangle translated by the given offset.
596 [[nodiscard]] constexpr TRect<T> Shift(T dx, T dy) const {
597 return {
598 saturated::Add(left_, dx), //
599 saturated::Add(top_, dy), //
600 saturated::Add(right_, dx), //
601 saturated::Add(bottom_, dy), //
602 };
603 }
604
605 /// @brief Returns a new rectangle translated by the given offset.
606 [[nodiscard]] constexpr TRect<T> Shift(TPoint<T> offset) const {
607 return Shift(offset.x, offset.y);
608 }
609
610 /// @brief Returns a rectangle with expanded edges. Negative expansion
611 /// results in shrinking.
612 [[nodiscard]] constexpr TRect<T> Expand(T left,
613 T top,
614 T right,
615 T bottom) const {
616 return {
617 saturated::Sub(left_, left), //
618 saturated::Sub(top_, top), //
619 saturated::Add(right_, right), //
620 saturated::Add(bottom_, bottom), //
621 };
622 }
623
624 /// @brief Returns a rectangle with expanded edges in all directions.
625 /// Negative expansion results in shrinking.
626 [[nodiscard]] constexpr TRect<T> Expand(T amount) const {
627 return {
628 saturated::Sub(left_, amount), //
629 saturated::Sub(top_, amount), //
630 saturated::Add(right_, amount), //
631 saturated::Add(bottom_, amount), //
632 };
633 }
634
635 /// @brief Returns a rectangle with expanded edges in all directions.
636 /// Negative expansion results in shrinking.
637 [[nodiscard]] constexpr TRect<T> Expand(T horizontal_amount,
638 T vertical_amount) const {
639 return {
640 saturated::Sub(left_, horizontal_amount), //
641 saturated::Sub(top_, vertical_amount), //
642 saturated::Add(right_, horizontal_amount), //
643 saturated::Add(bottom_, vertical_amount), //
644 };
645 }
646
647 /// @brief Returns a rectangle with expanded edges in all directions.
648 /// Negative expansion results in shrinking.
649 [[nodiscard]] constexpr TRect<T> Expand(TPoint<T> amount) const {
650 return Expand(amount.x, amount.y);
651 }
652
653 /// @brief Returns a rectangle with expanded edges in all directions.
654 /// Negative expansion results in shrinking.
655 [[nodiscard]] constexpr TRect<T> Expand(TSize<T> amount) const {
656 return Expand(amount.width, amount.height);
657 }
658
659 /// @brief Returns a new rectangle that represents the projection of the
660 /// source rectangle onto this rectangle. In other words, the source
661 /// rectangle is redefined in terms of the coordinate space of this
662 /// rectangle.
663 [[nodiscard]] constexpr TRect<T> Project(TRect<T> source) const {
664 if (IsEmpty()) {
665 return {};
666 }
667 return source.Shift(-left_, -top_)
668 .Scale(1.0 / static_cast<Scalar>(GetWidth()),
669 1.0 / static_cast<Scalar>(GetHeight()));
670 }
671
672 ONLY_ON_FLOAT_M([[nodiscard]] constexpr static, TRect)
673 RoundOut(const TRect<U>& r) {
674 return TRect::MakeLTRB(saturated::Cast<U, Type>(floor(r.GetLeft())),
675 saturated::Cast<U, Type>(floor(r.GetTop())),
676 saturated::Cast<U, Type>(ceil(r.GetRight())),
677 saturated::Cast<U, Type>(ceil(r.GetBottom())));
678 }
679
680 ONLY_ON_FLOAT_M([[nodiscard]] constexpr static, TRect)
681 Round(const TRect<U>& r) {
682 return TRect::MakeLTRB(saturated::Cast<U, Type>(round(r.GetLeft())),
683 saturated::Cast<U, Type>(round(r.GetTop())),
684 saturated::Cast<U, Type>(round(r.GetRight())),
685 saturated::Cast<U, Type>(round(r.GetBottom())));
686 }
687
688 [[nodiscard]] constexpr static std::optional<TRect> Union(
689 const TRect& a,
690 const std::optional<TRect> b) {
691 return b.has_value() ? a.Union(b.value()) : a;
692 }
693
694 [[nodiscard]] constexpr static std::optional<TRect> Union(
695 const std::optional<TRect> a,
696 const TRect& b) {
697 return a.has_value() ? a->Union(b) : b;
698 }
699
700 [[nodiscard]] constexpr static std::optional<TRect> Union(
701 const std::optional<TRect> a,
702 const std::optional<TRect> b) {
703 return a.has_value() ? Union(a.value(), b) : b;
704 }
705
706 [[nodiscard]] constexpr static std::optional<TRect> Intersection(
707 const TRect& a,
708 const std::optional<TRect> b) {
709 return b.has_value() ? a.Intersection(b.value()) : a;
710 }
711
712 [[nodiscard]] constexpr static std::optional<TRect> Intersection(
713 const std::optional<TRect> a,
714 const TRect& b) {
715 return a.has_value() ? a->Intersection(b) : b;
716 }
717
718 [[nodiscard]] constexpr static std::optional<TRect> Intersection(
719 const std::optional<TRect> a,
720 const std::optional<TRect> b) {
721 return a.has_value() ? Intersection(a.value(), b) : b;
722 }
723
724 private:
725 constexpr TRect(Type left, Type top, Type right, Type bottom)
726 : left_(left), top_(top), right_(right), bottom_(bottom) {}
727
728 Type left_;
729 Type top_;
730 Type right_;
731 Type bottom_;
732
733 static constexpr Scalar kMinimumHomogenous = 1.0f / (1 << 14);
734
735 // Clip p against the near clipping plane (W = kMinimumHomogenous)
736 // and interpolate a crossing point against the nearby neighbors
737 // left and right if p is clipped and either of them is not.
738 // This method can produce 0, 1, or 2 points per call depending on
739 // how many of the points are clipped.
740 // 0 - all points are clipped
741 // 1 - p is unclipped OR
742 // p is clipped and exactly one of the neighbors is not
743 // 2 - p is clipped and both neighbors are not
744 static constexpr int ClipAndInsert(Point clipped[],
745 int index,
746 const Vector3& left,
747 const Vector3& p,
748 const Vector3& right) {
749 if (p.z >= kMinimumHomogenous) {
750 clipped[index++] = {p.x / p.z, p.y / p.z};
751 } else {
752 index = InterpolateAndInsert(clipped, index, p, left);
753 index = InterpolateAndInsert(clipped, index, p, right);
754 }
755 return index;
756 }
757
758 // Interpolate (a clipped) point p against one of its neighbors
759 // and insert the point into the array where the line between them
760 // veers from clipped space to unclipped, if such a point exists.
761 static constexpr int InterpolateAndInsert(Point clipped[],
762 int index,
763 const Vector3& p,
764 const Vector3& neighbor) {
765 if (neighbor.z >= kMinimumHomogenous) {
766 auto t = (kMinimumHomogenous - p.z) / (neighbor.z - p.z);
767 clipped[index++] = {
768 (t * p.x + (1.0f - t) * neighbor.x) / kMinimumHomogenous,
769 (t * p.y + (1.0f - t) * neighbor.y) / kMinimumHomogenous,
770 };
771 }
772 return index;
773 }
774 };
775
776 using Rect = TRect<Scalar>;
777 using IRect32 = TRect<int32_t>;
778 using IRect64 = TRect<int64_t>;
779 using IRect = IRect64;
780
781 #undef ONLY_ON_FLOAT
782 #undef ONLY_ON_FLOAT_M
783
784 } // namespace impeller
785
786 namespace std {
787
788 template <class T>
789 inline std::ostream& operator<<(std::ostream& out,
790 const impeller::TRect<T>& r) {
791 out << "(" << r.GetOrigin() << ", " << r.GetSize() << ")";
792 return out;
793 }
794
795 } // namespace std
796
797 #endif // FLUTTER_IMPELLER_GEOMETRY_RECT_H_
impeller::TRect::Union
constexpr static std::optional< TRect > Union(const std::optional< TRect > a, const TRect &b)
Definition: rect.h:694
impeller::TRect::GetLTRB
constexpr std::array< T, 4 > GetLTRB() const
Definition: rect.h:381
point.h
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::TRect::Expand
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:637
impeller::TRect::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::TRect::Expand
constexpr TRect< T > Expand(T amount) const
Returns a rectangle with expanded edges in all directions. Negative expansion results in shrinking.
Definition: rect.h:626
saturated_math.h
impeller::TRect::TransformBounds
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:466
impeller::TRect::IsMaximum
constexpr bool IsMaximum() const
Definition: rect.h:308
impeller::TRect::GetLeftTop
constexpr TPoint< T > GetLeftTop() const
Definition: rect.h:353
impeller::TRect::Intersection
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition: rect.h:522
impeller::TRect::Scale
constexpr TRect Scale(Type scale_x, Type scale_y) const
Definition: rect.h:203
impeller::TRect::GetNormalizingTransform
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:485
impeller::TRect::MakeWH
constexpr static TRect MakeWH(Type width, Type height)
Definition: rect.h:140
impeller::TRect::CutoutOrEmpty
constexpr TRect CutoutOrEmpty(const TRect &o) const
Definition: rect.h:591
impeller::TRect::Intersection
constexpr static std::optional< TRect > Intersection(const TRect &a, const std::optional< TRect > b)
Definition: rect.h:706
std::operator<<
std::ostream & operator<<(std::ostream &out, const impeller::Color &c)
Definition: color.h:926
impeller::TRect::Round
Round(const TRect< U > &r)
Definition: rect.h:681
impeller::TRect::TRect
constexpr TRect()
Definition: rect.h:127
impeller::TRect::GetCenter
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition: rect.h:376
impeller::TRect::GetX
constexpr Type GetX() const
Returns the X coordinate of the upper left corner, equivalent to |GetOrigin().x|.
Definition: rect.h:327
ONLY_ON_FLOAT_M
#define ONLY_ON_FLOAT_M(Modifiers, Return)
Definition: rect.h:22
impeller::TRect::GetHeight
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition: rect.h:341
impeller::TRect::GetOrigin
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:314
impeller::TRect::GetRightTop
constexpr TPoint< T > GetRightTop() const
Definition: rect.h:357
offset
SeparatedVector2 offset
Definition: stroke_path_geometry.cc:304
impeller::TRect::Contains
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:267
impeller::TRect::operator==
constexpr bool operator==(const TRect &r) const
Definition: rect.h:185
impeller::TRect::IntersectsWithRect
constexpr bool IntersectsWithRect(const TRect &o) const
Definition: rect.h:540
impeller::TRect::GetPoints
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:408
impeller::TRect::MakePointBounds
constexpr static std::optional< TRect > MakePointBounds(const U &value)
Definition: rect.h:155
impeller::TRect::Shift
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition: rect.h:596
impeller::TRect::ContainsInclusive
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:244
impeller::TRect::IsEmpty
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition: rect.h:291
impeller::TRect::RoundOut
RoundOut(const TRect< U > &r)
Definition: rect.h:673
impeller::TRect::IntersectionOrEmpty
constexpr TRect IntersectionOrEmpty(const TRect &o) const
Definition: rect.h:536
matrix.h
impeller::TRect::Scale
constexpr TRect Scale(TSize< T > scale) const
Definition: rect.h:214
impeller::TSize
Definition: size.h:19
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::TRect::TransformAndClipBounds
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:432
impeller::TRect::GetLeft
constexpr auto GetLeft() const
Definition: rect.h:345
impeller::TRect::GetTransformedPoints
constexpr std::array< TPoint< T >, 4 > GetTransformedPoints(const Matrix &transform) const
Definition: rect.h:420
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:213
impeller::TRect::GetWidth
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition: rect.h:335
impeller::TRect::GetLeftBottom
constexpr TPoint< T > GetLeftBottom() const
Definition: rect.h:361
impeller::TRect::IsSquare
constexpr bool IsSquare() const
Returns true if width and height are equal and neither is NaN.
Definition: rect.h:298
impeller::TRect::MakeOriginSize
constexpr static TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:144
impeller::TRect::Scale
constexpr TRect Scale(Type scale) const
Definition: rect.h:196
impeller::TRect::MakePointBounds
constexpr static std::optional< TRect > MakePointBounds(const PointIter first, const PointIter last)
Definition: rect.h:160
impeller::TRect::Shift
constexpr TRect< T > Shift(TPoint< T > offset) const
Returns a new rectangle translated by the given offset.
Definition: rect.h:606
impeller::TRect::Scale
constexpr TRect Scale(TPoint< T > scale) const
Definition: rect.h:210
impeller::TSize::width
Type width
Definition: size.h:22
impeller::TRect::Contains
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:225
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::TRect::IsFinite
IsFinite() const
Returns true if all of the fields of this floating point rectangle are finite.
Definition: rect.h:282
scalar.h
impeller::TRect::Expand
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:649
impeller::TRect::Expand
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:655
impeller::TRect::Cutout
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:551
impeller::TRect::GetSize
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:321
impeller::TRect::GetRight
constexpr auto GetRight() const
Definition: rect.h:349
impeller::TRect::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:150
std
Definition: comparable.h:95
impeller::TRect::Project
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:663
impeller::TPoint
Definition: point.h:27
impeller::TRect::MakeMaximum
constexpr static TRect MakeMaximum()
Definition: rect.h:178
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::IRect64
TRect< int64_t > IRect64
Definition: rect.h:778
scale
const Scalar scale
Definition: stroke_path_geometry.cc:301
impeller::TRect::Union
constexpr TRect Union(const TRect &o) const
Definition: rect.h:507
impeller::TRect::Area
constexpr T Area() const
Get the area of the rectangle, equivalent to |GetSize().Area()|.
Definition: rect.h:370
impeller::TRect::GetBottom
constexpr auto GetBottom() const
Definition: rect.h:351
impeller::TSize::height
Type height
Definition: size.h:23
impeller::TRect::Union
constexpr static std::optional< TRect > Union(const std::optional< TRect > a, const std::optional< TRect > b)
Definition: rect.h:700
impeller::TRect::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::TRect::GetPositive
constexpr TRect GetPositive() const
Get a version of this rectangle that has a non-negative size.
Definition: rect.h:392
impeller::TRect::GetRightBottom
constexpr TPoint< T > GetRightBottom() const
Definition: rect.h:365
impeller::TRect::Intersection
constexpr static std::optional< TRect > Intersection(const std::optional< TRect > a, const TRect &b)
Definition: rect.h:712
impeller::TRect::GetY
constexpr Type GetY() const
Returns the Y coordinate of the upper left corner, equivalent to |GetOrigin().y|.
Definition: rect.h:331
impeller
Definition: allocation.cc:12
impeller::TRect::GetXYWH
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:387
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::TRect::GetTop
constexpr auto GetTop() const
Definition: rect.h:347
impeller::TRect
Definition: rect.h:122
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
size.h
impeller::TRect::Expand
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:612
impeller::TRect::Union
constexpr static std::optional< TRect > Union(const TRect &a, const std::optional< TRect > b)
Definition: rect.h:688
impeller::TRect::Intersection
constexpr static std::optional< TRect > Intersection(const std::optional< TRect > a, const std::optional< TRect > b)
Definition: rect.h:718
impeller::TRect::operator!=
constexpr bool operator!=(const TRect &r) const
Definition: rect.h:192