Skip to content

Commit bd5e441

Browse files
committed
RectangleList: DRY implementation of subtract() and avoid unnecessary allocations for int-based rectangle lists
1 parent d10c5c3 commit bd5e441

File tree

1 file changed

+135
-82
lines changed

1 file changed

+135
-82
lines changed

modules/juce_graphics/geometry/juce_RectangleList.h

Lines changed: 135 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -212,95 +212,24 @@ class RectangleList final
212212
*/
213213
void subtract (const RectangleType rect)
214214
{
215-
using PointType = Point<ValueType>;
216-
217-
const auto numRects = rects.size();
218-
219-
if (numRects == 0)
215+
if (rects.isEmpty())
220216
return;
221217

222-
struct AABB
218+
if constexpr (std::is_floating_point_v<ValueType>)
223219
{
224-
AABB() = default;
225-
AABB (const RectangleType& r) : tl (r.getTopLeft()), br (r.getBottomRight()) {}
226-
AABB (PointType a, PointType b) : tl (a), br (b) {}
227-
operator RectangleType() const { return RectangleType { tl, br }; }
228-
229-
bool completelyOutside (const AABB& other) const
230-
{
231-
return other.br.x <= tl.x || br.x <= other.tl.x || other.br.y <= tl.y || br.y <= other.tl.y;
232-
}
233-
234-
PointType tl, br;
235-
};
220+
Array<AABB> alternativeRepresentation;
221+
alternativeRepresentation.resize (rects.size());
222+
std::copy (rects.begin(), rects.end(), alternativeRepresentation.begin());
236223

237-
Array<AABB> aabbs;
238-
aabbs.resize (rects.size());
239-
std::copy (rects.begin(), rects.end(), aabbs.begin());
240-
const AABB aabb { rect };
224+
subtract (alternativeRepresentation, rect);
241225

242-
for (int i = numRects; --i >= 0;)
226+
rects.resize (alternativeRepresentation.size());
227+
std::copy (alternativeRepresentation.begin(), alternativeRepresentation.end(), rects.begin());
228+
}
229+
else
243230
{
244-
auto& rRef = aabbs.getReference (i);;
245-
const auto r = rRef;
246-
247-
if (r.completelyOutside (aabb))
248-
continue;
249-
250-
if (r.tl.x < aabb.tl.x && aabb.tl.x < r.br.x)
251-
{
252-
if (aabb.tl.y <= r.tl.y && r.br.y <= aabb.br.y && r.br.x <= aabb.br.x)
253-
{
254-
rRef.br.x = aabb.tl.x;
255-
}
256-
else
257-
{
258-
rRef.tl.x = aabb.tl.x;
259-
aabbs.insert (++i, { r.tl, { aabb.tl.x, r.br.y } });
260-
++i;
261-
}
262-
}
263-
else if (r.tl.x < aabb.br.x && aabb.br.x < r.br.x)
264-
{
265-
rRef.tl.x = aabb.br.x;
266-
267-
if (r.tl.y < aabb.tl.y || aabb.br.y < r.br.y || r.tl.x < aabb.tl.x)
268-
{
269-
aabbs.insert (++i, { r.tl, { aabb.br.x, r.br.y } });
270-
++i;
271-
}
272-
}
273-
else if (r.tl.y < aabb.tl.y && aabb.tl.y < r.br.y)
274-
{
275-
if (aabb.tl.x <= r.tl.x && r.br.x <= aabb.br.x && r.br.y <= aabb.br.y)
276-
{
277-
rRef.br.y = aabb.tl.y;
278-
}
279-
else
280-
{
281-
rRef.tl.y = aabb.tl.y;
282-
aabbs.insert (++i, { r.tl, { r.br.x, aabb.tl.y } });
283-
++i;
284-
}
285-
}
286-
else if (r.tl.y < aabb.br.y && aabb.br.y < r.br.y)
287-
{
288-
rRef.tl.y = aabb.br.y;
289-
290-
if (r.tl.x < aabb.tl.x || aabb.br.x < r.br.x || r.tl.y < aabb.tl.y)
291-
{
292-
aabbs.insert (++i, { r.tl, { r.br.x, aabb.br.y } });
293-
++i;
294-
}
295-
}
296-
else
297-
{
298-
aabbs.remove (i);
299-
}
231+
subtract (rects, rect);
300232
}
301-
302-
rects.resize (aabbs.size());
303-
std::copy (aabbs.begin(), aabbs.end(), rects.begin());
304233
}
305234

