Add union and merge functions and tests.

This commit is contained in:
Bill Currie 2012-01-06 12:57:25 +09:00
parent 73f2c12815
commit c25327f4ed
3 changed files with 138 additions and 1 deletions

View file

@ -137,4 +137,33 @@ vrect_t *VRect_VSplit (const vrect_t *r, int x);
*/
vrect_t *VRect_Difference (const vrect_t *r, const vrect_t *s);
/** Return the union of the two rectangles.
The union rectangle will be big enough to entirely contain both \a r1
and \a r2. An empty rectangle will be returned if both rectangles are
empty.
\param r1 The first rectangle.
\param r2 The second rectangle.
\return The union of the two rectangles.
\note It is the caller's responsibility to delete the returned rectangle.
*/
vrect_t *VRect_Union (const vrect_t *r1, const vrect_t *r2);
/** Return the rectangle representing the merge of the two given rectangles.
If the two given rectangles cannot be perfectly joined (either they
intesect or subtracting them from the merged rectangle would result
in scrap rectangles, null will be returned. Two empty rectangles will
result in null being returned, but only one empty rectangle will result
in a copy of the non-empty rectangle.
\param r1 The first rectangle to be merged.
\param r2 The second rectangle to be merged.
\return The merged rectangle, or null if the given rectangles
cannot be merged.
\note It is the caller's responsibility to delete the returned rectangle.
*/
vrect_t *VRect_Merge (const vrect_t *r, const vrect_t *s);
#endif//__QF_vrect_h

View file

@ -95,6 +95,31 @@ struct {
{VRect_Difference, VR (3,3,2,2), VR (1,1,3,3), &de_15_0},
{VRect_Difference, VR (4,4,2,2), VR (1,1,3,3), &de_16_0},
{VRect_Union, VR (0, 0, 0, 0), VR (1, 1, -1, 1), 0, VR (1, 1, 1, -1), 1},
{VRect_Union, VR (0, 0, 0, 0), VR (1, 1, 3, 3), 0, VR (1, 1, 3, 3), 1},
{VRect_Union, VR (0, 0, 2, 5), VR (1, 1, -1, 1), 0, VR (0, 0, 2, 5), 1},
{VRect_Union, VR (0, 0, 2, 5), VR (1, 1, 3, 3), 0, VR (0, 0, 4, 5), 1},
{VRect_Merge, VR (0, 0, 0, 0), VR (1, 1, -1, 1), 0},
{VRect_Merge, VR (0, 0, 0, 0), VR (1, 1, 3, 3), 0, VR (1, 1, 3, 3), 1},
{VRect_Merge, VR (0, 0, 2, 5), VR (1, 1, -1, 1), 0, VR (0, 0, 2, 5), 1},
{VRect_Merge, VR (0, 0, 2, 5), VR (1, 1, 3, 3), 0},
{VRect_Merge, VR (0,0,2,2), VR (1,1,3,3), 0},
{VRect_Merge, VR (0,0,5,2), VR (1,1,3,3), 0},
{VRect_Merge, VR (2,0,2,2), VR (1,1,3,3), 0},
{VRect_Merge, VR (3,0,2,2), VR (1,1,3,3), 0},
{VRect_Merge, VR (0,0,4,1), VR (1,1,3,3), 0},
{VRect_Merge, VR (1,-1,3,1), VR (1,1,3,3), 0},
{VRect_Merge, VR (0,4,4,1), VR (1,1,3,3), 0},
{VRect_Merge, VR (1,5,3,1), VR (1,1,3,3), 0},
{VRect_Merge, VR (1,0,3,1), VR (1,1,3,3), 0, VR (1,0,3,4), 1},
{VRect_Merge, VR (1,4,3,1), VR (1,1,3,3), 0, VR (1,1,3,4), 1},
{VRect_Merge, VR (0,1,1,3), VR (1,1,3,3), 0, VR (0,1,4,3), 1},
{VRect_Merge, VR (4,1,1,3), VR (1,1,3,3), 0, VR (1,1,4,3), 1},
};
#define num_tests (sizeof (tests) / sizeof (tests[0]))
@ -116,6 +141,9 @@ compare_rects (vrect_t *r1, vrect_t *r2)
return 1;
if (!r1 || !r2)
return 0;
// when both rects are empty, their exact values don't matter.
if (VRect_IsEmpty (r1) && VRect_IsEmpty (r2))
return 1;
if (r1->x != r2->x || r1->y != r2->y
|| r1->width != r2->width || r1->height != r2->height)
return 0;

View file

@ -38,14 +38,18 @@ static __attribute__ ((used)) const char rcsid[] = "$Id$";
#include "QF/mathlib.h"
#include "QF/vrect.h"
//#define TEST_MEMORY
#define RECT_BLOCK 128
#ifndef TEST_MEMORY
static vrect_t *free_rects;
#endif
VISIBLE vrect_t *
VRect_New (int x, int y, int width, int height)
{
vrect_t *r;
#ifndef TEST_MEMORY
if (!free_rects) {
int i;
@ -56,6 +60,9 @@ VRect_New (int x, int y, int width, int height)
}
r = free_rects;
free_rects = free_rects->next;
#else
r = malloc (sizeof (vrect_t));
#endif
r->next = 0;
r->x = x;
r->y = y;
@ -67,8 +74,12 @@ VRect_New (int x, int y, int width, int height)
VISIBLE void
VRect_Delete (vrect_t *rect)
{
#ifndef TEST_MEMORY
rect->next = free_rects;
free_rects = rect;
#else
free (rect);
#endif
}
VISIBLE vrect_t *
@ -178,3 +189,72 @@ VRect_Difference (const vrect_t *r, const vrect_t *s)
return rects;
}
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) {
VRect_Delete (t);
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;
}