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; +}