VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_graphics / geometry / juce_RectangleList.h
blob8e106cd40ec5f4c243017a12d305abfcd4249b01
1 /*
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
8 licensing.
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
21 DISCLAIMED.
23 ==============================================================================
26 namespace juce
29 //==============================================================================
30 /**
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
35 adjacent rectangles.
37 @see Rectangle
39 @tags{Graphics}
41 template <typename ValueType>
42 class RectangleList final
44 public:
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)
65 rects = other.rects;
66 return *this;
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);
79 return *this;
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. */
96 void clear()
98 rects.clearQuick();
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
105 merged into it.
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())
116 if (isEmpty())
118 rects.add (rect);
120 else
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))
131 rects.remove (j);
132 else if (! ourRect.reduceIfPartlyContainedIn (rect))
133 anyOverlaps = true;
137 if (anyOverlaps && ! isEmpty())
139 RectangleList r (rect);
141 for (auto& ourRect : rects)
143 if (rect.intersects (ourRect))
145 r.subtract (ourRect);
147 if (r.isEmpty())
148 return;
152 rects.addArray (r.rects);
154 else
156 rects.add (rect);
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())
185 rects.add (rect);
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)
196 add (r);
199 /** Removes a rectangular region from the list.
201 Any rectangles in the list which overlap this will be clipped and subdivided
202 if necessary.
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);
217 auto rx1 = r.getX();
218 auto ry1 = r.getY();
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);
230 else
232 r.setX (x1);
233 r.setWidth (rx2 - x1);
235 rects.insert (++i, RectangleType (rx1, ry1, x1 - rx1, ry2 - ry1));
236 ++i;
239 else if (x2 > rx1 && x2 < rx2)
241 r.setX (x2);
242 r.setWidth (rx2 - x2);
244 if (y1 > ry1 || y2 < ry2 || x1 > rx1)
246 rects.insert (++i, RectangleType (rx1, ry1, x2 - rx1, ry2 - ry1));
247 ++i;
250 else if (y1 > ry1 && y1 < ry2)
252 if (x1 <= rx1 && x2 >= rx2 && y2 >= ry2)
254 r.setHeight (y1 - ry1);
256 else
258 r.setY (y1);
259 r.setHeight (ry2 - y1);
261 rects.insert (++i, RectangleType (rx1, ry1, rx2 - rx1, y1 - ry1));
262 ++i;
265 else if (y2 > ry1 && y2 < ry2)
267 r.setY (y2);
268 r.setHeight (ry2 - y2);
270 if (x1 > rx1 || x2 < rx2 || y1 > ry1)
272 rects.insert (++i, RectangleType (rx1, ry1, rx2 - rx1, y2 - ry1));
273 ++i;
276 else
278 rects.remove (i);
285 /** Removes all areas in another RectangleList from this one.
287 Any rectangles in the list which overlap this will be clipped and subdivided
288 if necessary.
290 @returns true if the resulting list is non-empty.
292 bool subtract (const RectangleList& otherList)
294 for (auto& r : otherList)
296 if (isEmpty())
297 return false;
299 subtract (r);
302 return ! isEmpty();
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
308 if necessary.
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;
320 if (rect.isEmpty())
322 clear();
324 else
326 for (int i = rects.size(); --i >= 0;)
328 auto& r = rects.getReference (i);
330 if (! rect.intersectRectangle (r))
331 rects.remove (i);
332 else
333 notEmpty = true;
337 return notEmpty;
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)
352 if (isEmpty())
353 return false;
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);
368 swapWith (result);
369 return ! isEmpty();
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.
379 @see clipTo
381 bool getIntersectionWith (RectangleType rect, RectangleList& destRegion) const
383 jassert (rect.isFinite()); // You must provide a valid rectangle to this method!
385 destRegion.clear();
387 if (! rect.isEmpty())
388 for (auto r : rects)
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
398 to swap them.
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))
413 return true;
415 return false;
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)
440 r.subtract (rect);
442 if (r.isEmpty())
443 return true;
446 else if (! isEmpty())
448 return rects.getReference (0).contains (rectangleToCheck);
451 return false;
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))
464 return true;
466 return false;
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))
476 return true;
478 return false;
481 //==============================================================================
482 /** Returns the smallest rectangle that can enclose the whole of this region. */
483 RectangleType getBounds() const noexcept
485 if (isEmpty())
486 return {};
488 auto& r = rects.getReference (0);
490 if (rects.size() == 1)
491 return r;
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
515 add/subtract calls.
517 void consolidate()
519 for (int i = 0; i < rects.size() - 1; ++i)
521 auto& r = rects.getReference (i);
522 auto rx1 = r.getX();
523 auto ry1 = r.getY();
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));
543 i = -1;
544 break;
547 if (jry2 > ry1 && jry2 < ry2)
549 r.setHeight (jry2 - ry1);
550 rects.add (RectangleType (rx1, jry2, rx2 - rx1, ry2 - jry2));
551 i = -1;
552 break;
554 else if (ry1 > jry1 && ry1 < jry2)
556 r2.setHeight (ry1 - jry1);
557 rects.add (RectangleType (jrx1, ry1, jrx2 - jrx1, jry2 - ry1));
558 i = -1;
559 break;
561 else if (ry2 > jry1 && ry2 < jry2)
563 r2.setHeight (ry2 - jry1);
564 rects.add (RectangleType (jrx1, ry2, jrx2 - jrx1, jry2 - ry2));
565 i = -1;
566 break;
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)))
580 rects.remove (j);
581 i = -1;
582 break;
588 /** Adds an x and y value to all the coordinates. */
589 void offsetAll (Point<ValueType> offset) noexcept
591 for (auto& r : rects)
592 r += offset;
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)
606 r *= scaleFactor;
609 /** Applies a transform to all the rectangles.
610 Obviously this will create a mess if the transform involves any
611 rotation or skewing.
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. */
621 Path toPath() const
623 Path p;
625 for (auto& r : rects)
626 p.addRectangle (r);
628 return p;
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);
648 private:
649 //==============================================================================
650 Array<RectangleType> rects;
653 } // namespace juce