1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 //==============================================================================
30 /**
31     Manages a rectangle and allows geometric operations to be performed on it.
32 
33     @see RectangleList, Path, Line, Point
34 
35     @tags{Graphics}
36 */
37 template <typename ValueType>
38 class Rectangle
39 {
40 public:
41     //==============================================================================
42     /** Creates a rectangle of zero size.
43         The default coordinates will be (0, 0, 0, 0).
44     */
45     Rectangle() = default;
46 
47     /** Creates a copy of another rectangle. */
48     Rectangle (const Rectangle&) = default;
49 
50     /** Creates a rectangle with a given position and size. */
Rectangle(ValueType initialX,ValueType initialY,ValueType width,ValueType height)51     Rectangle (ValueType initialX, ValueType initialY,
52                ValueType width, ValueType height) noexcept
53       : pos (initialX, initialY),
54         w (width), h (height)
55     {
56     }
57 
58     /** Creates a rectangle with a given size, and a position of (0, 0). */
Rectangle(ValueType width,ValueType height)59     Rectangle (ValueType width, ValueType height) noexcept
60       : w (width), h (height)
61     {
62     }
63 
64     /** Creates a Rectangle from the positions of two opposite corners. */
Rectangle(Point<ValueType> corner1,Point<ValueType> corner2)65     Rectangle (Point<ValueType> corner1, Point<ValueType> corner2) noexcept
66       : pos (jmin (corner1.x, corner2.x),
67              jmin (corner1.y, corner2.y)),
68         w (corner1.x - corner2.x),
69         h (corner1.y - corner2.y)
70     {
71         if (w < ValueType()) w = -w;
72         if (h < ValueType()) h = -h;
73     }
74 
75     /** Creates a Rectangle from a set of left, right, top, bottom coordinates.
76         The right and bottom values must be larger than the left and top ones, or the resulting
77         rectangle will have a negative size.
78     */
leftTopRightBottom(ValueType left,ValueType top,ValueType right,ValueType bottom)79     static Rectangle leftTopRightBottom (ValueType left, ValueType top,
80                                          ValueType right, ValueType bottom) noexcept
81     {
82         return { left, top, right - left, bottom - top };
83     }
84 
85     /** Creates a copy of another rectangle. */
86     Rectangle& operator= (const Rectangle&) = default;
87 
88     /** Destructor. */
89     ~Rectangle() = default;
90 
91     //==============================================================================
92     /** Returns true if the rectangle's width or height are zero or less */
isEmpty()93     bool isEmpty() const noexcept                                   { return w <= ValueType() || h <= ValueType(); }
94 
95     /** Returns true if the rectangle's values are all finite numbers, i.e. not NaN or infinity. */
isFinite()96     inline bool isFinite() const noexcept                           { return pos.isFinite() && juce_isfinite (w) && juce_isfinite (h); }
97 
98     /** Returns the x coordinate of the rectangle's left-hand-side. */
getX()99     inline ValueType getX() const noexcept                          { return pos.x; }
100 
101     /** Returns the y coordinate of the rectangle's top edge. */
getY()102     inline ValueType getY() const noexcept                          { return pos.y; }
103 
104     /** Returns the width of the rectangle. */
getWidth()105     inline ValueType getWidth() const noexcept                      { return w; }
106 
107     /** Returns the height of the rectangle. */
getHeight()108     inline ValueType getHeight() const noexcept                     { return h; }
109 
110     /** Returns the x coordinate of the rectangle's right-hand-side. */
getRight()111     inline ValueType getRight() const noexcept                      { return pos.x + w; }
112 
113     /** Returns the y coordinate of the rectangle's bottom edge. */
getBottom()114     inline ValueType getBottom() const noexcept                     { return pos.y + h; }
115 
116     /** Returns the x coordinate of the rectangle's centre. */
getCentreX()117     ValueType getCentreX() const noexcept                           { return pos.x + w / (ValueType) 2; }
118 
119     /** Returns the y coordinate of the rectangle's centre. */
getCentreY()120     ValueType getCentreY() const noexcept                           { return pos.y + h / (ValueType) 2; }
121 
122     /** Returns the centre point of the rectangle. */
getCentre()123     Point<ValueType> getCentre() const noexcept                     { return { pos.x + w / (ValueType) 2,
124                                                                                pos.y + h / (ValueType) 2 }; }
125 
126     /** Returns the aspect ratio of the rectangle's width / height.
127         If widthOverHeight is true, it returns width / height; if widthOverHeight is false,
128         it returns height / width. */
129     ValueType getAspectRatio (bool widthOverHeight = true) const noexcept                           { return widthOverHeight ? w / h : h / w; }
130 
131     //==============================================================================
132     /** Returns the rectangle's top-left position as a Point. */
getPosition()133     inline Point<ValueType> getPosition() const noexcept                                            { return pos; }
134 
135     /** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */
setPosition(Point<ValueType> newPos)136     inline void setPosition (Point<ValueType> newPos) noexcept                                      { pos = newPos; }
137 
138     /** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */
setPosition(ValueType newX,ValueType newY)139     inline void setPosition (ValueType newX, ValueType newY) noexcept                               { pos.setXY (newX, newY); }
140 
141     /** Returns the rectangle's top-left position as a Point. */
getTopLeft()142     Point<ValueType> getTopLeft() const noexcept                                                    { return pos; }
143 
144     /** Returns the rectangle's top-right position as a Point. */
getTopRight()145     Point<ValueType> getTopRight() const noexcept                                                   { return { pos.x + w, pos.y }; }
146 
147     /** Returns the rectangle's bottom-left position as a Point. */
getBottomLeft()148     Point<ValueType> getBottomLeft() const noexcept                                                 { return { pos.x, pos.y + h }; }
149 
150     /** Returns the rectangle's bottom-right position as a Point. */
getBottomRight()151     Point<ValueType> getBottomRight() const noexcept                                                { return { pos.x + w, pos.y + h }; }
152 
153     /** Returns the rectangle's left and right positions as a Range. */
getHorizontalRange()154     Range<ValueType> getHorizontalRange() const noexcept                                            { return Range<ValueType>::withStartAndLength (pos.x, w); }
155 
156     /** Returns the rectangle's top and bottom positions as a Range. */
getVerticalRange()157     Range<ValueType> getVerticalRange() const noexcept                                              { return Range<ValueType>::withStartAndLength (pos.y, h); }
158 
159     /** Changes the rectangle's size, leaving the position of its top-left corner unchanged. */
setSize(ValueType newWidth,ValueType newHeight)160     void setSize (ValueType newWidth, ValueType newHeight) noexcept                                 { w = newWidth; h = newHeight; }
161 
162     /** Changes all the rectangle's coordinates. */
setBounds(ValueType newX,ValueType newY,ValueType newWidth,ValueType newHeight)163     void setBounds (ValueType newX, ValueType newY,
164                     ValueType newWidth, ValueType newHeight) noexcept                               { pos.x = newX; pos.y = newY; w = newWidth; h = newHeight; }
165 
166     /** Changes the rectangle's X coordinate */
setX(ValueType newX)167     inline void setX (ValueType newX) noexcept                                                      { pos.x = newX; }
168 
169     /** Changes the rectangle's Y coordinate */
setY(ValueType newY)170     inline void setY (ValueType newY) noexcept                                                      { pos.y = newY; }
171 
172     /** Changes the rectangle's width */
setWidth(ValueType newWidth)173     inline void setWidth (ValueType newWidth) noexcept                                              { w = newWidth; }
174 
175     /** Changes the rectangle's height */
setHeight(ValueType newHeight)176     inline void setHeight (ValueType newHeight) noexcept                                            { h = newHeight; }
177 
178     /** Changes the position of the rectangle's centre (leaving its size unchanged). */
setCentre(ValueType newCentreX,ValueType newCentreY)179     inline void setCentre (ValueType newCentreX, ValueType newCentreY) noexcept                     { pos.x = newCentreX - w / (ValueType) 2;
180                                                                                                       pos.y = newCentreY - h / (ValueType) 2; }
181 
182     /** Changes the position of the rectangle's centre (leaving its size unchanged). */
setCentre(Point<ValueType> newCentre)183     inline void setCentre (Point<ValueType> newCentre) noexcept                                     { setCentre (newCentre.x, newCentre.y); }
184 
185     /** Changes the position of the rectangle's left and right edges. */
setHorizontalRange(Range<ValueType> range)186     void setHorizontalRange (Range<ValueType> range) noexcept                                       { pos.x = range.getStart(); w = range.getLength(); }
187 
188     /** Changes the position of the rectangle's top and bottom edges. */
setVerticalRange(Range<ValueType> range)189     void setVerticalRange (Range<ValueType> range) noexcept                                         { pos.y = range.getStart(); h = range.getLength(); }
190 
191     /** Returns a rectangle which has the same size and y-position as this one, but with a different x-position. */
withX(ValueType newX)192     Rectangle withX (ValueType newX) const noexcept                                                 { return { newX, pos.y, w, h }; }
193 
194     /** Returns a rectangle which has the same size and x-position as this one, but with a different y-position. */
withY(ValueType newY)195     Rectangle withY (ValueType newY) const noexcept                                                 { return { pos.x, newY, w, h }; }
196 
197     /** Returns a rectangle which has the same size and y-position as this one, but whose right-hand edge has the given position. */
withRightX(ValueType newRightX)198     Rectangle withRightX (ValueType newRightX) const noexcept                                       { return { newRightX - w, pos.y, w, h }; }
199 
200     /** Returns a rectangle which has the same size and x-position as this one, but whose bottom edge has the given position. */
withBottomY(ValueType newBottomY)201     Rectangle withBottomY (ValueType newBottomY) const noexcept                                     { return { pos.x, newBottomY - h, w, h }; }
202 
203     /** Returns a rectangle with the same size as this one, but a new position. */
withPosition(ValueType newX,ValueType newY)204     Rectangle withPosition (ValueType newX, ValueType newY) const noexcept                          { return { newX, newY, w, h }; }
205 
206     /** Returns a rectangle with the same size as this one, but a new position. */
withPosition(Point<ValueType> newPos)207     Rectangle withPosition (Point<ValueType> newPos) const noexcept                                 { return { newPos.x, newPos.y, w, h }; }
208 
209     /** Returns a rectangle whose size is the same as this one, but whose top-left position is (0, 0). */
withZeroOrigin()210     Rectangle withZeroOrigin() const noexcept                                                       { return { w, h }; }
211 
212     /** Returns a rectangle with the same size as this one, but a new centre position. */
withCentre(Point<ValueType> newCentre)213     Rectangle withCentre (Point<ValueType> newCentre) const noexcept                                { return { newCentre.x - w / (ValueType) 2,
214                                                                                                                newCentre.y - h / (ValueType) 2, w, h }; }
215 
216     /** Returns a rectangle which has the same position and height as this one, but with a different width. */
withWidth(ValueType newWidth)217     Rectangle withWidth (ValueType newWidth) const noexcept                                         { return { pos.x, pos.y, jmax (ValueType(), newWidth), h }; }
218 
219     /** Returns a rectangle which has the same position and width as this one, but with a different height. */
withHeight(ValueType newHeight)220     Rectangle withHeight (ValueType newHeight) const noexcept                                       { return { pos.x, pos.y, w, jmax (ValueType(), newHeight) }; }
221 
222     /** Returns a rectangle with the same top-left position as this one, but a new size. */
withSize(ValueType newWidth,ValueType newHeight)223     Rectangle withSize (ValueType newWidth, ValueType newHeight) const noexcept                     { return { pos.x, pos.y, jmax (ValueType(), newWidth), jmax (ValueType(), newHeight) }; }
224 
225     /** Returns a rectangle with the same centre position as this one, but a new size. */
withSizeKeepingCentre(ValueType newWidth,ValueType newHeight)226     Rectangle withSizeKeepingCentre (ValueType newWidth, ValueType newHeight) const noexcept        { return { pos.x + (w - newWidth)  / (ValueType) 2,
227                                                                                                                pos.y + (h - newHeight) / (ValueType) 2, newWidth, newHeight }; }
228 
229     /** Moves the x position, adjusting the width so that the right-hand edge remains in the same place.
230         If the x is moved to be on the right of the current right-hand edge, the width will be set to zero.
231         @see withLeft
232     */
setLeft(ValueType newLeft)233     void setLeft (ValueType newLeft) noexcept                   { w = jmax (ValueType(), pos.x + w - newLeft); pos.x = newLeft; }
234 
235     /** Returns a new rectangle with a different x position, but the same right-hand edge as this one.
236         If the new x is beyond the right of the current right-hand edge, the width will be set to zero.
237         @see setLeft
238     */
withLeft(ValueType newLeft)239     Rectangle withLeft (ValueType newLeft) const noexcept       { return { newLeft, pos.y, jmax (ValueType(), pos.x + w - newLeft), h }; }
240 
241     /** Moves the y position, adjusting the height so that the bottom edge remains in the same place.
242         If the y is moved to be below the current bottom edge, the height will be set to zero.
243         @see withTop
244     */
setTop(ValueType newTop)245     void setTop (ValueType newTop) noexcept                     { h = jmax (ValueType(), pos.y + h - newTop); pos.y = newTop; }
246 
247     /** Returns a new rectangle with a different y position, but the same bottom edge as this one.
248         If the new y is beyond the bottom of the current rectangle, the height will be set to zero.
249         @see setTop
250     */
withTop(ValueType newTop)251     Rectangle withTop (ValueType newTop) const noexcept         { return { pos.x, newTop, w, jmax (ValueType(), pos.y + h - newTop) }; }
252 
253     /** Adjusts the width so that the right-hand edge of the rectangle has this new value.
254         If the new right is below the current X value, the X will be pushed down to match it.
255         @see getRight, withRight
256     */
setRight(ValueType newRight)257     void setRight (ValueType newRight) noexcept                 { pos.x = jmin (pos.x, newRight); w = newRight - pos.x; }
258 
259     /** Returns a new rectangle with a different right-hand edge position, but the same left-hand edge as this one.
260         If the new right edge is below the current left-hand edge, the width will be set to zero.
261         @see setRight
262     */
withRight(ValueType newRight)263     Rectangle withRight (ValueType newRight) const noexcept     { return { jmin (pos.x, newRight), pos.y, jmax (ValueType(), newRight - pos.x), h }; }
264 
265     /** Adjusts the height so that the bottom edge of the rectangle has this new value.
266         If the new bottom is lower than the current Y value, the Y will be pushed down to match it.
267         @see getBottom, withBottom
268     */
setBottom(ValueType newBottom)269     void setBottom (ValueType newBottom) noexcept               { pos.y = jmin (pos.y, newBottom); h = newBottom - pos.y; }
270 
271     /** Returns a new rectangle with a different bottom edge position, but the same top edge as this one.
272         If the new y is beyond the bottom of the current rectangle, the height will be set to zero.
273         @see setBottom
274     */
withBottom(ValueType newBottom)275     Rectangle withBottom (ValueType newBottom) const noexcept   { return { pos.x, jmin (pos.y, newBottom), w, jmax (ValueType(), newBottom - pos.y) }; }
276 
277     /** Returns a version of this rectangle with the given amount removed from its left-hand edge. */
withTrimmedLeft(ValueType amountToRemove)278     Rectangle withTrimmedLeft (ValueType amountToRemove) const noexcept     { return withLeft (pos.x + amountToRemove); }
279 
280     /** Returns a version of this rectangle with the given amount removed from its right-hand edge. */
withTrimmedRight(ValueType amountToRemove)281     Rectangle withTrimmedRight (ValueType amountToRemove) const noexcept    { return withWidth (w - amountToRemove); }
282 
283     /** Returns a version of this rectangle with the given amount removed from its top edge. */
withTrimmedTop(ValueType amountToRemove)284     Rectangle withTrimmedTop (ValueType amountToRemove) const noexcept      { return withTop (pos.y + amountToRemove); }
285 
286     /** Returns a version of this rectangle with the given amount removed from its bottom edge. */
withTrimmedBottom(ValueType amountToRemove)287     Rectangle withTrimmedBottom (ValueType amountToRemove) const noexcept   { return withHeight (h - amountToRemove); }
288 
289     //==============================================================================
290     /** Moves the rectangle's position by adding amount to its x and y coordinates. */
translate(ValueType deltaX,ValueType deltaY)291     void translate (ValueType deltaX,
292                     ValueType deltaY) noexcept
293     {
294         pos.x += deltaX;
295         pos.y += deltaY;
296     }
297 
298     /** Returns a rectangle which is the same as this one moved by a given amount. */
translated(ValueType deltaX,ValueType deltaY)299     Rectangle translated (ValueType deltaX,
300                           ValueType deltaY) const noexcept
301     {
302         return { pos.x + deltaX, pos.y + deltaY, w, h };
303     }
304 
305     /** Returns a rectangle which is the same as this one moved by a given amount. */
306     Rectangle operator+ (Point<ValueType> deltaPosition) const noexcept
307     {
308         return { pos.x + deltaPosition.x, pos.y + deltaPosition.y, w, h };
309     }
310 
311     /** Moves this rectangle by a given amount. */
312     Rectangle& operator+= (Point<ValueType> deltaPosition) noexcept
313     {
314         pos += deltaPosition;
315         return *this;
316     }
317 
318     /** Returns a rectangle which is the same as this one moved by a given amount. */
319     Rectangle operator- (Point<ValueType> deltaPosition) const noexcept
320     {
321         return { pos.x - deltaPosition.x, pos.y - deltaPosition.y, w, h };
322     }
323 
324     /** Moves this rectangle by a given amount. */
325     Rectangle& operator-= (Point<ValueType> deltaPosition) noexcept
326     {
327         pos -= deltaPosition;
328         return *this;
329     }
330 
331     /** Returns a rectangle that has been scaled by the given amount, centred around the origin.
332         Note that if the rectangle has int coordinates and it's scaled by a
333         floating-point amount, then the result will be converted back to integer
334         coordinates using getSmallestIntegerContainer().
335     */
336     template <typename FloatType>
337     Rectangle operator* (FloatType scaleFactor) const noexcept
338     {
339         Rectangle r (*this);
340         r *= scaleFactor;
341         return r;
342     }
343 
344     /** Scales this rectangle by the given amount, centred around the origin.
345         Note that if the rectangle has int coordinates and it's scaled by a
346         floating-point amount, then the result will be converted back to integer
347         coordinates using getSmallestIntegerContainer().
348     */
349     template <typename FloatType>
350     Rectangle operator*= (FloatType scaleFactor) noexcept
351     {
352         Rectangle<FloatType> ((FloatType) pos.x * scaleFactor,
353                               (FloatType) pos.y * scaleFactor,
354                               (FloatType) w * scaleFactor,
355                               (FloatType) h * scaleFactor).copyWithRounding (*this);
356         return *this;
357     }
358 
359     /** Scales this rectangle by the given X and Y factors, centred around the origin.
360         Note that if the rectangle has int coordinates and it's scaled by a
361         floating-point amount, then the result will be converted back to integer
362         coordinates using getSmallestIntegerContainer().
363     */
364     template <typename FloatType>
365     Rectangle operator*= (Point<FloatType> scaleFactor) noexcept
366     {
367         Rectangle<FloatType> ((FloatType) pos.x * scaleFactor.x,
368                               (FloatType) pos.y * scaleFactor.y,
369                               (FloatType) w * scaleFactor.x,
370                               (FloatType) h * scaleFactor.y).copyWithRounding (*this);
371         return *this;
372     }
373 
374     /** Scales this rectangle by the given amount, centred around the origin. */
375     template <typename FloatType>
376     Rectangle operator/ (FloatType scaleFactor) const noexcept
377     {
378         Rectangle r (*this);
379         r /= scaleFactor;
380         return r;
381     }
382 
383     /** Scales this rectangle by the given amount, centred around the origin. */
384     template <typename FloatType>
385     Rectangle operator/= (FloatType scaleFactor) noexcept
386     {
387         Rectangle<FloatType> ((FloatType) pos.x / scaleFactor,
388                               (FloatType) pos.y / scaleFactor,
389                               (FloatType) w / scaleFactor,
390                               (FloatType) h / scaleFactor).copyWithRounding (*this);
391         return *this;
392     }
393 
394     /** Scales this rectangle by the given X and Y factors, centred around the origin. */
395     template <typename FloatType>
396     Rectangle operator/= (Point<FloatType> scaleFactor) noexcept
397     {
398         Rectangle<FloatType> ((FloatType) pos.x / scaleFactor.x,
399                               (FloatType) pos.y / scaleFactor.y,
400                               (FloatType) w / scaleFactor.x,
401                               (FloatType) h / scaleFactor.y).copyWithRounding (*this);
402         return *this;
403     }
404 
405     /** Expands the rectangle by a given amount.
406 
407         Effectively, its new size is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2).
408         @see expanded, reduce, reduced
409     */
expand(ValueType deltaX,ValueType deltaY)410     void expand (ValueType deltaX,
411                  ValueType deltaY) noexcept
412     {
413         auto nw = jmax (ValueType(), w + deltaX * 2);
414         auto nh = jmax (ValueType(), h + deltaY * 2);
415         setBounds (pos.x - deltaX, pos.y - deltaY, nw, nh);
416     }
417 
418     /** Returns a rectangle that is larger than this one by a given amount.
419 
420         Effectively, the rectangle returned is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2).
421         @see expand, reduce, reduced
422     */
expanded(ValueType deltaX,ValueType deltaY)423     Rectangle expanded (ValueType deltaX,
424                         ValueType deltaY) const noexcept
425     {
426         auto nw = jmax (ValueType(), w + deltaX * 2);
427         auto nh = jmax (ValueType(), h + deltaY * 2);
428         return { pos.x - deltaX, pos.y - deltaY, nw, nh };
429     }
430 
431     /** Returns a rectangle that is larger than this one by a given amount.
432 
433         Effectively, the rectangle returned is (x - delta, y - delta, w + delta * 2, h + delta * 2).
434         @see expand, reduce, reduced
435     */
expanded(ValueType delta)436     Rectangle expanded (ValueType delta) const noexcept
437     {
438         return expanded (delta, delta);
439     }
440 
441     /** Shrinks the rectangle by a given amount.
442 
443         Effectively, its new size is (x + deltaX, y + deltaY, w - deltaX * 2, h - deltaY * 2).
444         @see reduced, expand, expanded
445     */
reduce(ValueType deltaX,ValueType deltaY)446     void reduce (ValueType deltaX,
447                  ValueType deltaY) noexcept
448     {
449         expand (-deltaX, -deltaY);
450     }
451 
452     /** Returns a rectangle that is smaller than this one by a given amount.
453 
454         Effectively, the rectangle returned is (x + deltaX, y + deltaY, w - deltaX * 2, h - deltaY * 2).
455         @see reduce, expand, expanded
456     */
reduced(ValueType deltaX,ValueType deltaY)457     Rectangle reduced (ValueType deltaX,
458                        ValueType deltaY) const noexcept
459     {
460         return expanded (-deltaX, -deltaY);
461     }
462 
463     /** Returns a rectangle that is smaller than this one by a given amount.
464 
465         Effectively, the rectangle returned is (x + delta, y + delta, w - delta * 2, h - delta * 2).
466         @see reduce, expand, expanded
467     */
reduced(ValueType delta)468     Rectangle reduced (ValueType delta) const noexcept
469     {
470         return reduced (delta, delta);
471     }
472 
473     /** Removes a strip from the top of this rectangle, reducing this rectangle
474         by the specified amount and returning the section that was removed.
475 
476         E.g. if this rectangle is (100, 100, 300, 300) and amountToRemove is 50, this will
477         return (100, 100, 300, 50) and leave this rectangle as (100, 150, 300, 250).
478 
479         If amountToRemove is greater than the height of this rectangle, it'll be clipped to
480         that value.
481     */
removeFromTop(ValueType amountToRemove)482     Rectangle removeFromTop (ValueType amountToRemove) noexcept
483     {
484         const Rectangle r (pos.x, pos.y, w, jmin (amountToRemove, h));
485         pos.y += r.h; h -= r.h;
486         return r;
487     }
488 
489     /** Removes a strip from the left-hand edge of this rectangle, reducing this rectangle
490         by the specified amount and returning the section that was removed.
491 
492         E.g. if this rectangle is (100, 100, 300, 300) and amountToRemove is 50, this will
493         return (100, 100, 50, 300) and leave this rectangle as (150, 100, 250, 300).
494 
495         If amountToRemove is greater than the width of this rectangle, it'll be clipped to
496         that value.
497     */
removeFromLeft(ValueType amountToRemove)498     Rectangle removeFromLeft (ValueType amountToRemove) noexcept
499     {
500         const Rectangle r (pos.x, pos.y, jmin (amountToRemove, w), h);
501         pos.x += r.w; w -= r.w;
502         return r;
503     }
504 
505     /** Removes a strip from the right-hand edge of this rectangle, reducing this rectangle
506         by the specified amount and returning the section that was removed.
507 
508         E.g. if this rectangle is (100, 100, 300, 300) and amountToRemove is 50, this will
509         return (350, 100, 50, 300) and leave this rectangle as (100, 100, 250, 300).
510 
511         If amountToRemove is greater than the width of this rectangle, it'll be clipped to
512         that value.
513     */
removeFromRight(ValueType amountToRemove)514     Rectangle removeFromRight (ValueType amountToRemove) noexcept
515     {
516         amountToRemove = jmin (amountToRemove, w);
517         const Rectangle r (pos.x + w - amountToRemove, pos.y, amountToRemove, h);
518         w -= amountToRemove;
519         return r;
520     }
521 
522     /** Removes a strip from the bottom of this rectangle, reducing this rectangle
523         by the specified amount and returning the section that was removed.
524 
525         E.g. if this rectangle is (100, 100, 300, 300) and amountToRemove is 50, this will
526         return (100, 350, 300, 50) and leave this rectangle as (100, 100, 300, 250).
527 
528         If amountToRemove is greater than the height of this rectangle, it'll be clipped to
529         that value.
530     */
removeFromBottom(ValueType amountToRemove)531     Rectangle removeFromBottom (ValueType amountToRemove) noexcept
532     {
533         amountToRemove = jmin (amountToRemove, h);
534         const Rectangle r (pos.x, pos.y + h - amountToRemove, w, amountToRemove);
535         h -= amountToRemove;
536         return r;
537     }
538 
539     //==============================================================================
540     /** Returns the nearest point to the specified point that lies within this rectangle. */
getConstrainedPoint(Point<ValueType> point)541     Point<ValueType> getConstrainedPoint (Point<ValueType> point) const noexcept
542     {
543         return { jlimit (pos.x, pos.x + w, point.x),
544                  jlimit (pos.y, pos.y + h, point.y) };
545     }
546 
547     /** Returns a point within this rectangle, specified as proportional coordinates.
548         The relative X and Y values should be between 0 and 1, where 0 is the left or
549         top of this rectangle, and 1 is the right or bottom. (Out-of-bounds values
550         will return a point outside the rectangle).
551     */
552     template <typename FloatType>
getRelativePoint(FloatType relativeX,FloatType relativeY)553     Point<ValueType> getRelativePoint (FloatType relativeX, FloatType relativeY) const noexcept
554     {
555         return { pos.x + static_cast<ValueType> ((FloatType) w * relativeX),
556                  pos.y + static_cast<ValueType> ((FloatType) h * relativeY) };
557     }
558 
559     /** Returns a proportion of the width of this rectangle. */
560     template <typename FloatType>
proportionOfWidth(FloatType proportion)561     ValueType proportionOfWidth (FloatType proportion) const noexcept
562     {
563         return static_cast<ValueType> ((FloatType) w * proportion);
564     }
565 
566     /** Returns a proportion of the height of this rectangle. */
567     template <typename FloatType>
proportionOfHeight(FloatType proportion)568     ValueType proportionOfHeight (FloatType proportion) const noexcept
569     {
570         return static_cast<ValueType> ((FloatType) h * proportion);
571     }
572 
573     /** Returns a rectangle based on some proportional coordinates relative to this one.
574         So for example getProportion ({ 0.25f, 0.25f, 0.5f, 0.5f }) would return a rectangle
575         of half the original size, with the same centre.
576     */
577     template <typename FloatType>
getProportion(Rectangle<FloatType> proportionalRect)578     Rectangle getProportion (Rectangle<FloatType> proportionalRect) const noexcept
579     {
580         return { pos.x + static_cast<ValueType> (w * proportionalRect.pos.x),
581                  pos.y + static_cast<ValueType> (h * proportionalRect.pos.y),
582                  proportionOfWidth  (proportionalRect.w),
583                  proportionOfHeight (proportionalRect.h) };
584     }
585 
586     //==============================================================================
587     /** Returns true if the two rectangles are identical. */
588     bool operator== (const Rectangle& other) const noexcept     { return pos == other.pos && w == other.w && h == other.h; }
589 
590     /** Returns true if the two rectangles are not identical. */
591     bool operator!= (const Rectangle& other) const noexcept     { return pos != other.pos || w != other.w || h != other.h; }
592 
593     /** Returns true if this coordinate is inside the rectangle. */
contains(ValueType xCoord,ValueType yCoord)594     bool contains (ValueType xCoord, ValueType yCoord) const noexcept
595     {
596         return xCoord >= pos.x && yCoord >= pos.y && xCoord < pos.x + w && yCoord < pos.y + h;
597     }
598 
599     /** Returns true if this coordinate is inside the rectangle. */
contains(Point<ValueType> point)600     bool contains (Point<ValueType> point) const noexcept
601     {
602         return point.x >= pos.x && point.y >= pos.y && point.x < pos.x + w && point.y < pos.y + h;
603     }
604 
605     /** Returns true if this other rectangle is completely inside this one. */
contains(Rectangle other)606     bool contains (Rectangle other) const noexcept
607     {
608         return pos.x <= other.pos.x && pos.y <= other.pos.y
609             && pos.x + w >= other.pos.x + other.w && pos.y + h >= other.pos.y + other.h;
610     }
611 
612     /** Returns true if any part of another rectangle overlaps this one. */
intersects(Rectangle other)613     bool intersects (Rectangle other) const noexcept
614     {
615         return pos.x + w > other.pos.x
616             && pos.y + h > other.pos.y
617             && pos.x < other.pos.x + other.w
618             && pos.y < other.pos.y + other.h
619             && w > ValueType() && h > ValueType()
620             && other.w > ValueType() && other.h > ValueType();
621     }
622 
623     /** Returns true if any part of the given line lies inside this rectangle. */
intersects(const Line<ValueType> & line)624     bool intersects (const Line<ValueType>& line) const noexcept
625     {
626         return contains (line.getStart()) || contains (line.getEnd())
627                 || line.intersects (Line<ValueType> (getTopLeft(),     getTopRight()))
628                 || line.intersects (Line<ValueType> (getTopRight(),    getBottomRight()))
629                 || line.intersects (Line<ValueType> (getBottomRight(), getBottomLeft()))
630                 || line.intersects (Line<ValueType> (getBottomLeft(),  getTopLeft()));
631     }
632 
633     /** Returns the region that is the overlap between this and another rectangle.
634         If the two rectangles don't overlap, the rectangle returned will be empty.
635     */
getIntersection(Rectangle other)636     Rectangle getIntersection (Rectangle other) const noexcept
637     {
638         auto nx = jmax (pos.x, other.pos.x);
639         auto ny = jmax (pos.y, other.pos.y);
640         auto nw = jmin (pos.x + w, other.pos.x + other.w) - nx;
641 
642         if (nw >= ValueType())
643         {
644             auto nh = jmin (pos.y + h, other.pos.y + other.h) - ny;
645 
646             if (nh >= ValueType())
647                 return { nx, ny, nw, nh };
648         }
649 
650         return {};
651     }
652 
653     /** Clips a set of rectangle coordinates so that they lie only within this one.
654         This is a non-static version of intersectRectangles().
655         Returns false if the two rectangles didn't overlap.
656     */
intersectRectangle(ValueType & otherX,ValueType & otherY,ValueType & otherW,ValueType & otherH)657     bool intersectRectangle (ValueType& otherX, ValueType& otherY, ValueType& otherW, ValueType& otherH) const noexcept
658     {
659         auto maxX = jmax (otherX, pos.x);
660         otherW = jmin (otherX + otherW, pos.x + w) - maxX;
661 
662         if (otherW > ValueType())
663         {
664             auto maxY = jmax (otherY, pos.y);
665             otherH = jmin (otherY + otherH, pos.y + h) - maxY;
666 
667             if (otherH > ValueType())
668             {
669                 otherX = maxX; otherY = maxY;
670                 return true;
671             }
672         }
673 
674         return false;
675     }
676 
677     /** Clips a rectangle so that it lies only within this one.
678         Returns false if the two rectangles didn't overlap.
679     */
intersectRectangle(Rectangle<ValueType> & rectangleToClip)680     bool intersectRectangle (Rectangle<ValueType>& rectangleToClip) const noexcept
681     {
682         return intersectRectangle (rectangleToClip.pos.x, rectangleToClip.pos.y,
683                                    rectangleToClip.w,     rectangleToClip.h);
684     }
685 
686     /** Returns the smallest rectangle that contains both this one and the one passed-in.
687 
688         If either this or the other rectangle are empty, they will not be counted as
689         part of the resulting region.
690     */
getUnion(Rectangle other)691     Rectangle getUnion (Rectangle other) const noexcept
692     {
693         if (other.isEmpty())  return *this;
694         if (isEmpty())        return other;
695 
696         auto newX = jmin (pos.x, other.pos.x);
697         auto newY = jmin (pos.y, other.pos.y);
698 
699         return { newX, newY,
700                  jmax (pos.x + w, other.pos.x + other.w) - newX,
701                  jmax (pos.y + h, other.pos.y + other.h) - newY };
702     }
703 
704     /** If this rectangle merged with another one results in a simple rectangle, this
705         will set this rectangle to the result, and return true.
706 
707         Returns false and does nothing to this rectangle if the two rectangles don't overlap,
708         or if they form a complex region.
709     */
enlargeIfAdjacent(Rectangle other)710     bool enlargeIfAdjacent (Rectangle other) noexcept
711     {
712         if (pos.x == other.pos.x && getRight() == other.getRight()
713              && (other.getBottom() >= pos.y && other.pos.y <= getBottom()))
714         {
715             auto newY = jmin (pos.y, other.pos.y);
716             h = jmax (getBottom(), other.getBottom()) - newY;
717             pos.y = newY;
718             return true;
719         }
720 
721         if (pos.y == other.pos.y && getBottom() == other.getBottom()
722              && (other.getRight() >= pos.x && other.pos.x <= getRight()))
723         {
724             auto newX = jmin (pos.x, other.pos.x);
725             w = jmax (getRight(), other.getRight()) - newX;
726             pos.x = newX;
727             return true;
728         }
729 
730         return false;
731     }
732 
733     /** If after removing another rectangle from this one the result is a simple rectangle,
734         this will set this object's bounds to be the result, and return true.
735 
736         Returns false and does nothing to this rectangle if the two rectangles don't overlap,
737         or if removing the other one would form a complex region.
738     */
reduceIfPartlyContainedIn(Rectangle other)739     bool reduceIfPartlyContainedIn (Rectangle other) noexcept
740     {
741         int inside = 0;
742         auto otherR = other.getRight();
743         if (pos.x >= other.pos.x && pos.x < otherR) inside = 1;
744         auto otherB = other.getBottom();
745         if (pos.y >= other.pos.y && pos.y < otherB) inside |= 2;
746         auto r = pos.x + w;
747         if (r >= other.pos.x && r < otherR) inside |= 4;
748         auto b = pos.y + h;
749         if (b >= other.pos.y && b < otherB) inside |= 8;
750 
751         switch (inside)
752         {
753             case 1 + 2 + 8:  w = r - otherR; pos.x = otherR; return true;
754             case 1 + 2 + 4:  h = b - otherB; pos.y = otherB; return true;
755             case 2 + 4 + 8:  w = other.pos.x - pos.x;        return true;
756             case 1 + 4 + 8:  h = other.pos.y - pos.y;        return true;
757             default:         break;
758         }
759 
760         return false;
761     }
762 
763     /** Tries to fit this rectangle within a target area, returning the result.
764 
765         If this rectangle is not completely inside the target area, then it'll be
766         shifted (without changing its size) so that it lies within the target. If it
767         is larger than the target rectangle in either dimension, then that dimension
768         will be reduced to fit within the target.
769     */
constrainedWithin(Rectangle areaToFitWithin)770     Rectangle constrainedWithin (Rectangle areaToFitWithin) const noexcept
771     {
772         auto newPos = areaToFitWithin.withSize (areaToFitWithin.getWidth() - w,
773                                                 areaToFitWithin.getHeight() - h)
774                         .getConstrainedPoint (pos);
775 
776         return { newPos.x, newPos.y,
777                  jmin (w, areaToFitWithin.getWidth()),
778                  jmin (h, areaToFitWithin.getHeight()) };
779     }
780 
781     /** Returns the smallest rectangle that can contain the shape created by applying
782         a transform to this rectangle.
783 
784         This should only be used on floating point rectangles.
785     */
transformedBy(const AffineTransform & transform)786     Rectangle transformedBy (const AffineTransform& transform) const noexcept
787     {
788         using FloatType = typename TypeHelpers::SmallestFloatType<ValueType>::type;
789 
790         auto x1 = static_cast<FloatType> (pos.x),     y1 = static_cast<FloatType> (pos.y);
791         auto x2 = static_cast<FloatType> (pos.x + w), y2 = static_cast<FloatType> (pos.y);
792         auto x3 = static_cast<FloatType> (pos.x),     y3 = static_cast<FloatType> (pos.y + h);
793         auto x4 = static_cast<FloatType> (x2),        y4 = static_cast<FloatType> (y3);
794 
795         transform.transformPoints (x1, y1, x2, y2);
796         transform.transformPoints (x3, y3, x4, y4);
797 
798         auto rx1 = jmin (x1, x2, x3, x4);
799         auto rx2 = jmax (x1, x2, x3, x4);
800         auto ry1 = jmin (y1, y2, y3, y4);
801         auto ry2 = jmax (y1, y2, y3, y4);
802 
803         Rectangle r;
804         Rectangle<FloatType> (rx1, ry1, rx2 - rx1, ry2 - ry1).copyWithRounding (r);
805         return r;
806     }
807 
808     /** Returns the smallest integer-aligned rectangle that completely contains this one.
809         This is only relevant for floating-point rectangles, of course.
810         @see toFloat(), toNearestInt(), toNearestIntEdges()
811     */
getSmallestIntegerContainer()812     Rectangle<int> getSmallestIntegerContainer() const noexcept
813     {
814         return Rectangle<int>::leftTopRightBottom (floorAsInt (pos.x),
815                                                    floorAsInt (pos.y),
816                                                    ceilAsInt  (pos.x + w),
817                                                    ceilAsInt  (pos.y + h));
818     }
819 
820     /** Casts this rectangle to a Rectangle<int>.
821         This uses roundToInt to snap x, y, width and height to the nearest integer (losing precision).
822         If the rectangle already uses integers, this will simply return a copy.
823         @see getSmallestIntegerContainer(), toNearestIntEdges()
824     */
toNearestInt()825     Rectangle<int> toNearestInt() const noexcept
826     {
827         return { roundToInt (pos.x), roundToInt (pos.y),
828                  roundToInt (w),     roundToInt (h) };
829     }
830 
831     /** Casts this rectangle to a Rectangle<int>.
832         This uses roundToInt to snap top, left, right and bottom to the nearest integer (losing precision).
833         If the rectangle already uses integers, this will simply return a copy.
834         @see getSmallestIntegerContainer(), toNearestInt()
835     */
toNearestIntEdges()836     Rectangle<int> toNearestIntEdges() const noexcept
837     {
838         return Rectangle<int>::leftTopRightBottom (roundToInt (pos.x),       roundToInt (pos.y),
839                                                    roundToInt (getRight()),  roundToInt (getBottom()));
840     }
841 
842     /** Casts this rectangle to a Rectangle<float>.
843         @see getSmallestIntegerContainer
844     */
toFloat()845     Rectangle<float> toFloat() const noexcept
846     {
847         return { static_cast<float> (pos.x), static_cast<float> (pos.y),
848                  static_cast<float> (w),     static_cast<float> (h) };
849     }
850 
851     /** Casts this rectangle to a Rectangle<double>.
852         @see getSmallestIntegerContainer
853     */
toDouble()854     Rectangle<double> toDouble() const noexcept
855     {
856         return { static_cast<double> (pos.x), static_cast<double> (pos.y),
857                  static_cast<double> (w),     static_cast<double> (h) };
858     }
859 
860     /** Casts this rectangle to a Rectangle with the given type.
861         If the target type is a conversion from float to int, then the conversion
862         will be done using getSmallestIntegerContainer().
863     */
864     template <typename TargetType>
toType()865     Rectangle<TargetType> toType() const noexcept
866     {
867         Rectangle<TargetType> r;
868         copyWithRounding (r);
869         return r;
870     }
871 
872     /** Returns the smallest Rectangle that can contain a set of points. */
findAreaContainingPoints(const Point<ValueType> * points,int numPoints)873     static Rectangle findAreaContainingPoints (const Point<ValueType>* points, int numPoints) noexcept
874     {
875         if (numPoints <= 0)
876             return {};
877 
878         auto minX = points[0].x;
879         auto maxX = minX;
880         auto minY = points[0].y;
881         auto maxY = minY;
882 
883         for (int i = 1; i < numPoints; ++i)
884         {
885             minX = jmin (minX, points[i].x);
886             maxX = jmax (maxX, points[i].x);
887             minY = jmin (minY, points[i].y);
888             maxY = jmax (maxY, points[i].y);
889         }
890 
891         return { minX, minY, maxX - minX, maxY - minY };
892     }
893 
894     //==============================================================================
895     /** Static utility to intersect two sets of rectangular coordinates.
896         Returns false if the two regions didn't overlap.
897         @see intersectRectangle
898     */
intersectRectangles(ValueType & x1,ValueType & y1,ValueType & w1,ValueType & h1,ValueType x2,ValueType y2,ValueType w2,ValueType h2)899     static bool intersectRectangles (ValueType& x1, ValueType& y1, ValueType& w1, ValueType& h1,
900                                      ValueType  x2, ValueType  y2, ValueType  w2, ValueType  h2) noexcept
901     {
902         auto x = jmax (x1, x2);
903         w1 = jmin (x1 + w1, x2 + w2) - x;
904 
905         if (w1 > ValueType())
906         {
907             auto y = jmax (y1, y2);
908             h1 = jmin (y1 + h1, y2 + h2) - y;
909 
910             if (h1 > ValueType())
911             {
912                 x1 = x; y1 = y;
913                 return true;
914             }
915         }
916 
917         return false;
918     }
919 
920     //==============================================================================
921     /** Creates a string describing this rectangle.
922 
923         The string will be of the form "x y width height", e.g. "100 100 400 200".
924 
925         Coupled with the fromString() method, this is very handy for things like
926         storing rectangles (particularly component positions) in XML attributes.
927 
928         @see fromString
929     */
toString()930     String toString() const
931     {
932         String s;
933         s.preallocateBytes (32);
934         s << pos.x << ' ' << pos.y << ' ' << w << ' ' << h;
935         return s;
936     }
937 
938     /** Parses a string containing a rectangle's details.
939 
940         The string should contain 4 integer tokens, in the form "x y width height". They
941         can be comma or whitespace separated.
942 
943         This method is intended to go with the toString() method, to form an easy way
944         of saving/loading rectangles as strings.
945 
946         @see toString
947     */
fromString(StringRef stringVersion)948     static Rectangle fromString (StringRef stringVersion)
949     {
950         StringArray toks;
951         toks.addTokens (stringVersion.text.findEndOfWhitespace(), ",; \t\r\n", "");
952 
953         return { parseIntAfterSpace (toks[0]),
954                  parseIntAfterSpace (toks[1]),
955                  parseIntAfterSpace (toks[2]),
956                  parseIntAfterSpace (toks[3]) };
957     }
958 
959    #ifndef DOXYGEN
960     // This has been renamed by transformedBy, in order to match the method names used in the Point class.
961     JUCE_DEPRECATED_WITH_BODY (Rectangle transformed (const AffineTransform& t) const noexcept, { return transformedBy (t); })
962    #endif
963 
964 private:
965     template <typename OtherType> friend class Rectangle;
966 
967     Point<ValueType> pos;
968     ValueType w {}, h {};
969 
parseIntAfterSpace(StringRef s)970     static ValueType parseIntAfterSpace (StringRef s) noexcept
971         { return static_cast<ValueType> (s.text.findEndOfWhitespace().getIntValue32()); }
972 
copyWithRounding(Rectangle<int> & result)973     void copyWithRounding (Rectangle<int>& result) const noexcept    { result = getSmallestIntegerContainer(); }
copyWithRounding(Rectangle<float> & result)974     void copyWithRounding (Rectangle<float>& result) const noexcept  { result = toFloat(); }
copyWithRounding(Rectangle<double> & result)975     void copyWithRounding (Rectangle<double>& result) const noexcept { result = toDouble(); }
976 
floorAsInt(int n)977     static int floorAsInt (int n) noexcept     { return n; }
floorAsInt(float n)978     static int floorAsInt (float n) noexcept   { return n > (float)  std::numeric_limits<int>::min() ? (int) std::floor (n) : std::numeric_limits<int>::min(); }
floorAsInt(double n)979     static int floorAsInt (double n) noexcept  { return n > (double) std::numeric_limits<int>::min() ? (int) std::floor (n) : std::numeric_limits<int>::min(); }
ceilAsInt(int n)980     static int ceilAsInt (int n) noexcept      { return n; }
ceilAsInt(float n)981     static int ceilAsInt (float n) noexcept    { return n < (float)  std::numeric_limits<int>::max() ? (int) std::ceil (n) : std::numeric_limits<int>::max(); }
ceilAsInt(double n)982     static int ceilAsInt (double n) noexcept   { return n < (double) std::numeric_limits<int>::max() ? (int) std::ceil (n) : std::numeric_limits<int>::max(); }
983 };
984 
985 } // namespace juce
986