/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2013 - Raw Material Software Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ RectangleList::RectangleList() noexcept { } RectangleList::RectangleList (const Rectangle& rect) { addWithoutMerging (rect); } RectangleList::RectangleList (const RectangleList& other) : rects (other.rects) { } RectangleList& RectangleList::operator= (const RectangleList& other) { rects = other.rects; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS RectangleList::RectangleList (RectangleList&& other) noexcept : rects (static_cast >&&> (other.rects)) { } RectangleList& RectangleList::operator= (RectangleList&& other) noexcept { rects = static_cast >&&> (other.rects); return *this; } #endif RectangleList::~RectangleList() { } //============================================================================== void RectangleList::clear() { rects.clearQuick(); } Rectangle RectangleList::getRectangle (const int index) const noexcept { if (isPositiveAndBelow (index, rects.size())) return rects.getReference (index); return Rectangle(); } bool RectangleList::isEmpty() const noexcept { return rects.size() == 0; } //============================================================================== RectangleList::Iterator::Iterator (const RectangleList& list) noexcept : current (nullptr), owner (list), index (list.rects.size()) { } RectangleList::Iterator::~Iterator() { } bool RectangleList::Iterator::next() noexcept { if (--index >= 0) { current = &(owner.rects.getReference (index)); return true; } return false; } //============================================================================== void RectangleList::add (const Rectangle& rect) { if (! rect.isEmpty()) { if (rects.size() == 0) { rects.add (rect); } else { bool anyOverlaps = false; for (int j = rects.size(); --j >= 0;) { Rectangle& ourRect = rects.getReference (j); if (rect.intersects (ourRect)) { if (rect.contains (ourRect)) rects.remove (j); else if (! ourRect.reduceIfPartlyContainedIn (rect)) anyOverlaps = true; } } if (anyOverlaps && rects.size() > 0) { RectangleList r (rect); for (int i = rects.size(); --i >= 0;) { const Rectangle& ourRect = rects.getReference (i); if (rect.intersects (ourRect)) { r.subtract (ourRect); if (r.rects.size() == 0) return; } } rects.addArray (r.rects); } else { rects.add (rect); } } } } void RectangleList::addWithoutMerging (const Rectangle& rect) { if (! rect.isEmpty()) rects.add (rect); } void RectangleList::add (const int x, const int y, const int w, const int h) { add (Rectangle (x, y, w, h)); } void RectangleList::add (const RectangleList& other) { for (const Rectangle* r = other.begin(), * const e = other.end(); r != e; ++r) add (*r); } void RectangleList::subtract (const Rectangle& rect) { const int originalNumRects = rects.size(); if (originalNumRects > 0) { const int x1 = rect.pos.x; const int y1 = rect.pos.y; const int x2 = x1 + rect.w; const int y2 = y1 + rect.h; for (int i = getNumRectangles(); --i >= 0;) { Rectangle& r = rects.getReference (i); const int rx1 = r.pos.x; const int ry1 = r.pos.y; const int rx2 = rx1 + r.w; const int ry2 = ry1 + r.h; if (! (x2 <= rx1 || x1 >= rx2 || y2 <= ry1 || y1 >= ry2)) { if (x1 > rx1 && x1 < rx2) { if (y1 <= ry1 && y2 >= ry2 && x2 >= rx2) { r.w = x1 - rx1; } else { r.pos.x = x1; r.w = rx2 - x1; rects.insert (++i, Rectangle (rx1, ry1, x1 - rx1, ry2 - ry1)); ++i; } } else if (x2 > rx1 && x2 < rx2) { r.pos.x = x2; r.w = rx2 - x2; if (y1 > ry1 || y2 < ry2 || x1 > rx1) { rects.insert (++i, Rectangle (rx1, ry1, x2 - rx1, ry2 - ry1)); ++i; } } else if (y1 > ry1 && y1 < ry2) { if (x1 <= rx1 && x2 >= rx2 && y2 >= ry2) { r.h = y1 - ry1; } else { r.pos.y = y1; r.h = ry2 - y1; rects.insert (++i, Rectangle (rx1, ry1, rx2 - rx1, y1 - ry1)); ++i; } } else if (y2 > ry1 && y2 < ry2) { r.pos.y = y2; r.h = ry2 - y2; if (x1 > rx1 || x2 < rx2 || y1 > ry1) { rects.insert (++i, Rectangle (rx1, ry1, rx2 - rx1, y2 - ry1)); ++i; } } else { rects.remove (i); } } } } } bool RectangleList::subtract (const RectangleList& otherList) { for (int i = otherList.rects.size(); --i >= 0 && rects.size() > 0;) subtract (otherList.rects.getReference (i)); return rects.size() > 0; } bool RectangleList::clipTo (const Rectangle& rect) { bool notEmpty = false; if (rect.isEmpty()) { clear(); } else { for (int i = rects.size(); --i >= 0;) { Rectangle& r = rects.getReference (i); if (! rect.intersectRectangle (r.pos.x, r.pos.y, r.w, r.h)) rects.remove (i); else notEmpty = true; } } return notEmpty; } bool RectangleList::clipTo (const RectangleList& other) { if (rects.size() == 0) return false; RectangleList result; for (int j = 0; j < rects.size(); ++j) { const Rectangle& rect = rects.getReference (j); for (int i = other.rects.size(); --i >= 0;) { Rectangle r (other.rects.getReference (i)); if (rect.intersectRectangle (r.pos.x, r.pos.y, r.w, r.h)) result.rects.add (r); } } swapWith (result); return ! isEmpty(); } bool RectangleList::getIntersectionWith (const Rectangle& rect, RectangleList& destRegion) const { destRegion.clear(); if (! rect.isEmpty()) { for (int i = rects.size(); --i >= 0;) { Rectangle r (rects.getReference (i)); if (rect.intersectRectangle (r.pos.x, r.pos.y, r.w, r.h)) destRegion.rects.add (r); } } return destRegion.rects.size() > 0; } void RectangleList::swapWith (RectangleList& otherList) noexcept { rects.swapWith (otherList.rects); } //============================================================================== void RectangleList::consolidate() { for (int i = 0; i < getNumRectangles() - 1; ++i) { Rectangle& r = rects.getReference (i); const int rx1 = r.pos.x; const int ry1 = r.pos.y; const int rx2 = rx1 + r.w; const int ry2 = ry1 + r.h; for (int j = rects.size(); --j > i;) { Rectangle& r2 = rects.getReference (j); const int jrx1 = r2.pos.x; const int jry1 = r2.pos.y; const int jrx2 = jrx1 + r2.w; const int jry2 = jry1 + r2.h; // if the vertical edges of any blocks are touching and their horizontals don't // line up, split them horizontally.. if (jrx1 == rx2 || jrx2 == rx1) { if (jry1 > ry1 && jry1 < ry2) { r.h = jry1 - ry1; rects.add (Rectangle (rx1, jry1, rx2 - rx1, ry2 - jry1)); i = -1; break; } if (jry2 > ry1 && jry2 < ry2) { r.h = jry2 - ry1; rects.add (Rectangle (rx1, jry2, rx2 - rx1, ry2 - jry2)); i = -1; break; } else if (ry1 > jry1 && ry1 < jry2) { r2.h = ry1 - jry1; rects.add (Rectangle (jrx1, ry1, jrx2 - jrx1, jry2 - ry1)); i = -1; break; } else if (ry2 > jry1 && ry2 < jry2) { r2.h = ry2 - jry1; rects.add (Rectangle (jrx1, ry2, jrx2 - jrx1, jry2 - ry2)); i = -1; break; } } } } for (int i = 0; i < rects.size() - 1; ++i) { Rectangle& r = rects.getReference (i); for (int j = rects.size(); --j > i;) { if (r.enlargeIfAdjacent (rects.getReference (j))) { rects.remove (j); i = -1; break; } } } } //============================================================================== bool RectangleList::containsPoint (const int x, const int y) const noexcept { for (const Rectangle* r = rects.begin(), * const e = rects.end(); r != e; ++r) if (r->contains (x, y)) return true; return false; } bool RectangleList::containsRectangle (const Rectangle& rectangleToCheck) const { if (rects.size() > 1) { RectangleList r (rectangleToCheck); for (int i = rects.size(); --i >= 0;) { r.subtract (rects.getReference (i)); if (r.rects.size() == 0) return true; } } else if (rects.size() > 0) { return rects.getReference (0).contains (rectangleToCheck); } return false; } bool RectangleList::intersectsRectangle (const Rectangle& rectangleToCheck) const noexcept { for (const Rectangle* r = rects.begin(), * const e = rects.end(); r != e; ++r) if (r->intersects (rectangleToCheck)) return true; return false; } bool RectangleList::intersects (const RectangleList& other) const noexcept { for (const Rectangle* r = rects.begin(), * const e = rects.end(); r != e; ++r) if (other.intersectsRectangle (*r)) return true; return false; } Rectangle RectangleList::getBounds() const noexcept { if (rects.size() <= 1) { if (rects.size() == 0) return Rectangle(); return rects.getReference (0); } else { const Rectangle& r = rects.getReference (0); int minX = r.pos.x; int minY = r.pos.y; int maxX = minX + r.w; int maxY = minY + r.h; for (int i = rects.size(); --i > 0;) { const Rectangle& r2 = rects.getReference (i); minX = jmin (minX, r2.pos.x); minY = jmin (minY, r2.pos.y); maxX = jmax (maxX, r2.getRight()); maxY = jmax (maxY, r2.getBottom()); } return Rectangle (minX, minY, maxX - minX, maxY - minY); } } void RectangleList::offsetAll (const int dx, const int dy) noexcept { for (Rectangle* r = rects.begin(), * const e = rects.end(); r != e; ++r) { r->pos.x += dx; r->pos.y += dy; } } //============================================================================== Path RectangleList::toPath() const { Path p; for (int i = 0; i < rects.size(); ++i) p.addRectangle (rects.getReference (i)); return p; }