Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #ifndef MOZILLA_GFX_BASERECT_H_
7 : #define MOZILLA_GFX_BASERECT_H_
8 :
9 : #include <algorithm>
10 : #include <cmath>
11 : #include <ostream>
12 :
13 : #include "mozilla/Assertions.h"
14 : #include "mozilla/FloatingPoint.h"
15 : #include "mozilla/TypeTraits.h"
16 : #include "Types.h"
17 :
18 : namespace mozilla {
19 : namespace gfx {
20 :
21 : /**
22 : * Rectangles have two interpretations: a set of (zero-size) points,
23 : * and a rectangular area of the plane. Most rectangle operations behave
24 : * the same no matter what interpretation is being used, but some operations
25 : * differ:
26 : * -- Equality tests behave differently. When a rectangle represents an area,
27 : * all zero-width and zero-height rectangles are equal to each other since they
28 : * represent the empty area. But when a rectangle represents a set of
29 : * mathematical points, zero-width and zero-height rectangles can be unequal.
30 : * -- The union operation can behave differently. When rectangles represent
31 : * areas, taking the union of a zero-width or zero-height rectangle with
32 : * another rectangle can just ignore the empty rectangle. But when rectangles
33 : * represent sets of mathematical points, we may need to extend the latter
34 : * rectangle to include the points of a zero-width or zero-height rectangle.
35 : *
36 : * To ensure that these interpretations are explicitly disambiguated, we
37 : * deny access to the == and != operators and require use of IsEqualEdges and
38 : * IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
39 : * methods.
40 : *
41 : * Do not use this class directly. Subclass it, pass that subclass as the
42 : * Sub parameter, and only use that subclass.
43 : */
44 : template <class T, class Sub, class Point, class SizeT, class MarginT>
45 : struct BaseRect {
46 : T x, y, width, height;
47 :
48 : // Constructors
49 116941 : BaseRect() : x(0), y(0), width(0), height(0) {}
50 24761 : BaseRect(const Point& aOrigin, const SizeT &aSize) :
51 24761 : x(aOrigin.x), y(aOrigin.y), width(aSize.width), height(aSize.height)
52 : {
53 24761 : }
54 60001 : BaseRect(T aX, T aY, T aWidth, T aHeight) :
55 60001 : x(aX), y(aY), width(aWidth), height(aHeight)
56 : {
57 60001 : }
58 :
59 : // Emptiness. An empty rect is one that has no area, i.e. its height or width
60 : // is <= 0
61 82223 : bool IsEmpty() const { return height <= 0 || width <= 0; }
62 1311 : void SetEmpty() { width = height = 0; }
63 :
64 : // "Finite" means not inf and not NaN
65 40 : bool IsFinite() const
66 : {
67 : typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value, float, double>::Type FloatType;
68 80 : return (mozilla::IsFinite(FloatType(x)) &&
69 80 : mozilla::IsFinite(FloatType(y)) &&
70 120 : mozilla::IsFinite(FloatType(width)) &&
71 80 : mozilla::IsFinite(FloatType(height)));
72 : }
73 :
74 : // Returns true if this rectangle contains the interior of aRect. Always
75 : // returns true if aRect is empty, and always returns false is aRect is
76 : // nonempty but this rect is empty.
77 3806 : bool Contains(const Sub& aRect) const
78 : {
79 10773 : return aRect.IsEmpty() ||
80 10561 : (x <= aRect.x && aRect.XMost() <= XMost() &&
81 10332 : y <= aRect.y && aRect.YMost() <= YMost());
82 : }
83 : // Returns true if this rectangle contains the point. Points are considered
84 : // in the rectangle if they are on the left or top edge, but outside if they
85 : // are on the right or bottom edge.
86 28 : bool Contains(T aX, T aY) const
87 : {
88 84 : return x <= aX && aX < XMost() &&
89 84 : y <= aY && aY < YMost();
90 : }
91 : // Returns true if this rectangle contains the point. Points are considered
92 : // in the rectangle if they are on the left or top edge, but outside if they
93 : // are on the right or bottom edge.
94 28 : bool Contains(const Point& aPoint) const { return Contains(aPoint.x, aPoint.y); }
95 :
96 : // Intersection. Returns TRUE if the receiver's area has non-empty
97 : // intersection with aRect's area, and FALSE otherwise.
98 : // Always returns false if aRect is empty or 'this' is empty.
99 3726 : bool Intersects(const Sub& aRect) const
100 : {
101 10220 : return !IsEmpty() && !aRect.IsEmpty() &&
102 8815 : x < aRect.XMost() && aRect.x < XMost() &&
103 8394 : y < aRect.YMost() && aRect.y < YMost();
104 : }
105 : // Returns the rectangle containing the intersection of the points
106 : // (including edges) of *this and aRect. If there are no points in that
107 : // intersection, returns an empty rectangle with x/y set to the std::max of the x/y
108 : // of *this and aRect.
109 21800 : MOZ_MUST_USE Sub Intersect(const Sub& aRect) const
110 : {
111 21800 : Sub result;
112 21800 : result.x = std::max<T>(x, aRect.x);
113 21800 : result.y = std::max<T>(y, aRect.y);
114 21800 : result.width = std::min<T>(x - result.x + width, aRect.x - result.x + aRect.width);
115 21800 : result.height = std::min<T>(y - result.y + height, aRect.y - result.y + aRect.height);
116 21800 : if (result.width < 0 || result.height < 0) {
117 1109 : result.SizeTo(0, 0);
118 : }
119 21800 : return result;
120 : }
121 : // Sets *this to be the rectangle containing the intersection of the points
122 : // (including edges) of *this and aRect. If there are no points in that
123 : // intersection, sets *this to be an empty rectangle with x/y set to the std::max
124 : // of the x/y of *this and aRect.
125 : //
126 : // 'this' can be the same object as either aRect1 or aRect2
127 10820 : bool IntersectRect(const Sub& aRect1, const Sub& aRect2)
128 : {
129 10820 : *static_cast<Sub*>(this) = aRect1.Intersect(aRect2);
130 10820 : return !IsEmpty();
131 : }
132 :
133 : // Returns the smallest rectangle that contains both the area of both
134 : // this and aRect2.
135 : // Thus, empty input rectangles are ignored.
136 : // If both rectangles are empty, returns this.
137 : // WARNING! This is not safe against overflow, prefer using SafeUnion instead
138 : // when dealing with int-based rects.
139 2563 : MOZ_MUST_USE Sub Union(const Sub& aRect) const
140 : {
141 2563 : if (IsEmpty()) {
142 1708 : return aRect;
143 856 : } else if (aRect.IsEmpty()) {
144 11 : return *static_cast<const Sub*>(this);
145 : } else {
146 845 : return UnionEdges(aRect);
147 : }
148 : }
149 : // Returns the smallest rectangle that contains both the points (including
150 : // edges) of both aRect1 and aRect2.
151 : // Thus, empty input rectangles are allowed to affect the result.
152 : // WARNING! This is not safe against overflow, prefer using SafeUnionEdges
153 : // instead when dealing with int-based rects.
154 845 : MOZ_MUST_USE Sub UnionEdges(const Sub& aRect) const
155 : {
156 845 : Sub result;
157 845 : result.x = std::min(x, aRect.x);
158 845 : result.y = std::min(y, aRect.y);
159 845 : result.width = std::max(XMost(), aRect.XMost()) - result.x;
160 845 : result.height = std::max(YMost(), aRect.YMost()) - result.y;
161 845 : return result;
162 : }
163 : // Computes the smallest rectangle that contains both the area of both
164 : // aRect1 and aRect2, and fills 'this' with the result.
165 : // Thus, empty input rectangles are ignored.
166 : // If both rectangles are empty, sets 'this' to aRect2.
167 : //
168 : // 'this' can be the same object as either aRect1 or aRect2
169 2422 : void UnionRect(const Sub& aRect1, const Sub& aRect2)
170 : {
171 2422 : *static_cast<Sub*>(this) = aRect1.Union(aRect2);
172 2422 : }
173 :
174 : // Computes the smallest rectangle that contains both the points (including
175 : // edges) of both aRect1 and aRect2.
176 : // Thus, empty input rectangles are allowed to affect the result.
177 : //
178 : // 'this' can be the same object as either aRect1 or aRect2
179 : void UnionRectEdges(const Sub& aRect1, const Sub& aRect2)
180 : {
181 : *static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2);
182 : }
183 :
184 : // Expands the rect to include the point
185 256 : void ExpandToEnclose(const Point& aPoint)
186 : {
187 256 : if (aPoint.x < x) {
188 0 : width = XMost() - aPoint.x;
189 0 : x = aPoint.x;
190 256 : } else if (aPoint.x > XMost()) {
191 96 : width = aPoint.x - x;
192 : }
193 256 : if (aPoint.y < y) {
194 24 : height = YMost() - aPoint.y;
195 24 : y = aPoint.y;
196 232 : } else if (aPoint.y > YMost()) {
197 80 : height = aPoint.y - y;
198 : }
199 256 : }
200 :
201 1207 : void SetRect(T aX, T aY, T aWidth, T aHeight)
202 : {
203 1207 : x = aX; y = aY; width = aWidth; height = aHeight;
204 1207 : }
205 0 : void SetRect(const Point& aPt, const SizeT& aSize)
206 : {
207 0 : SetRect(aPt.x, aPt.y, aSize.width, aSize.height);
208 0 : }
209 980 : void MoveTo(T aX, T aY) { x = aX; y = aY; }
210 953 : void MoveTo(const Point& aPoint) { x = aPoint.x; y = aPoint.y; }
211 418 : void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
212 23820 : void MoveBy(const Point& aPoint) { x += aPoint.x; y += aPoint.y; }
213 1278 : void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
214 680 : void SizeTo(const SizeT& aSize) { width = aSize.width; height = aSize.height; }
215 :
216 175 : void Inflate(T aD) { Inflate(aD, aD); }
217 185 : void Inflate(T aDx, T aDy)
218 : {
219 185 : x -= aDx;
220 185 : y -= aDy;
221 185 : width += 2 * aDx;
222 185 : height += 2 * aDy;
223 185 : }
224 338 : void Inflate(const MarginT& aMargin)
225 : {
226 338 : x -= aMargin.left;
227 338 : y -= aMargin.top;
228 338 : width += aMargin.LeftRight();
229 338 : height += aMargin.TopBottom();
230 338 : }
231 1 : void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); }
232 :
233 0 : void Deflate(T aD) { Deflate(aD, aD); }
234 10 : void Deflate(T aDx, T aDy)
235 : {
236 10 : x += aDx;
237 10 : y += aDy;
238 10 : width = std::max(T(0), width - 2 * aDx);
239 10 : height = std::max(T(0), height - 2 * aDy);
240 10 : }
241 3601 : void Deflate(const MarginT& aMargin)
242 : {
243 3601 : x += aMargin.left;
244 3601 : y += aMargin.top;
245 3601 : width = std::max(T(0), width - aMargin.LeftRight());
246 3601 : height = std::max(T(0), height - aMargin.TopBottom());
247 3601 : }
248 0 : void Deflate(const SizeT& aSize) { Deflate(aSize.width, aSize.height); }
249 :
250 : // Return true if the rectangles contain the same set of points, including
251 : // points on the edges.
252 : // Use when we care about the exact x/y/width/height values being
253 : // equal (i.e. we care about differences in empty rectangles).
254 13836 : bool IsEqualEdges(const Sub& aRect) const
255 : {
256 39431 : return x == aRect.x && y == aRect.y &&
257 38595 : width == aRect.width && height == aRect.height;
258 : }
259 : // Return true if the rectangles contain the same area of the plane.
260 : // Use when we do not care about differences in empty rectangles.
261 8997 : bool IsEqualInterior(const Sub& aRect) const
262 : {
263 8997 : return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty());
264 : }
265 :
266 6985 : friend Sub operator+(Sub aSub, const Point& aPoint)
267 : {
268 6985 : aSub += aPoint;
269 6985 : return aSub;
270 : }
271 5278 : friend Sub operator-(Sub aSub, const Point& aPoint)
272 : {
273 5278 : aSub -= aPoint;
274 5278 : return aSub;
275 : }
276 : friend Sub operator+(Sub aSub, const SizeT& aSize)
277 : {
278 : aSub += aSize;
279 : return aSub;
280 : }
281 : friend Sub operator-(Sub aSub, const SizeT& aSize)
282 : {
283 : aSub -= aSize;
284 : return aSub;
285 : }
286 14249 : Sub& operator+=(const Point& aPoint)
287 : {
288 14249 : MoveBy(aPoint);
289 14249 : return *static_cast<Sub*>(this);
290 : }
291 5550 : Sub& operator-=(const Point& aPoint)
292 : {
293 5550 : MoveBy(-aPoint);
294 5550 : return *static_cast<Sub*>(this);
295 : }
296 : Sub& operator+=(const SizeT& aSize)
297 : {
298 : width += aSize.width;
299 : height += aSize.height;
300 : return *static_cast<Sub*>(this);
301 : }
302 : Sub& operator-=(const SizeT& aSize)
303 : {
304 : width -= aSize.width;
305 : height -= aSize.height;
306 : return *static_cast<Sub*>(this);
307 : }
308 : // Find difference as a Margin
309 0 : MarginT operator-(const Sub& aRect) const
310 : {
311 0 : return MarginT(aRect.y - y,
312 0 : XMost() - aRect.XMost(),
313 0 : YMost() - aRect.YMost(),
314 0 : aRect.x - x);
315 : }
316 :
317 : // Helpers for accessing the vertices
318 35641 : Point TopLeft() const { return Point(x, y); }
319 3678 : Point TopRight() const { return Point(XMost(), y); }
320 3068 : Point BottomLeft() const { return Point(x, YMost()); }
321 3786 : Point BottomRight() const { return Point(XMost(), YMost()); }
322 153 : Point AtCorner(Corner aCorner) const {
323 153 : switch (aCorner) {
324 45 : case eCornerTopLeft: return TopLeft();
325 42 : case eCornerTopRight: return TopRight();
326 33 : case eCornerBottomRight: return BottomRight();
327 33 : case eCornerBottomLeft: return BottomLeft();
328 : }
329 0 : MOZ_CRASH("GFX: Incomplete switch");
330 : }
331 0 : Point CCWCorner(mozilla::Side side) const {
332 0 : switch (side) {
333 0 : case eSideTop: return TopLeft();
334 0 : case eSideRight: return TopRight();
335 0 : case eSideBottom: return BottomRight();
336 0 : case eSideLeft: return BottomLeft();
337 : }
338 0 : MOZ_CRASH("GFX: Incomplete switch");
339 : }
340 0 : Point CWCorner(mozilla::Side side) const {
341 0 : switch (side) {
342 0 : case eSideTop: return TopRight();
343 0 : case eSideRight: return BottomRight();
344 0 : case eSideBottom: return BottomLeft();
345 0 : case eSideLeft: return TopLeft();
346 : }
347 0 : MOZ_CRASH("GFX: Incomplete switch");
348 : }
349 0 : Point Center() const { return Point(x, y) + Point(width, height)/2; }
350 28697 : SizeT Size() const { return SizeT(width, height); }
351 :
352 0 : T Area() const { return width * height; }
353 :
354 : // Helper methods for computing the extents
355 5953 : T X() const { return x; }
356 6024 : T Y() const { return y; }
357 3253 : T Width() const { return width; }
358 3365 : T Height() const { return height; }
359 45975 : T XMost() const { return x + width; }
360 44426 : T YMost() const { return y + height; }
361 :
362 : // Get the coordinate of the edge on the given side.
363 0 : T Edge(mozilla::Side aSide) const
364 : {
365 0 : switch (aSide) {
366 0 : case eSideTop: return Y();
367 0 : case eSideRight: return XMost();
368 0 : case eSideBottom: return YMost();
369 0 : case eSideLeft: return X();
370 : }
371 0 : MOZ_CRASH("GFX: Incomplete switch");
372 : }
373 :
374 : // Moves one edge of the rect without moving the opposite edge.
375 18 : void SetLeftEdge(T aX) {
376 18 : MOZ_ASSERT(aX <= XMost());
377 18 : width = XMost() - aX;
378 18 : x = aX;
379 18 : }
380 408 : void SetRightEdge(T aXMost) {
381 408 : MOZ_ASSERT(aXMost >= x);
382 408 : width = aXMost - x;
383 408 : }
384 18 : void SetTopEdge(T aY) {
385 18 : MOZ_ASSERT(aY <= YMost());
386 18 : height = YMost() - aY;
387 18 : y = aY;
388 18 : }
389 408 : void SetBottomEdge(T aYMost) {
390 408 : MOZ_ASSERT(aYMost >= y);
391 408 : height = aYMost - y;
392 408 : }
393 :
394 : // Round the rectangle edges to integer coordinates, such that the rounded
395 : // rectangle has the same set of pixel centers as the original rectangle.
396 : // Edges at offset 0.5 round up.
397 : // Suitable for most places where integral device coordinates
398 : // are needed, but note that any translation should be applied first to
399 : // avoid pixel rounding errors.
400 : // Note that this is *not* rounding to nearest integer if the values are negative.
401 : // They are always rounding as floor(n + 0.5).
402 : // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
403 : // If you need similar method which is using NS_round(), you should create
404 : // new |RoundAwayFromZero()| method.
405 880 : void Round()
406 : {
407 880 : T x0 = static_cast<T>(floor(T(X()) + 0.5));
408 880 : T y0 = static_cast<T>(floor(T(Y()) + 0.5));
409 880 : T x1 = static_cast<T>(floor(T(XMost()) + 0.5));
410 880 : T y1 = static_cast<T>(floor(T(YMost()) + 0.5));
411 :
412 880 : x = x0;
413 880 : y = y0;
414 :
415 880 : width = x1 - x0;
416 880 : height = y1 - y0;
417 880 : }
418 :
419 : // Snap the rectangle edges to integer coordinates, such that the
420 : // original rectangle contains the resulting rectangle.
421 47 : void RoundIn()
422 : {
423 47 : T x0 = static_cast<T>(ceil(T(X())));
424 47 : T y0 = static_cast<T>(ceil(T(Y())));
425 47 : T x1 = static_cast<T>(floor(T(XMost())));
426 47 : T y1 = static_cast<T>(floor(T(YMost())));
427 :
428 47 : x = x0;
429 47 : y = y0;
430 :
431 47 : width = x1 - x0;
432 47 : height = y1 - y0;
433 47 : }
434 :
435 : // Snap the rectangle edges to integer coordinates, such that the
436 : // resulting rectangle contains the original rectangle.
437 1092 : void RoundOut()
438 : {
439 1092 : T x0 = static_cast<T>(floor(T(X())));
440 1092 : T y0 = static_cast<T>(floor(T(Y())));
441 1092 : T x1 = static_cast<T>(ceil(T(XMost())));
442 1092 : T y1 = static_cast<T>(ceil(T(YMost())));
443 :
444 1092 : x = x0;
445 1092 : y = y0;
446 :
447 1092 : width = x1 - x0;
448 1092 : height = y1 - y0;
449 1092 : }
450 :
451 : // Scale 'this' by aScale without doing any rounding.
452 0 : void Scale(T aScale) { Scale(aScale, aScale); }
453 : // Scale 'this' by aXScale and aYScale, without doing any rounding.
454 242 : void Scale(T aXScale, T aYScale)
455 : {
456 242 : T right = XMost() * aXScale;
457 242 : T bottom = YMost() * aYScale;
458 242 : x = x * aXScale;
459 242 : y = y * aYScale;
460 242 : width = right - x;
461 242 : height = bottom - y;
462 242 : }
463 : // Scale 'this' by aScale, converting coordinates to integers so that the result is
464 : // the smallest integer-coordinate rectangle containing the unrounded result.
465 : // Note: this can turn an empty rectangle into a non-empty rectangle
466 305 : void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); }
467 : // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
468 : // that the result is the smallest integer-coordinate rectangle containing the
469 : // unrounded result.
470 : // Note: this can turn an empty rectangle into a non-empty rectangle
471 306 : void ScaleRoundOut(double aXScale, double aYScale)
472 : {
473 306 : T right = static_cast<T>(ceil(double(XMost()) * aXScale));
474 306 : T bottom = static_cast<T>(ceil(double(YMost()) * aYScale));
475 306 : x = static_cast<T>(floor(double(x) * aXScale));
476 306 : y = static_cast<T>(floor(double(y) * aYScale));
477 306 : width = right - x;
478 306 : height = bottom - y;
479 306 : }
480 : // Scale 'this' by aScale, converting coordinates to integers so that the result is
481 : // the largest integer-coordinate rectangle contained by the unrounded result.
482 : void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); }
483 : // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
484 : // that the result is the largest integer-coordinate rectangle contained by the
485 : // unrounded result.
486 28 : void ScaleRoundIn(double aXScale, double aYScale)
487 : {
488 28 : T right = static_cast<T>(floor(double(XMost()) * aXScale));
489 28 : T bottom = static_cast<T>(floor(double(YMost()) * aYScale));
490 28 : x = static_cast<T>(ceil(double(x) * aXScale));
491 28 : y = static_cast<T>(ceil(double(y) * aYScale));
492 28 : width = std::max<T>(0, right - x);
493 28 : height = std::max<T>(0, bottom - y);
494 28 : }
495 : // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
496 : // the smallest integer-coordinate rectangle containing the unrounded result.
497 : // Note: this can turn an empty rectangle into a non-empty rectangle
498 4 : void ScaleInverseRoundOut(double aScale) { ScaleInverseRoundOut(aScale, aScale); }
499 : // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
500 : // that the result is the smallest integer-coordinate rectangle containing the
501 : // unrounded result.
502 : // Note: this can turn an empty rectangle into a non-empty rectangle
503 221 : void ScaleInverseRoundOut(double aXScale, double aYScale)
504 : {
505 221 : T right = static_cast<T>(ceil(double(XMost()) / aXScale));
506 221 : T bottom = static_cast<T>(ceil(double(YMost()) / aYScale));
507 221 : x = static_cast<T>(floor(double(x) / aXScale));
508 221 : y = static_cast<T>(floor(double(y) / aYScale));
509 221 : width = right - x;
510 221 : height = bottom - y;
511 221 : }
512 : // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
513 : // the largest integer-coordinate rectangle contained by the unrounded result.
514 25 : void ScaleInverseRoundIn(double aScale) { ScaleInverseRoundIn(aScale, aScale); }
515 : // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
516 : // that the result is the largest integer-coordinate rectangle contained by the
517 : // unrounded result.
518 25 : void ScaleInverseRoundIn(double aXScale, double aYScale)
519 : {
520 25 : T right = static_cast<T>(floor(double(XMost()) / aXScale));
521 25 : T bottom = static_cast<T>(floor(double(YMost()) / aYScale));
522 25 : x = static_cast<T>(ceil(double(x) / aXScale));
523 25 : y = static_cast<T>(ceil(double(y) / aYScale));
524 25 : width = std::max<T>(0, right - x);
525 25 : height = std::max<T>(0, bottom - y);
526 25 : }
527 :
528 : /**
529 : * Clamp aPoint to this rectangle. It is allowed to end up on any
530 : * edge of the rectangle.
531 : */
532 576 : MOZ_MUST_USE Point ClampPoint(const Point& aPoint) const
533 : {
534 1152 : return Point(std::max(x, std::min(XMost(), aPoint.x)),
535 1728 : std::max(y, std::min(YMost(), aPoint.y)));
536 : }
537 :
538 : /**
539 : * Translate this rectangle to be inside aRect. If it doesn't fit inside
540 : * aRect then the dimensions that don't fit will be shrunk so that they
541 : * do fit. The resulting rect is returned.
542 : */
543 39 : MOZ_MUST_USE Sub MoveInsideAndClamp(const Sub& aRect) const
544 : {
545 39 : Sub rect(std::max(aRect.x, x),
546 39 : std::max(aRect.y, y),
547 39 : std::min(aRect.width, width),
548 156 : std::min(aRect.height, height));
549 39 : rect.x = std::min(rect.XMost(), aRect.XMost()) - rect.width;
550 39 : rect.y = std::min(rect.YMost(), aRect.YMost()) - rect.height;
551 39 : return rect;
552 : }
553 :
554 : // Returns the largest rectangle that can be represented with 32-bit
555 : // signed integers, centered around a point at 0,0. As BaseRect's represent
556 : // the dimensions as a top-left point with a width and height, the width
557 : // and height will be the largest positive 32-bit value. The top-left
558 : // position coordinate is divided by two to center the rectangle around a
559 : // point at 0,0.
560 560 : static Sub MaxIntRect()
561 : {
562 75 : return Sub(
563 560 : static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
564 560 : static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
565 535 : static_cast<T>(std::numeric_limits<int32_t>::max()),
566 535 : static_cast<T>(std::numeric_limits<int32_t>::max())
567 2165 : );
568 : };
569 :
570 : // Returns a point representing the distance, along each dimension, of the
571 : // given point from this rectangle. The distance along a dimension is defined
572 : // as zero if the point is within the bounds of the rectangle in that
573 : // dimension; otherwise, it's the distance to the closer endpoint of the
574 : // rectangle in that dimension.
575 0 : Point DistanceTo(const Point& aPoint) const
576 : {
577 0 : return {DistanceFromInterval(aPoint.x, x, XMost()),
578 0 : DistanceFromInterval(aPoint.y, y, YMost())};
579 : }
580 :
581 0 : friend std::ostream& operator<<(std::ostream& stream,
582 : const BaseRect<T, Sub, Point, SizeT, MarginT>& aRect) {
583 0 : return stream << '(' << aRect.x << ',' << aRect.y << ','
584 0 : << aRect.width << ',' << aRect.height << ')';
585 : }
586 :
587 : private:
588 : // Do not use the default operator== or operator!= !
589 : // Use IsEqualEdges or IsEqualInterior explicitly.
590 : bool operator==(const Sub& aRect) const { return false; }
591 : bool operator!=(const Sub& aRect) const { return false; }
592 :
593 : // Helper function for DistanceTo() that computes the distance of a
594 : // coordinate along one dimension from an interval in that dimension.
595 0 : static T DistanceFromInterval(T aCoord, T aIntervalStart, T aIntervalEnd)
596 : {
597 0 : if (aCoord < aIntervalStart) {
598 0 : return aIntervalStart - aCoord;
599 : }
600 0 : if (aCoord > aIntervalEnd) {
601 0 : return aCoord - aIntervalEnd;
602 : }
603 0 : return 0;
604 : }
605 : };
606 :
607 : } // namespace gfx
608 : } // namespace mozilla
609 :
610 : #endif /* MOZILLA_GFX_BASERECT_H_ */
|