2012-01-06 00:52:14 +00:00
|
|
|
/*
|
|
|
|
vrect.c
|
|
|
|
|
|
|
|
Rectangle manipulation
|
|
|
|
|
|
|
|
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
|
|
|
|
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
|
|
Date: 2012/1/6
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program 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.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to:
|
|
|
|
|
|
|
|
Free Software Foundation, Inc.
|
|
|
|
59 Temple Place - Suite 330
|
|
|
|
Boston, MA 02111-1307, USA
|
|
|
|
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "QF/mathlib.h"
|
2021-06-12 13:50:51 +00:00
|
|
|
|
|
|
|
#include "QF/ui/vrect.h"
|
2012-01-06 00:52:14 +00:00
|
|
|
|
2012-01-06 03:57:25 +00:00
|
|
|
//#define TEST_MEMORY
|
|
|
|
|
2012-01-06 00:52:14 +00:00
|
|
|
#define RECT_BLOCK 128
|
2012-01-06 03:57:25 +00:00
|
|
|
#ifndef TEST_MEMORY
|
2012-01-06 00:52:14 +00:00
|
|
|
static vrect_t *free_rects;
|
2012-01-06 03:57:25 +00:00
|
|
|
#endif
|
2012-01-06 00:52:14 +00:00
|
|
|
|
|
|
|
VISIBLE vrect_t *
|
|
|
|
VRect_New (int x, int y, int width, int height)
|
|
|
|
{
|
|
|
|
vrect_t *r;
|
2012-01-06 03:57:25 +00:00
|
|
|
#ifndef TEST_MEMORY
|
2012-01-06 00:52:14 +00:00
|
|
|
if (!free_rects) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
free_rects = malloc (RECT_BLOCK * sizeof (vrect_t));
|
|
|
|
for (i = 0; i < RECT_BLOCK - 1; i++)
|
|
|
|
free_rects[i].next = &free_rects[i + 1];
|
|
|
|
free_rects[i].next = 0;
|
|
|
|
}
|
|
|
|
r = free_rects;
|
|
|
|
free_rects = free_rects->next;
|
2012-01-06 03:57:25 +00:00
|
|
|
#else
|
|
|
|
r = malloc (sizeof (vrect_t));
|
|
|
|
#endif
|
2012-01-06 00:52:14 +00:00
|
|
|
r->next = 0;
|
|
|
|
r->x = x;
|
|
|
|
r->y = y;
|
|
|
|
r->width = width;
|
|
|
|
r->height = height;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
VISIBLE void
|
|
|
|
VRect_Delete (vrect_t *rect)
|
|
|
|
{
|
2012-01-06 03:57:25 +00:00
|
|
|
#ifndef TEST_MEMORY
|
2012-01-06 00:52:14 +00:00
|
|
|
rect->next = free_rects;
|
|
|
|
free_rects = rect;
|
2012-01-06 03:57:25 +00:00
|
|
|
#else
|
|
|
|
free (rect);
|
|
|
|
#endif
|
2012-01-06 00:52:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VISIBLE vrect_t *
|
|
|
|
VRect_Intersect (const vrect_t *r1, const vrect_t *r2)
|
|
|
|
{
|
|
|
|
int minx = max (VRect_MinX (r1), VRect_MinX (r2));
|
|
|
|
int miny = max (VRect_MinY (r1), VRect_MinY (r2));
|
|
|
|
int maxx = min (VRect_MaxX (r1), VRect_MaxX (r2));
|
|
|
|
int maxy = min (VRect_MaxY (r1), VRect_MaxY (r2));
|
|
|
|
return VRect_New (minx, miny, maxx - minx, maxy - miny);
|
|
|
|
}
|
|
|
|
|
|
|
|
VISIBLE vrect_t *
|
|
|
|
VRect_HSplit (const vrect_t *r, int y)
|
|
|
|
{
|
|
|
|
vrect_t *r1, *r2;
|
|
|
|
int r1miny = VRect_MinY (r);
|
|
|
|
int r1maxy = min (VRect_MaxY (r), y);
|
|
|
|
int r2miny = max (VRect_MinY (r), y);
|
|
|
|
int r2maxy = VRect_MaxY (r);
|
|
|
|
r1 = VRect_New (VRect_MinX (r), r1miny, r->width, r1maxy - r1miny);
|
|
|
|
r2 = VRect_New (VRect_MinX (r), r2miny, r->width, r2maxy - r2miny);
|
|
|
|
r1->next = r2;
|
|
|
|
return r1;
|
|
|
|
}
|
|
|
|
|
|
|
|
VISIBLE vrect_t *
|
|
|
|
VRect_VSplit (const vrect_t *r, int x)
|
|
|
|
{
|
|
|
|
vrect_t *r1, *r2;
|
|
|
|
int r1minx = VRect_MinX (r);
|
|
|
|
int r1maxx = min (VRect_MaxX (r), x);
|
|
|
|
int r2minx = max (VRect_MinX (r), x);
|
|
|
|
int r2maxx = VRect_MaxX (r);
|
|
|
|
r1 = VRect_New (r1minx, VRect_MinY (r), r1maxx - r1minx, r->height);
|
|
|
|
r2 = VRect_New (r2minx, VRect_MinY (r), r2maxx - r2minx, r->height);
|
|
|
|
r1->next = r2;
|
|
|
|
return r1;
|
|
|
|
}
|
|
|
|
|
|
|
|
VISIBLE vrect_t *
|
|
|
|
VRect_Difference (const vrect_t *r, const vrect_t *s)
|
|
|
|
{
|
|
|
|
vrect_t *i, *t, *_r;
|
|
|
|
vrect_t *rects = 0;
|
|
|
|
#define STASH(t) \
|
|
|
|
do { \
|
|
|
|
if (!VRect_IsEmpty (t)) { \
|
|
|
|
t->next = rects; \
|
|
|
|
rects = t; \
|
|
|
|
} else { \
|
|
|
|
VRect_Delete (t); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
// Find the intersection of r (original rect) and s (the rect being
|
|
|
|
// subtracted from r). The intersection rect is used to carve the original
|
|
|
|
// rect. If there is no intersection (the intersection rect is empty),
|
|
|
|
// then there is nothing to subtract and the original rect (actually, a
|
|
|
|
// copy of it) is returned.
|
|
|
|
i = VRect_Intersect (r, s);
|
|
|
|
if (VRect_IsEmpty (i)) {
|
|
|
|
// copy r's shape to i, and return i.
|
|
|
|
i->x = r->x;
|
|
|
|
i->y = r->y;
|
|
|
|
i->width = r->width;
|
|
|
|
i->height = r->height;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Split r along the top of the intersection rect and stash the top
|
|
|
|
// section if it is not empty.
|
|
|
|
t = VRect_HSplit (r, VRect_MinY (i));
|
|
|
|
// can't delete r: we don't own it
|
|
|
|
_r = t->next;
|
|
|
|
STASH (t); // maybe stash the top section
|
|
|
|
// r now represents the portion of the original rect below the top of
|
|
|
|
// the intersection rect.
|
|
|
|
|
|
|
|
// Split r along the bottom of the intersection rect and stash the bottom
|
|
|
|
// section if it is not empty.
|
|
|
|
t = VRect_HSplit (_r, VRect_MaxY (i));
|
|
|
|
VRect_Delete (_r); // finished with that copy of _r
|
|
|
|
_r = t;
|
|
|
|
STASH (t->next); // maybe stash the bottom section
|
|
|
|
// r now represents the horizontal section that is common with the
|
|
|
|
// intersection rect.
|
|
|
|
|
|
|
|
// Split r along the left side of tht intersection rect and stash the
|
|
|
|
// left section if it is not empty.
|
|
|
|
t = VRect_VSplit (_r, VRect_MinX (i));
|
|
|
|
VRect_Delete (_r); // finished with that copy of _r
|
|
|
|
_r = t->next;
|
|
|
|
STASH (t); // maybe stash the left section
|
|
|
|
// r now represets the section of the original rect that is between the
|
|
|
|
// top and bottom, and to the right of the left side of the intersection
|
|
|
|
// rect.
|
|
|
|
|
|
|
|
// Split r along the right side of the intersection rect and stash the
|
|
|
|
// right section if it is not empty. The left section is discarded.
|
|
|
|
t = VRect_VSplit (_r, VRect_MaxX (i));
|
|
|
|
VRect_Delete (_r); // finished with that copy of _r
|
|
|
|
STASH (t->next); // maybe stash the right section
|
|
|
|
VRect_Delete (t); // discard the left section
|
|
|
|
|
2012-01-06 02:04:50 +00:00
|
|
|
VRect_Delete (i); // finished with the intersection rect
|
|
|
|
|
2012-01-06 00:52:14 +00:00
|
|
|
return rects;
|
|
|
|
}
|
2012-01-06 03:57:25 +00:00
|
|
|
|
|
|
|
VISIBLE vrect_t *
|
|
|
|
VRect_Union (const vrect_t *r1, const vrect_t *r2)
|
|
|
|
{
|
|
|
|
int minx, miny;
|
|
|
|
int maxx, maxy;
|
|
|
|
|
|
|
|
if (VRect_IsEmpty (r1))
|
|
|
|
return VRect_New (r2->x, r2->y, r2->width, r2->height);
|
|
|
|
|
|
|
|
if (VRect_IsEmpty (r2))
|
|
|
|
return VRect_New (r1->x, r1->y, r1->width, r1->height);
|
|
|
|
|
|
|
|
minx = min (VRect_MinX (r1), VRect_MinX (r2));
|
|
|
|
miny = min (VRect_MinY (r1), VRect_MinY (r2));
|
|
|
|
maxx = max (VRect_MaxX (r1), VRect_MaxX (r2));
|
|
|
|
maxy = max (VRect_MaxY (r1), VRect_MaxY (r2));
|
|
|
|
return VRect_New (minx, miny, maxx - minx, maxy - miny);
|
|
|
|
}
|
|
|
|
|
|
|
|
VISIBLE vrect_t *
|
|
|
|
VRect_Merge (const vrect_t *r1, const vrect_t *r2)
|
|
|
|
{
|
|
|
|
vrect_t *t, *u = 0;
|
|
|
|
vrect_t *merge;
|
|
|
|
|
|
|
|
// cannot merge intersecting rectangles
|
|
|
|
t = VRect_Intersect (r1, r2);
|
|
|
|
if (!VRect_IsEmpty (t)) {
|
|
|
|
VRect_Delete (t);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
VRect_Delete (t);
|
|
|
|
|
|
|
|
merge = VRect_Union (r1, r2);
|
|
|
|
if (VRect_IsEmpty (merge)) {
|
|
|
|
VRect_Delete (merge);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the subtracting r1 from the union results in more than one rectangle
|
|
|
|
// then r1 and r2 cannot be merged.
|
|
|
|
t = VRect_Difference (merge, r1);
|
|
|
|
if (t && t->next)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
// If subtracting r2 from the result of the previous difference results in
|
|
|
|
// any rectangles, then r1 and r2 cannot be merged.
|
|
|
|
if (t)
|
|
|
|
u = VRect_Difference (t, r2);
|
|
|
|
if (!u) {
|
2012-01-24 10:56:59 +00:00
|
|
|
if (t)
|
|
|
|
VRect_Delete (t);
|
2012-01-06 03:57:25 +00:00
|
|
|
return merge;
|
|
|
|
}
|
|
|
|
cleanup:
|
|
|
|
VRect_Delete (merge);
|
|
|
|
// merge is used as a temp while freeing t and u
|
|
|
|
while (u) {
|
|
|
|
merge = u->next;
|
|
|
|
VRect_Delete (u);
|
|
|
|
u = merge;
|
|
|
|
}
|
|
|
|
while (t) {
|
|
|
|
merge = t->next;
|
|
|
|
VRect_Delete (t);
|
|
|
|
t = merge;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2022-09-02 08:47:27 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2022-09-20 10:32:49 +00:00
|
|
|
// First check for exact fits as no heuristics are necessary
|
|
|
|
if (width == rect->width) {
|
|
|
|
if (height == rect->height) {
|
|
|
|
// Exact fit, return a new rect of the same size
|
|
|
|
return VRect_New (rect->x, rect->y, rect->width, rect->height);
|
|
|
|
}
|
|
|
|
|
|
|
|
vrect_t *r = VRect_HSplit (rect, rect->y + height);
|
|
|
|
if (VRect_IsEmpty (r->next)) {
|
|
|
|
VRect_Delete (r->next);
|
|
|
|
r->next = 0;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
} else if (height == rect->height) {
|
|
|
|
// Because rect's width ks known to be greater than the requested
|
|
|
|
// width, the split is guaranteed to produce two non-empty rectangles.
|
|
|
|
return VRect_VSplit (rect, rect->x + width);
|
|
|
|
}
|
|
|
|
|
2022-09-02 08:47:27 +00:00
|
|
|
int a = rect->height - height;
|
|
|
|
int b = rect->width - width;
|
|
|
|
vrect_t *r, *s;
|
2022-09-20 10:32:49 +00:00
|
|
|
if (b * height <= a * width || 16 * a > 15 * rect->height) {
|
|
|
|
// horizontal cuts produce better memory locality so favor them
|
|
|
|
r = VRect_HSplit (rect, rect->y + height);
|
2022-09-02 08:47:27 +00:00
|
|
|
if (VRect_IsEmpty (r->next)) {
|
|
|
|
VRect_Delete (r->next);
|
|
|
|
r->next = 0;
|
|
|
|
}
|
2022-09-20 10:32:49 +00:00
|
|
|
s = VRect_VSplit (r, r->x + width);
|
2022-09-02 08:47:27 +00:00
|
|
|
} else {
|
2022-09-20 10:32:49 +00:00
|
|
|
// 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);
|
2022-09-02 08:47:27 +00:00
|
|
|
if (VRect_IsEmpty (r->next)) {
|
|
|
|
VRect_Delete (r->next);
|
|
|
|
r->next = 0;
|
|
|
|
}
|
2022-09-20 10:32:49 +00:00
|
|
|
s = VRect_HSplit (r, r->y + height);
|
2022-09-02 08:47:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (VRect_IsEmpty (s->next)) {
|
|
|
|
VRect_Delete (s->next);
|
|
|
|
s->next = r->next;
|
|
|
|
} else {
|
|
|
|
s->next->next = r->next;
|
|
|
|
}
|
|
|
|
VRect_Delete (r);
|
|
|
|
return s;
|
|
|
|
}
|