From 28e9a0a9ba250bc3c029f25716c2d45a30ff18db Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Fri, 2 Sep 2022 17:47:27 +0900 Subject: [PATCH] [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. --- include/QF/ui/vrect.h | 23 +++++++++++++++++++++++ libs/ui/vrect.c | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/include/QF/ui/vrect.h b/include/QF/ui/vrect.h index 64d3b6302..c05bc51f9 100644 --- a/include/QF/ui/vrect.h +++ b/include/QF/ui/vrect.h @@ -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 diff --git a/libs/ui/vrect.c b/libs/ui/vrect.c index cbfe72595..f9ff47763 100644 --- a/libs/ui/vrect.c +++ b/libs/ui/vrect.c @@ -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; +}