306235
/** Removes all areas in another RectangleList from this one.
@@ -667,6 +596,130 @@ class RectangleList final
667596
}
668597

669598
private:
599+
using PointType = Point<ValueType>;
600+
601+
struct AABB
602+
{
603+
AABB() = default;
604+
AABB (const RectangleType& r) : tl (r.getTopLeft()), br (r.getBottomRight()) {}
605+
AABB (PointType a, PointType b) : tl (a), br (b) {}
606+
operator RectangleType() const { return RectangleType { tl, br }; }
607+
608+
bool completelyOutside (const AABB& other) const
609+
{
610+
return other.br.x <= tl.x || br.x <= other.tl.x || other.br.y <= tl.y || br.y <= other.tl.y;
611+
}
612+
613+
PointType tl, br;
614+
};
615+
616+
static PointType getTL (const AABB& aabb)
617+
{
618+
return aabb.tl;
619+
}
620+
621+
static PointType getBR (const AABB& aabb)
622+
{
623+
return aabb.br;
624+
}
625+
626+
static PointType getTL (const RectangleType& r)
627+
{
628+
return r.getTopLeft();
629+
}
630+
631+
static PointType getBR (const RectangleType& r)
632+
{
633+
return r.getBottomRight();
634+
}
635+
636+
template <typename Rect>
637+
static void subtract (Array<Rect>& rectList, RectangleType rect)
638+
{
639+
jassert (! rectList.isEmpty());
640+
641+
const AABB rAABB { getTL (rect), getBR (rect) };
642+
643+
for (int i = rectList.size(); --i >= 0;)
644+
{
645+
auto& iRef = rectList.getReference (i);
646+
const AABB iAABB { getTL (iRef), getBR (iRef) };
647+
648+
if (iAABB.completelyOutside (rAABB))
649+
continue;
650+
651+
std::invoke ([&]
652+
{
653+
// For each rectangle in the list, we check for an overlap with the parameter rect
654+
// and subdivide the list rectangles as necessary.
655+
// The checks for each list rectangle happen in two stages, treating first the X
656+
// then the Y axis as the 'major' axis. The terms 'major' and 'minor' are somewhat
657+
// arbitrary. Essentially, in each pass, the rectangles in the list are only
658+
// modified and/or subdivided on the major axis, without changing any minor axis
659+
// coordinates. Subdividing an overlapping rect in this way may produce new rects
660+
// that still overlap with the parameter rect. However, those will get cleaned up
661+
// on the subsequent pass, where the major/minor axes are swapped.
662+
663+
struct Fields
664+
{
665+
ValueType PointType::* major;
666+
ValueType PointType::* minor;
667+
};
668+
669+
for (const auto& fields : { Fields { &PointType::x, &PointType::y },
670+
Fields { &PointType::y, &PointType::x } })
671+
{
672+
const auto replaceMajor = [&] (auto modified, const auto& replacement)
673+
{
674+
modified.*(fields.major) = replacement.*(fields.major);
675+
return modified;
676+
};
677+
678+
const auto insert = [&] (const PointType& rAABBpt)
679+
{
680+
rectList.insert (++i, { iAABB.tl, replaceMajor (iAABB.br, rAABBpt) });
681+
++i;
682+
};
683+
684+
if ( iAABB.tl.*(fields.major) < rAABB.tl.*(fields.major)
685+
&& rAABB.tl.*(fields.major) < iAABB.br.*(fields.major))
686+
{
687+
if ( rAABB.tl.*(fields.minor) <= iAABB.tl.*(fields.minor)
688+
&& iAABB.br.*(fields.minor) <= rAABB.br.*(fields.minor)
689+
&& iAABB.br.*(fields.major) <= rAABB.br.*(fields.major))
690+
{
691+
iRef = { iAABB.tl, replaceMajor (iAABB.br, rAABB.tl) };
692+
}
693+
else
694+
{
695+
iRef = { replaceMajor (iAABB.tl, rAABB.tl), iAABB.br };
696+
insert (rAABB.tl);
697+
}
698+
699+
return;
700+
}
701+
702+
if ( iAABB.tl.*(fields.major) < rAABB.br.*(fields.major)
703+
&& rAABB.br.*(fields.major) < iAABB.br.*(fields.major))
704+
{
705+
iRef = { replaceMajor (iAABB.tl, rAABB.br), iAABB.br };
706+
707+
if ( iAABB.tl.*(fields.minor) < rAABB.tl.*(fields.minor)
708+
|| rAABB.br.*(fields.minor) < iAABB.br.*(fields.minor)
709+
|| iAABB.tl.*(fields.major) < rAABB.tl.*(fields.major))
710+
{
711+
insert (rAABB.br);
712+
}
713+
714+
return;
715+
}
716+
}
717+
718+
rectList.remove (i);
719+
});
720+
}
721+
}
722+
670723
//==============================================================================
671724
Array<RectangleType> rects;
672725
};

0 commit comments

Comments
 (0)