[ui] Add a specialized function for subrect allocation

While VRect_Difference worked for subrect allocation, it wasn't ideal as
it tended to produce a lot of long, narrow strips that were difficult to
reuse and thus wasted a lot of the super-rectangle's area. This is
because it always does horizontal splits first. However, rewriting
VRect_Difference didn't seem to be the best option.

VRect_SubRect (the new function) takes only width and height, and splits
the given rectangle such that if there are two off-cuts, they will be
both the minimum and maximum possible area. This does seem to make for
much better utilization of the available area. It's also faster as it
does only the two splits, rather than four.
This commit is contained in:
Bill Currie 2022-09-02 17:47:27 +09:00
parent cd66dbfc97
commit 28e9a0a9ba
2 changed files with 64 additions and 0 deletions

View File

@ -165,4 +165,27 @@ vrect_t *VRect_Union (const vrect_t *r1, const vrect_t *r2);
*/
vrect_t *VRect_Merge (const vrect_t *r1, const vrect_t *r2);
/** Return up to three rectangles resulting from carving out the upper-left
rectangle of the specified width and height.
The first rectangle in the list will be the part of the supplied rectangle
that is covered by \a width and \a height (if either parameter is larger
than the supplied rectangle, then the returned rectangle will have the same
size as the supplied rectangle in that dimension). The other rectangles,
if there are any, will be the remainder of the supplied rectangle such that
the areas are min-max (to avoid creating long narrow rectangles).
\param rect The rectangle to carve.
\param width Width of the sub-rectangle carved from \a rect.
\param height Height of the sub-rectangle carved from \a rect.
\return The carved-out sub-rectangle, plus up to two rectangles
representing the remainder of \a rect, with splits such
that the two rectangles are both minimum and maximum area.
The larger of the two remainder rectangles will allways be
last.
\note It is the caller's responsibility to delete the returned
rectangles.
*/
vrect_t *VRect_SubRect (const vrect_t *rect, int width, int height);
#endif//__QF_ui_vrect_h

View File

@ -258,3 +258,44 @@ cleanup:
}
return 0;
}
VISIBLE vrect_t *
VRect_SubRect (const vrect_t *rect, int width, int height)
{
if (width > rect->width) {
width = rect->width;
}
if (height > rect->height) {
height = rect->height;
}
int a = rect->height - height;
int b = rect->width - width;
vrect_t *r, *s;
if (b * height > a * width) {
// a horizontal cut produces a larger off-cut rectangle than a vertical
// cut, so do a vertical cut first so the off-cut is as small as
// possible
r = VRect_VSplit (rect, rect->x + width);
if (VRect_IsEmpty (r->next)) {
VRect_Delete (r->next);
r->next = 0;
}
s = VRect_HSplit (r, r->y + height);
} else {
r = VRect_HSplit (rect, rect->y + height);
if (VRect_IsEmpty (r->next)) {
VRect_Delete (r->next);
r->next = 0;
}
s = VRect_VSplit (r, r->x + width);
}
if (VRect_IsEmpty (s->next)) {
VRect_Delete (s->next);
s->next = r->next;
} else {
s->next->next = r->next;
}
VRect_Delete (r);
return s;
}