2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
23 ==============================================================================
29 //==============================================================================
31 Maintains a set of rectangles as a complex region.
33 This class allows a set of rectangles to be treated as a solid shape, and can
34 add and remove rectangular sections of it, and simplify overlapping or
41 template <typename ValueType
>
42 class RectangleList final
45 using RectangleType
= Rectangle
<ValueType
>;
47 //==============================================================================
48 /** Creates an empty RectangleList */
49 RectangleList() = default;
51 /** Creates a copy of another list */
52 RectangleList (const RectangleList
& other
) : rects (other
.rects
)
56 /** Creates a list containing just one rectangle. */
57 RectangleList (RectangleType rect
)
59 addWithoutMerging (rect
);
62 /** Copies this list from another one. */
63 RectangleList
& operator= (const RectangleList
& other
)
69 /** Move constructor */
70 RectangleList (RectangleList
&& other
) noexcept
71 : rects (std::move (other
.rects
))
75 /** Move assignment operator */
76 RectangleList
& operator= (RectangleList
&& other
) noexcept
78 rects
= std::move (other
.rects
);
82 //==============================================================================
83 /** Returns true if the region is empty. */
84 bool isEmpty() const noexcept
{ return rects
.isEmpty(); }
86 /** Returns the number of rectangles in the list. */
87 int getNumRectangles() const noexcept
{ return rects
.size(); }
89 /** Returns one of the rectangles at a particular index.
90 @returns the rectangle at the index, or an empty rectangle if the index is out-of-range.
92 RectangleType
getRectangle (int index
) const noexcept
{ return rects
[index
]; }
94 //==============================================================================
95 /** Removes all rectangles to leave an empty region. */
101 /** Merges a new rectangle into the list.
103 The rectangle being added will first be clipped to remove any parts of it
104 that overlap existing rectangles in the list, and adjacent rectangles will be
107 The rectangle can have any size and may be empty, but if it's floating point
108 then it's expected to not contain any INF values.
110 void add (RectangleType rect
)
112 jassert (rect
.isFinite()); // You must provide a valid rectangle to this method!
114 if (! rect
.isEmpty())
122 bool anyOverlaps
= false;
124 for (int j
= rects
.size(); --j
>= 0;)
126 auto& ourRect
= rects
.getReference (j
);
128 if (rect
.intersects (ourRect
))
130 if (rect
.contains (ourRect
))
132 else if (! ourRect
.reduceIfPartlyContainedIn (rect
))
137 if (anyOverlaps
&& ! isEmpty())
139 RectangleList
r (rect
);
141 for (auto& ourRect
: rects
)
143 if (rect
.intersects (ourRect
))
145 r
.subtract (ourRect
);
152 rects
.addArray (r
.rects
);
162 /** Merges a new rectangle into the list.
164 The rectangle being added will first be clipped to remove any parts of it
165 that overlap existing rectangles in the list.
167 void add (ValueType x
, ValueType y
, ValueType width
, ValueType height
)
169 add (RectangleType (x
, y
, width
, height
));
172 /** Dumbly adds a rectangle to the list without checking for overlaps.
174 This simply adds the rectangle to the end, it doesn't merge it or remove
175 any overlapping bits.
177 The rectangle can have any size and may be empty, but if it's floating point
178 then it's expected to not contain any INF values.
180 void addWithoutMerging (RectangleType rect
)
182 jassert (rect
.isFinite()); // You must provide a valid rectangle to this method!
184 if (! rect
.isEmpty())
188 /** Merges another rectangle list into this one.
190 Any overlaps between the two lists will be clipped, so that the result is
191 the union of both lists.
193 void add (const RectangleList
& other
)
195 for (auto& r
: other
)
199 /** Removes a rectangular region from the list.
201 Any rectangles in the list which overlap this will be clipped and subdivided
204 void subtract (RectangleType rect
)
206 if (auto numRects
= rects
.size())
208 auto x1
= rect
.getX();
209 auto y1
= rect
.getY();
210 auto x2
= x1
+ rect
.getWidth();
211 auto y2
= y1
+ rect
.getHeight();
213 for (int i
= numRects
; --i
>= 0;)
215 auto& r
= rects
.getReference (i
);
219 auto rx2
= rx1
+ r
.getWidth();
220 auto ry2
= ry1
+ r
.getHeight();
222 if (! (x2
<= rx1
|| x1
>= rx2
|| y2
<= ry1
|| y1
>= ry2
))
224 if (x1
> rx1
&& x1
< rx2
)
226 if (y1
<= ry1
&& y2
>= ry2
&& x2
>= rx2
)
228 r
.setWidth (x1
- rx1
);
233 r
.setWidth (rx2
- x1
);
235 rects
.insert (++i
, RectangleType (rx1
, ry1
, x1
- rx1
, ry2
- ry1
));
239 else if (x2
> rx1
&& x2
< rx2
)
242 r
.setWidth (rx2
- x2
);
244 if (y1
> ry1
|| y2
< ry2
|| x1
> rx1
)
246 rects
.insert (++i
, RectangleType (rx1
, ry1
, x2
- rx1
, ry2
- ry1
));
250 else if (y1
> ry1
&& y1
< ry2
)
252 if (x1
<= rx1
&& x2
>= rx2
&& y2
>= ry2
)
254 r
.setHeight (y1
- ry1
);
259 r
.setHeight (ry2
- y1
);
261 rects
.insert (++i
, RectangleType (rx1
, ry1
, rx2
- rx1
, y1
- ry1
));
265 else if (y2
> ry1
&& y2
< ry2
)
268 r
.setHeight (ry2
- y2
);
270 if (x1
> rx1
|| x2
< rx2
|| y1
> ry1
)
272 rects
.insert (++i
, RectangleType (rx1
, ry1
, rx2
- rx1
, y2
- ry1
));
285 /** Removes all areas in another RectangleList from this one.
287 Any rectangles in the list which overlap this will be clipped and subdivided
290 @returns true if the resulting list is non-empty.
292 bool subtract (const RectangleList
& otherList
)
294 for (auto& r
: otherList
)
305 /** Removes any areas of the region that lie outside a given rectangle.
307 Any rectangles in the list which overlap this will be clipped and subdivided
310 Returns true if the resulting region is not empty, false if it is empty.
312 @see getIntersectionWith
314 bool clipTo (RectangleType rect
)
316 jassert (rect
.isFinite()); // You must provide a valid rectangle to this method!
318 bool notEmpty
= false;
326 for (int i
= rects
.size(); --i
>= 0;)
328 auto& r
= rects
.getReference (i
);
330 if (! rect
.intersectRectangle (r
))
340 /** Removes any areas of the region that lie outside a given rectangle list.
342 Any rectangles in this object which overlap the specified list will be clipped
343 and subdivided if necessary.
345 Returns true if the resulting region is not empty, false if it is empty.
347 @see getIntersectionWith
349 template <typename OtherValueType
>
350 bool clipTo (const RectangleList
<OtherValueType
>& other
)
355 RectangleList result
;
357 for (auto& rect
: rects
)
359 for (auto& r
: other
)
361 auto clipped
= r
.template toType
<ValueType
>();
363 if (rect
.intersectRectangle (clipped
))
364 result
.rects
.add (clipped
);
372 /** Creates a region which is the result of clipping this one to a given rectangle.
374 Unlike the other clipTo method, this one doesn't affect this object - it puts the
375 resulting region into the list whose reference is passed-in.
377 Returns true if the resulting region is not empty, false if it is empty.
381 bool getIntersectionWith (RectangleType rect
, RectangleList
& destRegion
) const
383 jassert (rect
.isFinite()); // You must provide a valid rectangle to this method!
387 if (! rect
.isEmpty())
389 if (rect
.intersectRectangle (r
))
390 destRegion
.rects
.add (r
);
392 return ! destRegion
.isEmpty();
395 /** Swaps the contents of this and another list.
397 This swaps their internal pointers, so is hugely faster than using copy-by-value
400 void swapWith (RectangleList
& otherList
) noexcept
402 rects
.swapWith (otherList
.rects
);
405 //==============================================================================
406 /** Checks whether the region contains a given point.
407 @returns true if the point lies within one of the rectangles in the list
409 bool containsPoint (Point
<ValueType
> point
) const noexcept
411 for (auto& r
: rects
)
412 if (r
.contains (point
))
418 /** Checks whether the region contains a given point.
419 @returns true if the point lies within one of the rectangles in the list
421 bool containsPoint (ValueType x
, ValueType y
) const noexcept
423 return containsPoint (Point
<ValueType
> (x
, y
));
426 /** Checks whether the region contains the whole of a given rectangle.
428 @returns true all parts of the rectangle passed in lie within the region
429 defined by this object
430 @see intersectsRectangle, containsPoint
432 bool containsRectangle (RectangleType rectangleToCheck
) const
434 if (rects
.size() > 1)
436 RectangleList
r (rectangleToCheck
);
438 for (auto& rect
: rects
)
446 else if (! isEmpty())
448 return rects
.getReference (0).contains (rectangleToCheck
);
454 /** Checks whether the region contains any part of a given rectangle.
456 @returns true if any part of the rectangle passed in lies within the region
457 defined by this object
458 @see containsRectangle
460 bool intersectsRectangle (RectangleType rectangleToCheck
) const noexcept
462 for (auto& r
: rects
)
463 if (r
.intersects (rectangleToCheck
))
469 /** Checks whether this region intersects any part of another one.
470 @see intersectsRectangle
472 bool intersects (const RectangleList
& other
) const noexcept
474 for (auto& r
: rects
)
475 if (other
.intersectsRectangle (r
))
481 //==============================================================================
482 /** Returns the smallest rectangle that can enclose the whole of this region. */
483 RectangleType
getBounds() const noexcept
488 auto& r
= rects
.getReference (0);
490 if (rects
.size() == 1)
493 auto minX
= r
.getX();
494 auto minY
= r
.getY();
495 auto maxX
= minX
+ r
.getWidth();
496 auto maxY
= minY
+ r
.getHeight();
498 for (int i
= rects
.size(); --i
> 0;)
500 auto& r2
= rects
.getReference (i
);
502 minX
= jmin (minX
, r2
.getX());
503 minY
= jmin (minY
, r2
.getY());
504 maxX
= jmax (maxX
, r2
.getRight());
505 maxY
= jmax (maxY
, r2
.getBottom());
508 return { minX
, minY
, maxX
- minX
, maxY
- minY
};
511 /** Optimises the list into a minimum number of constituent rectangles.
513 This will try to combine any adjacent rectangles into larger ones where
514 possible, to simplify lists that might have been fragmented by repeated
519 for (int i
= 0; i
< rects
.size() - 1; ++i
)
521 auto& r
= rects
.getReference (i
);
524 auto rx2
= rx1
+ r
.getWidth();
525 auto ry2
= ry1
+ r
.getHeight();
527 for (int j
= rects
.size(); --j
> i
;)
529 auto& r2
= rects
.getReference (j
);
530 auto jrx1
= r2
.getX();
531 auto jry1
= r2
.getY();
532 auto jrx2
= jrx1
+ r2
.getWidth();
533 auto jry2
= jry1
+ r2
.getHeight();
535 // if the vertical edges of any blocks are touching and their horizontals don't
536 // line up, split them horizontally..
537 if (jrx1
== rx2
|| jrx2
== rx1
)
539 if (jry1
> ry1
&& jry1
< ry2
)
541 r
.setHeight (jry1
- ry1
);
542 rects
.add (RectangleType (rx1
, jry1
, rx2
- rx1
, ry2
- jry1
));
547 if (jry2
> ry1
&& jry2
< ry2
)
549 r
.setHeight (jry2
- ry1
);
550 rects
.add (RectangleType (rx1
, jry2
, rx2
- rx1
, ry2
- jry2
));
554 else if (ry1
> jry1
&& ry1
< jry2
)
556 r2
.setHeight (ry1
- jry1
);
557 rects
.add (RectangleType (jrx1
, ry1
, jrx2
- jrx1
, jry2
- ry1
));
561 else if (ry2
> jry1
&& ry2
< jry2
)
563 r2
.setHeight (ry2
- jry1
);
564 rects
.add (RectangleType (jrx1
, ry2
, jrx2
- jrx1
, jry2
- ry2
));
572 for (int i
= 0; i
< rects
.size() - 1; ++i
)
574 auto& r
= rects
.getReference (i
);
576 for (int j
= rects
.size(); --j
> i
;)
578 if (r
.enlargeIfAdjacent (rects
.getReference (j
)))
588 /** Adds an x and y value to all the coordinates. */
589 void offsetAll (Point
<ValueType
> offset
) noexcept
591 for (auto& r
: rects
)
595 /** Adds an x and y value to all the coordinates. */
596 void offsetAll (ValueType dx
, ValueType dy
) noexcept
598 offsetAll (Point
<ValueType
> (dx
, dy
));
601 /** Scales all the coordinates. */
602 template <typename ScaleType
>
603 void scaleAll (ScaleType scaleFactor
) noexcept
605 for (auto& r
: rects
)
609 /** Applies a transform to all the rectangles.
610 Obviously this will create a mess if the transform involves any
613 void transformAll (const AffineTransform
& transform
) noexcept
615 for (auto& r
: rects
)
616 r
= r
.transformedBy (transform
);
619 //==============================================================================
620 /** Creates a Path object to represent this region. */
625 for (auto& r
: rects
)
631 //==============================================================================
632 /** Standard method for iterating the rectangles in the list. */
633 const RectangleType
* begin() const noexcept
{ return rects
.begin(); }
634 /** Standard method for iterating the rectangles in the list. */
635 const RectangleType
* end() const noexcept
{ return rects
.end(); }
637 /** Increases the internal storage to hold a minimum number of rectangles.
638 Calling this before adding a large number of rectangles means that
639 the array won't have to keep dynamically resizing itself as the elements
640 are added, and it'll therefore be more efficient.
641 @see Array::ensureStorageAllocated
643 void ensureStorageAllocated (int minNumRectangles
)
645 rects
.ensureStorageAllocated (minNumRectangles
);
649 //==============================================================================
650 Array
<RectangleType
> rects
;