1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef MOZILLA_GFX_BASERECT_H_
8 #define MOZILLA_GFX_BASERECT_H_
13 #include <type_traits>
15 #include "mozilla/Assertions.h"
16 #include "mozilla/FloatingPoint.h"
23 * Rectangles have two interpretations: a set of (zero-size) points,
24 * and a rectangular area of the plane. Most rectangle operations behave
25 * the same no matter what interpretation is being used, but some operations
27 * -- Equality tests behave differently. When a rectangle represents an area,
28 * all zero-width and zero-height rectangles are equal to each other since they
29 * represent the empty area. But when a rectangle represents a set of
30 * mathematical points, zero-width and zero-height rectangles can be unequal.
31 * -- The union operation can behave differently. When rectangles represent
32 * areas, taking the union of a zero-width or zero-height rectangle with
33 * another rectangle can just ignore the empty rectangle. But when rectangles
34 * represent sets of mathematical points, we may need to extend the latter
35 * rectangle to include the points of a zero-width or zero-height rectangle.
37 * To ensure that these interpretations are explicitly disambiguated, we
38 * deny access to the == and != operators and require use of IsEqualEdges and
39 * IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
42 * Do not use this class directly. Subclass it, pass that subclass as the
43 * Sub parameter, and only use that subclass.
45 template <class T
, class Sub
, class Point
, class SizeT
, class MarginT
>
47 T x
, y
, width
, height
;
50 BaseRect() : x(0), y(0), width(0), height(0) {}
51 BaseRect(const Point
& aOrigin
, const SizeT
& aSize
)
52 : x(aOrigin
.x
), y(aOrigin
.y
), width(aSize
.width
), height(aSize
.height
) {}
53 BaseRect(T aX
, T aY
, T aWidth
, T aHeight
)
54 : x(aX
), y(aY
), width(aWidth
), height(aHeight
) {}
56 // Emptiness. An empty rect is one that has no area, i.e. its height or width
57 // is <= 0. Zero rect is the one with height and width set to zero. Note
58 // that SetEmpty() may change a rectangle that identified as IsEmpty().
59 MOZ_ALWAYS_INLINE
bool IsZeroArea() const {
60 return height
== 0 || width
== 0;
62 MOZ_ALWAYS_INLINE
bool IsEmpty() const { return height
<= 0 || width
<= 0; }
63 void SetEmpty() { width
= height
= 0; }
65 // "Finite" means not inf and not NaN
66 bool IsFinite() const {
68 std::conditional_t
<std::is_same_v
<T
, float>, float, double>;
69 return (mozilla::IsFinite(FloatType(x
)) &&
70 mozilla::IsFinite(FloatType(y
)) &&
71 mozilla::IsFinite(FloatType(width
)) &&
72 mozilla::IsFinite(FloatType(height
)));
75 // Returns true if this rectangle contains the interior of aRect. Always
76 // returns true if aRect is empty, and always returns false is aRect is
77 // nonempty but this rect is empty.
78 bool Contains(const Sub
& aRect
) const {
79 return aRect
.IsEmpty() || (x
<= aRect
.x
&& aRect
.XMost() <= XMost() &&
80 y
<= aRect
.y
&& aRect
.YMost() <= YMost());
82 // Returns true if this rectangle contains the point. Points are considered
83 // in the rectangle if they are on the left or top edge, but outside if they
84 // are on the right or bottom edge.
85 MOZ_ALWAYS_INLINE
bool Contains(T aX
, T aY
) const {
86 return x
<= aX
&& aX
< XMost() && y
<= aY
&& aY
< YMost();
88 MOZ_ALWAYS_INLINE
bool ContainsX(T aX
) const {
89 return x
<= aX
&& aX
< XMost();
91 MOZ_ALWAYS_INLINE
bool ContainsY(T aY
) const {
92 return y
<= aY
&& aY
< YMost();
94 // Returns true if this rectangle contains the point. Points are considered
95 // in the rectangle if they are on the left or top edge, but outside if they
96 // are on the right or bottom edge.
97 bool Contains(const Point
& aPoint
) const {
98 return Contains(aPoint
.x
, aPoint
.y
);
101 // Intersection. Returns TRUE if the receiver's area has non-empty
102 // intersection with aRect's area, and FALSE otherwise.
103 // Always returns false if aRect is empty or 'this' is empty.
104 bool Intersects(const Sub
& aRect
) const {
105 return !IsEmpty() && !aRect
.IsEmpty() && x
< aRect
.XMost() &&
106 aRect
.x
< XMost() && y
< aRect
.YMost() && aRect
.y
< YMost();
108 // Returns the rectangle containing the intersection of the points
109 // (including edges) of *this and aRect. If there are no points in that
110 // intersection, returns an empty rectangle with x/y set to the std::max of
111 // the x/y of *this and aRect.
112 [[nodiscard
]] Sub
Intersect(const Sub
& aRect
) const {
114 result
.x
= std::max
<T
>(x
, aRect
.x
);
115 result
.y
= std::max
<T
>(y
, aRect
.y
);
117 std::min
<T
>(x
- result
.x
+ width
, aRect
.x
- result
.x
+ aRect
.width
);
119 std::min
<T
>(y
- result
.y
+ height
, aRect
.y
- result
.y
+ aRect
.height
);
120 // See bug 1457110, this function expects to -only- size to 0,0 if the
121 // width/height is explicitly negative.
122 if (result
.width
< 0 || result
.height
< 0) {
127 // Sets *this to be the rectangle containing the intersection of the points
128 // (including edges) of *this and aRect. If there are no points in that
129 // intersection, sets *this to be an empty rectangle with x/y set to the
130 // std::max of the x/y of *this and aRect.
132 // 'this' can be the same object as either aRect1 or aRect2
133 bool IntersectRect(const Sub
& aRect1
, const Sub
& aRect2
) {
134 T newX
= std::max
<T
>(aRect1
.x
, aRect2
.x
);
135 T newY
= std::max
<T
>(aRect1
.y
, aRect2
.y
);
136 width
= std::min
<T
>(aRect1
.x
- newX
+ aRect1
.width
,
137 aRect2
.x
- newX
+ aRect2
.width
);
138 height
= std::min
<T
>(aRect1
.y
- newY
+ aRect1
.height
,
139 aRect2
.y
- newY
+ aRect2
.height
);
142 if (width
<= 0 || height
<= 0) {
149 // Returns the smallest rectangle that contains both the area of both
151 // Thus, empty input rectangles are ignored.
152 // If both rectangles are empty, returns this.
153 // WARNING! This is not safe against overflow, prefer using SafeUnion instead
154 // when dealing with int-based rects.
155 [[nodiscard
]] Sub
Union(const Sub
& aRect
) const {
158 } else if (aRect
.IsEmpty()) {
159 return *static_cast<const Sub
*>(this);
161 return UnionEdges(aRect
);
164 // Returns the smallest rectangle that contains both the points (including
165 // edges) of both aRect1 and aRect2.
166 // Thus, empty input rectangles are allowed to affect the result.
167 // WARNING! This is not safe against overflow, prefer using SafeUnionEdges
168 // instead when dealing with int-based rects.
169 [[nodiscard
]] Sub
UnionEdges(const Sub
& aRect
) const {
171 result
.x
= std::min(x
, aRect
.x
);
172 result
.y
= std::min(y
, aRect
.y
);
173 result
.width
= std::max(XMost(), aRect
.XMost()) - result
.x
;
174 result
.height
= std::max(YMost(), aRect
.YMost()) - result
.y
;
177 // Computes the smallest rectangle that contains both the area of both
178 // aRect1 and aRect2, and fills 'this' with the result.
179 // Thus, empty input rectangles are ignored.
180 // If both rectangles are empty, sets 'this' to aRect2.
182 // 'this' can be the same object as either aRect1 or aRect2
183 void UnionRect(const Sub
& aRect1
, const Sub
& aRect2
) {
184 *static_cast<Sub
*>(this) = aRect1
.Union(aRect2
);
187 void OrWith(const Sub
& aRect1
) {
188 UnionRect(*static_cast<Sub
*>(this), aRect1
);
191 // Computes the smallest rectangle that contains both the points (including
192 // edges) of both aRect1 and aRect2.
193 // Thus, empty input rectangles are allowed to affect the result.
195 // 'this' can be the same object as either aRect1 or aRect2
196 void UnionRectEdges(const Sub
& aRect1
, const Sub
& aRect2
) {
197 *static_cast<Sub
*>(this) = aRect1
.UnionEdges(aRect2
);
200 // Expands the rect to include the point
201 void ExpandToEnclose(const Point
& aPoint
) {
203 width
= XMost() - aPoint
.x
;
205 } else if (aPoint
.x
> XMost()) {
206 width
= aPoint
.x
- x
;
209 height
= YMost() - aPoint
.y
;
211 } else if (aPoint
.y
> YMost()) {
212 height
= aPoint
.y
- y
;
216 MOZ_ALWAYS_INLINE
void SetRect(T aX
, T aY
, T aWidth
, T aHeight
) {
222 MOZ_ALWAYS_INLINE
void SetRectX(T aX
, T aWidth
) {
226 MOZ_ALWAYS_INLINE
void SetRectY(T aY
, T aHeight
) {
230 MOZ_ALWAYS_INLINE
void SetBox(T aX
, T aY
, T aXMost
, T aYMost
) {
234 height
= aYMost
- aY
;
236 MOZ_ALWAYS_INLINE
void SetNonEmptyBox(T aX
, T aY
, T aXMost
, T aYMost
) {
239 width
= std::max(0, aXMost
- aX
);
240 height
= std::max(0, aYMost
- aY
);
242 MOZ_ALWAYS_INLINE
void SetBoxX(T aX
, T aXMost
) {
246 MOZ_ALWAYS_INLINE
void SetBoxY(T aY
, T aYMost
) {
248 height
= aYMost
- aY
;
250 void SetRect(const Point
& aPt
, const SizeT
& aSize
) {
251 SetRect(aPt
.x
, aPt
.y
, aSize
.width
, aSize
.height
);
253 MOZ_ALWAYS_INLINE
void GetRect(T
* aX
, T
* aY
, T
* aWidth
, T
* aHeight
) const {
260 MOZ_ALWAYS_INLINE
void MoveTo(T aX
, T aY
) {
264 MOZ_ALWAYS_INLINE
void MoveToX(T aX
) { x
= aX
; }
265 MOZ_ALWAYS_INLINE
void MoveToY(T aY
) { y
= aY
; }
266 MOZ_ALWAYS_INLINE
void MoveTo(const Point
& aPoint
) {
270 MOZ_ALWAYS_INLINE
void MoveBy(T aDx
, T aDy
) {
274 MOZ_ALWAYS_INLINE
void MoveByX(T aDx
) { x
+= aDx
; }
275 MOZ_ALWAYS_INLINE
void MoveByY(T aDy
) { y
+= aDy
; }
276 MOZ_ALWAYS_INLINE
void MoveBy(const Point
& aPoint
) {
280 MOZ_ALWAYS_INLINE
void SizeTo(T aWidth
, T aHeight
) {
284 MOZ_ALWAYS_INLINE
void SizeTo(const SizeT
& aSize
) {
286 height
= aSize
.height
;
289 void Inflate(T aD
) { Inflate(aD
, aD
); }
290 void Inflate(T aDx
, T aDy
) {
296 void Inflate(const MarginT
& aMargin
) {
299 width
+= aMargin
.LeftRight();
300 height
+= aMargin
.TopBottom();
302 void Inflate(const SizeT
& aSize
) { Inflate(aSize
.width
, aSize
.height
); }
304 void Deflate(T aD
) { Deflate(aD
, aD
); }
305 void Deflate(T aDx
, T aDy
) {
308 width
= std::max(T(0), width
- 2 * aDx
);
309 height
= std::max(T(0), height
- 2 * aDy
);
311 void Deflate(const MarginT
& aMargin
) {
314 width
= std::max(T(0), width
- aMargin
.LeftRight());
315 height
= std::max(T(0), height
- aMargin
.TopBottom());
317 void Deflate(const SizeT
& aSize
) { Deflate(aSize
.width
, aSize
.height
); }
319 // Return true if the rectangles contain the same set of points, including
320 // points on the edges.
321 // Use when we care about the exact x/y/width/height values being
322 // equal (i.e. we care about differences in empty rectangles).
323 bool IsEqualEdges(const Sub
& aRect
) const {
324 return x
== aRect
.x
&& y
== aRect
.y
&& width
== aRect
.width
&&
325 height
== aRect
.height
;
327 MOZ_ALWAYS_INLINE
bool IsEqualRect(T aX
, T aY
, T aW
, T aH
) {
328 return x
== aX
&& y
== aY
&& width
== aW
&& height
== aH
;
330 MOZ_ALWAYS_INLINE
bool IsEqualXY(T aX
, T aY
) { return x
== aX
&& y
== aY
; }
332 MOZ_ALWAYS_INLINE
bool IsEqualSize(T aW
, T aH
) {
333 return width
== aW
&& height
== aH
;
336 // Return true if the rectangles contain the same area of the plane.
337 // Use when we do not care about differences in empty rectangles.
338 bool IsEqualInterior(const Sub
& aRect
) const {
339 return IsEqualEdges(aRect
) || (IsEmpty() && aRect
.IsEmpty());
342 friend Sub
operator+(Sub aSub
, const Point
& aPoint
) {
346 friend Sub
operator-(Sub aSub
, const Point
& aPoint
) {
350 friend Sub
operator+(Sub aSub
, const SizeT
& aSize
) {
354 friend Sub
operator-(Sub aSub
, const SizeT
& aSize
) {
358 Sub
& operator+=(const Point
& aPoint
) {
360 return *static_cast<Sub
*>(this);
362 Sub
& operator-=(const Point
& aPoint
) {
364 return *static_cast<Sub
*>(this);
366 Sub
& operator+=(const SizeT
& aSize
) {
367 width
+= aSize
.width
;
368 height
+= aSize
.height
;
369 return *static_cast<Sub
*>(this);
371 Sub
& operator-=(const SizeT
& aSize
) {
372 width
-= aSize
.width
;
373 height
-= aSize
.height
;
374 return *static_cast<Sub
*>(this);
376 // Find difference as a Margin
377 MarginT
operator-(const Sub
& aRect
) const {
378 return MarginT(aRect
.y
- y
, XMost() - aRect
.XMost(),
379 YMost() - aRect
.YMost(), aRect
.x
- x
);
382 // Helpers for accessing the vertices
383 Point
TopLeft() const { return Point(x
, y
); }
384 Point
TopRight() const { return Point(XMost(), y
); }
385 Point
BottomLeft() const { return Point(x
, YMost()); }
386 Point
BottomRight() const { return Point(XMost(), YMost()); }
387 Point
AtCorner(Corner aCorner
) const {
391 case eCornerTopRight
:
393 case eCornerBottomRight
:
394 return BottomRight();
395 case eCornerBottomLeft
:
398 MOZ_CRASH("GFX: Incomplete switch");
400 Point
CCWCorner(mozilla::Side side
) const {
407 return BottomRight();
411 MOZ_CRASH("GFX: Incomplete switch");
413 Point
CWCorner(mozilla::Side side
) const {
418 return BottomRight();
424 MOZ_CRASH("GFX: Incomplete switch");
426 Point
Center() const { return Point(x
, y
) + Point(width
, height
) / 2; }
427 SizeT
Size() const { return SizeT(width
, height
); }
429 T
Area() const { return width
* height
; }
431 // Helper methods for computing the extents
432 MOZ_ALWAYS_INLINE T
X() const { return x
; }
433 MOZ_ALWAYS_INLINE T
Y() const { return y
; }
434 MOZ_ALWAYS_INLINE T
Width() const { return width
; }
435 MOZ_ALWAYS_INLINE T
Height() const { return height
; }
436 MOZ_ALWAYS_INLINE T
XMost() const { return x
+ width
; }
437 MOZ_ALWAYS_INLINE T
YMost() const { return y
+ height
; }
439 // Set width and height. SizeTo() sets them together.
440 MOZ_ALWAYS_INLINE
void SetWidth(T aWidth
) { width
= aWidth
; }
441 MOZ_ALWAYS_INLINE
void SetHeight(T aHeight
) { height
= aHeight
; }
443 // Get the coordinate of the edge on the given side.
444 T
Edge(mozilla::Side aSide
) const {
455 MOZ_CRASH("GFX: Incomplete switch");
458 // Moves one edge of the rect without moving the opposite edge.
459 void SetLeftEdge(T aX
) {
460 width
= XMost() - aX
;
463 void SetRightEdge(T aXMost
) { width
= aXMost
- x
; }
464 void SetTopEdge(T aY
) {
465 height
= YMost() - aY
;
468 void SetBottomEdge(T aYMost
) { height
= aYMost
- y
; }
471 std::swap(width
, height
);
474 // Round the rectangle edges to integer coordinates, such that the rounded
475 // rectangle has the same set of pixel centers as the original rectangle.
476 // Edges at offset 0.5 round up.
477 // Suitable for most places where integral device coordinates
478 // are needed, but note that any translation should be applied first to
479 // avoid pixel rounding errors.
480 // Note that this is *not* rounding to nearest integer if the values are
481 // negative. They are always rounding as floor(n + 0.5). See
482 // https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14 If you need similar
483 // method which is using NS_round(), you should create new
484 // |RoundAwayFromZero()| method.
486 T x0
= static_cast<T
>(std::floor(T(X()) + 0.5f
));
487 T y0
= static_cast<T
>(std::floor(T(Y()) + 0.5f
));
488 T x1
= static_cast<T
>(std::floor(T(XMost()) + 0.5f
));
489 T y1
= static_cast<T
>(std::floor(T(YMost()) + 0.5f
));
498 // Snap the rectangle edges to integer coordinates, such that the
499 // original rectangle contains the resulting rectangle.
501 T x0
= static_cast<T
>(std::ceil(T(X())));
502 T y0
= static_cast<T
>(std::ceil(T(Y())));
503 T x1
= static_cast<T
>(std::floor(T(XMost())));
504 T y1
= static_cast<T
>(std::floor(T(YMost())));
513 // Snap the rectangle edges to integer coordinates, such that the
514 // resulting rectangle contains the original rectangle.
516 T x0
= static_cast<T
>(std::floor(T(X())));
517 T y0
= static_cast<T
>(std::floor(T(Y())));
518 T x1
= static_cast<T
>(std::ceil(T(XMost())));
519 T y1
= static_cast<T
>(std::ceil(T(YMost())));
528 // Scale 'this' by aScale without doing any rounding.
529 void Scale(T aScale
) { Scale(aScale
, aScale
); }
530 // Scale 'this' by aXScale and aYScale, without doing any rounding.
531 void Scale(T aXScale
, T aYScale
) {
532 T right
= XMost() * aXScale
;
533 T bottom
= YMost() * aYScale
;
539 // Scale 'this' by aScale, converting coordinates to integers so that the
540 // result is the smallest integer-coordinate rectangle containing the
541 // unrounded result. Note: this can turn an empty rectangle into a non-empty
543 void ScaleRoundOut(double aScale
) { ScaleRoundOut(aScale
, aScale
); }
544 // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
545 // that the result is the smallest integer-coordinate rectangle containing the
547 // Note: this can turn an empty rectangle into a non-empty rectangle
548 void ScaleRoundOut(double aXScale
, double aYScale
) {
549 T right
= static_cast<T
>(ceil(double(XMost()) * aXScale
));
550 T bottom
= static_cast<T
>(ceil(double(YMost()) * aYScale
));
551 x
= static_cast<T
>(floor(double(x
) * aXScale
));
552 y
= static_cast<T
>(floor(double(y
) * aYScale
));
556 // Scale 'this' by aScale, converting coordinates to integers so that the
557 // result is the largest integer-coordinate rectangle contained by the
559 void ScaleRoundIn(double aScale
) { ScaleRoundIn(aScale
, aScale
); }
560 // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
561 // that the result is the largest integer-coordinate rectangle contained by
562 // the unrounded result.
563 void ScaleRoundIn(double aXScale
, double aYScale
) {
564 T right
= static_cast<T
>(floor(double(XMost()) * aXScale
));
565 T bottom
= static_cast<T
>(floor(double(YMost()) * aYScale
));
566 x
= static_cast<T
>(ceil(double(x
) * aXScale
));
567 y
= static_cast<T
>(ceil(double(y
) * aYScale
));
568 width
= std::max
<T
>(0, right
- x
);
569 height
= std::max
<T
>(0, bottom
- y
);
571 // Scale 'this' by 1/aScale, converting coordinates to integers so that the
572 // result is the smallest integer-coordinate rectangle containing the
573 // unrounded result. Note: this can turn an empty rectangle into a non-empty
575 void ScaleInverseRoundOut(double aScale
) {
576 ScaleInverseRoundOut(aScale
, aScale
);
578 // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers
579 // so that the result is the smallest integer-coordinate rectangle containing
580 // the unrounded result. Note: this can turn an empty rectangle into a
581 // non-empty rectangle
582 void ScaleInverseRoundOut(double aXScale
, double aYScale
) {
583 T right
= static_cast<T
>(ceil(double(XMost()) / aXScale
));
584 T bottom
= static_cast<T
>(ceil(double(YMost()) / aYScale
));
585 x
= static_cast<T
>(floor(double(x
) / aXScale
));
586 y
= static_cast<T
>(floor(double(y
) / aYScale
));
590 // Scale 'this' by 1/aScale, converting coordinates to integers so that the
591 // result is the largest integer-coordinate rectangle contained by the
593 void ScaleInverseRoundIn(double aScale
) {
594 ScaleInverseRoundIn(aScale
, aScale
);
596 // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers
597 // so that the result is the largest integer-coordinate rectangle contained by
598 // the unrounded result.
599 void ScaleInverseRoundIn(double aXScale
, double aYScale
) {
600 T right
= static_cast<T
>(floor(double(XMost()) / aXScale
));
601 T bottom
= static_cast<T
>(floor(double(YMost()) / aYScale
));
602 x
= static_cast<T
>(ceil(double(x
) / aXScale
));
603 y
= static_cast<T
>(ceil(double(y
) / aYScale
));
604 width
= std::max
<T
>(0, right
- x
);
605 height
= std::max
<T
>(0, bottom
- y
);
609 * Clamp aPoint to this rectangle. It is allowed to end up on any
610 * edge of the rectangle.
612 [[nodiscard
]] Point
ClampPoint(const Point
& aPoint
) const {
613 return Point(std::max(x
, std::min(XMost(), aPoint
.x
)),
614 std::max(y
, std::min(YMost(), aPoint
.y
)));
618 * Translate this rectangle to be inside aRect. If it doesn't fit inside
619 * aRect then the dimensions that don't fit will be shrunk so that they
620 * do fit. The resulting rect is returned.
622 [[nodiscard
]] Sub
MoveInsideAndClamp(const Sub
& aRect
) const {
623 Sub
rect(std::max(aRect
.x
, x
), std::max(aRect
.y
, y
),
624 std::min(aRect
.width
, width
), std::min(aRect
.height
, height
));
625 rect
.x
= std::min(rect
.XMost(), aRect
.XMost()) - rect
.width
;
626 rect
.y
= std::min(rect
.YMost(), aRect
.YMost()) - rect
.height
;
630 // Returns the largest rectangle that can be represented with 32-bit
631 // signed integers, centered around a point at 0,0. As BaseRect's represent
632 // the dimensions as a top-left point with a width and height, the width
633 // and height will be the largest positive 32-bit value. The top-left
634 // position coordinate is divided by two to center the rectangle around a
636 static Sub
MaxIntRect() {
637 return Sub(static_cast<T
>(-std::numeric_limits
<int32_t>::max() * 0.5),
638 static_cast<T
>(-std::numeric_limits
<int32_t>::max() * 0.5),
639 static_cast<T
>(std::numeric_limits
<int32_t>::max()),
640 static_cast<T
>(std::numeric_limits
<int32_t>::max()));
643 // Returns a point representing the distance, along each dimension, of the
644 // given point from this rectangle. The distance along a dimension is defined
645 // as zero if the point is within the bounds of the rectangle in that
646 // dimension; otherwise, it's the distance to the closer endpoint of the
647 // rectangle in that dimension.
648 Point
DistanceTo(const Point
& aPoint
) const {
649 return {DistanceFromInterval(aPoint
.x
, x
, XMost()),
650 DistanceFromInterval(aPoint
.y
, y
, YMost())};
653 friend std::ostream
& operator<<(
654 std::ostream
& stream
,
655 const BaseRect
<T
, Sub
, Point
, SizeT
, MarginT
>& aRect
) {
656 return stream
<< "(x=" << aRect
.x
<< ", y=" << aRect
.y
657 << ", w=" << aRect
.width
<< ", h=" << aRect
.height
<< ')';
661 // Do not use the default operator== or operator!= !
662 // Use IsEqualEdges or IsEqualInterior explicitly.
663 bool operator==(const Sub
& aRect
) const { return false; }
664 bool operator!=(const Sub
& aRect
) const { return false; }
666 // Helper function for DistanceTo() that computes the distance of a
667 // coordinate along one dimension from an interval in that dimension.
668 static T
DistanceFromInterval(T aCoord
, T aIntervalStart
, T aIntervalEnd
) {
669 if (aCoord
< aIntervalStart
) {
670 return aIntervalStart
- aCoord
;
672 if (aCoord
> aIntervalEnd
) {
673 return aCoord
- aIntervalEnd
;
680 } // namespace mozilla
682 #endif /* MOZILLA_GFX_BASERECT_H_ */