Skip to content
This repository has been archived by the owner on Feb 11, 2021. It is now read-only.

Commit

Permalink
Added an automated test for rectangle routines, currently only testin…
Browse files Browse the repository at this point in the history
…g line clipping.

Use the Cohen-Sutherland algorithm for line clipping which uses integer math and preserves ordering of clipped points.

Removed getopt() support in testsdl.c, replaced with simple argv scanning.
  • Loading branch information
slouken committed Dec 11, 2009
1 parent 2864160 commit 678acaa
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 267 deletions.
152 changes: 69 additions & 83 deletions src/video/SDL_rect.c
Expand Up @@ -196,16 +196,40 @@ SDL_EnclosePoints(const SDL_Point * points, int count, const SDL_Rect * clip,
return SDL_TRUE;
}

/* Use the Cohen-Sutherland algorithm for line clipping */
#define CODE_BOTTOM 1
#define CODE_TOP 2
#define CODE_LEFT 4
#define CODE_RIGHT 8

static int ComputeOutCode(const SDL_Rect * rect, int x, int y)
{
int code = 0;
if (y < 0) {
code |= CODE_TOP;
} else if (y >= rect->y + rect->h) {
code |= CODE_BOTTOM;
}
if (x < 0) {
code |= CODE_LEFT;
} else if (x >= rect->x + rect->w) {
code |= CODE_RIGHT;
}
return code;
}

SDL_bool
SDL_IntersectRectAndLine(const SDL_Rect * rect, int *X1, int *Y1, int *X2,
int *Y2)
{
int x, y;
int x1, y1;
int x2, y2;
int rectx1;
int recty1;
int rectx2;
int recty2;
int outcode1, outcode2;

if (!rect || !X1 || !Y1 || !X2 || !Y2) {
return SDL_FALSE;
Expand Down Expand Up @@ -262,95 +286,57 @@ SDL_IntersectRectAndLine(const SDL_Rect * rect, int *X1, int *Y1, int *X2,
return SDL_TRUE;
}

else {
/* The task of clipping a line with finite slope ratios in a fixed-
* precision coordinate space is not as immediately simple as it is
* with coordinates of arbitrary precision. If the ratio of slopes
* between the input line segment and the result line segment is not
* a whole number, you have in fact *moved* the line segment a bit,
* and there can be no avoiding it without more precision
*/
int *x_result_[] = { X1, X2, NULL }, **x_result = x_result_;
int *y_result_[] = { Y1, Y2, NULL }, **y_result = y_result_;
SDL_bool intersection = SDL_FALSE;
double b, m, left, right, bottom, top;
int xl, xh, yl, yh;

/* solve mx+b line formula */
m = (double) (y1 - y2) / (double) (x1 - x2);
b = y2 - m * (double) x2;

/* find some linear intersections */
left = (m * (double) rectx1) + b;
right = (m * (double) rectx2) + b;
top = (recty1 - b) / m;
bottom = (recty2 - b) / m;

/* sort end-points' x and y components individually */
if (x1 < x2) {
xl = x1;
xh = x2;
} else {
xl = x2;
xh = x1;
}
if (y1 < y2) {
yl = y1;
yh = y2;
} else {
yl = y2;
yh = y1;
}

#define RISING(a, b, c) (((a)<=(b))&&((b)<=(c)))

/* check for a point that's entirely inside the rect */
if (RISING(rectx1, x1, rectx2) && RISING(recty1, y1, recty2)) {
x_result++;
y_result++;
intersection = SDL_TRUE;
} else
/* it was determined earlier that *both* end-points are not contained */
if (RISING(rectx1, x2, rectx2) && RISING(recty1, y2, recty2)) {
**(x_result++) = x2;
**(y_result++) = y2;
intersection = SDL_TRUE;
}

if (RISING(recty1, left, recty2) && RISING(xl, rectx1, xh)) {
**(x_result++) = rectx1;
**(y_result++) = (int) left;
intersection = SDL_TRUE;
}

if (*x_result == NULL)
return intersection;
if (RISING(recty1, right, recty2) && RISING(xl, rectx2, xh)) {
**(x_result++) = rectx2;
**(y_result++) = (int) right;
intersection = SDL_TRUE;
/* More complicated Cohen-Sutherland algorithm */
outcode1 = ComputeOutCode(rect, x1, y1);
outcode2 = ComputeOutCode(rect, x2, y2);
while (outcode1 || outcode2) {
if (outcode1 & outcode2) {
return SDL_FALSE;
}

if (*x_result == NULL)
return intersection;
if (RISING(rectx1, top, rectx2) && RISING(yl, recty1, yh)) {
**(x_result++) = (int) top;
**(y_result++) = recty1;
intersection = SDL_TRUE;
if (outcode1) {
if (outcode1 & CODE_TOP) {
y = recty1;
x = x1 + ((x2 - x1) * (y - y1)) / (y2 - y1);
} else if (outcode1 & CODE_BOTTOM) {
y = recty2;
x = x1 + ((x2 - x1) * (y - y1)) / (y2 - y1);
} else if (outcode1 & CODE_LEFT) {
x = rectx1;
y = y1 + ((y2 - y1) * (x - x1)) / (x2 - x1);
} else if (outcode1 & CODE_RIGHT) {
x = rectx2;
y = y1 + ((y2 - y1) * (x - x1)) / (x2 - x1);
}
x1 = x;
y1 = y;
outcode1 = ComputeOutCode(rect, x, y);
}

if (*x_result == NULL)
return intersection;
if (RISING(rectx1, bottom, rectx2) && RISING(yl, recty2, yh)) {
**(x_result++) = (int) bottom;
**(y_result++) = recty2;
intersection = SDL_TRUE;
if (outcode2) {
if (outcode2 & CODE_TOP) {
y = recty1;
x = x1 + ((x2 - x1) * (y - y1)) / (y2 - y1);
} else if (outcode2 & CODE_BOTTOM) {
y = recty2;
x = x1 + ((x2 - x1) * (y - y1)) / (y2 - y1);
} else if (outcode2 & CODE_LEFT) {
x = rectx1;
y = y1 + ((y2 - y1) * (x - x1)) / (x2 - x1);
} else if (outcode2 & CODE_RIGHT) {
x = rectx2;
y = y1 + ((y2 - y1) * (x - x1)) / (x2 - x1);
}
x2 = x;
y2 = y;
outcode2 = ComputeOutCode(rect, x, y);
}

return intersection;
}

return SDL_FALSE;
*X1 = x1;
*Y1 = y1;
*X2 = x2;
*Y2 = y2;
return SDL_TRUE;
}

void
Expand Down
13 changes: 9 additions & 4 deletions test/automated/Makefile
Expand Up @@ -8,17 +8,19 @@ LDFLAGS := `sdl-config --libs`
#LDFLAGS := -lm -ldl -lesd -lpthread

SRC := testsdl.c \
rwops/rwops.c \
platform/platform.c \
rwops/rwops.c \
rect/rect.c \
surface/surface.c \
render/render.c \
audio/audio.c
COMMON_SRC := SDL_at.c common/common.c
COMMON_INCLUDE := SDL_at.h

TESTS_ALL := testsdl \
rwops/rwops \
platform/platform \
rwops/rwops \
rect/rect \
surface/surface \
render/render \
audio/audio
Expand All @@ -35,11 +37,14 @@ test: all
testsdl: $(SRC) $(COMMON_SRC)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(SRC) $(COMMON_SRC)

platform/platform: platform/platform.c $(COMMON_INCLUDE) $(COMMON_SRC)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ platform/platform.c $(COMMON_SRC) -DTEST_STANDALONE

rwops/rwops: rwops/rwops.c $(COMMON_INCLUDE) $(COMMON_SRC)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ rwops/rwops.c $(COMMON_SRC) -DTEST_STANDALONE

platform/platform: platform/platform.c $(COMMON_INCLUDE) $(COMMON_SRC)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ platform/platform.c $(COMMON_SRC) -DTEST_STANDALONE
rect/rect: rect/rect.c $(COMMON_INCLUDE) $(COMMON_SRC)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ rect/rect.c $(COMMON_SRC) -DTEST_STANDALONE

surface/surface: surface/surface.c $(COMMON_INCLUDE) $(COMMON_SRC)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ surface/surface.c $(COMMON_SRC) -DTEST_STANDALONE
Expand Down
1 change: 0 additions & 1 deletion test/automated/platform/platform.h
Expand Up @@ -11,7 +11,6 @@
# define _TEST_PLATFORM


const char *platform_getPlatform (void);
int test_platform (void);


Expand Down
156 changes: 156 additions & 0 deletions test/automated/rect/rect.c
@@ -0,0 +1,156 @@
/**
* Automated SDL rect test.
*
* Written by Edgar Simo "bobbens"
*
* Released under Public Domain.
*/




#include "SDL_rect.h"
#include "../SDL_at.h"


/*
* Prototypes.
*/
static void rect_testIntersectRectAndLine (void);


/**
* @brief Tests SDL_IntersectRectAndLine()
*/
static void rect_testIntersectRectAndLine (void)
{
SDL_Rect rect = { 0, 0, 32, 32 };
int x1, y1;
int x2, y2;
SDL_bool clipped;

SDL_ATbegin( "IntersectRectAndLine" );

x1 = -10;
y1 = 0;
x2 = -10;
y2 = 31;
clipped = SDL_IntersectRectAndLine(&rect, &x1, &y1, &x2, &y2);
SDL_ATvassert( !clipped &&
x1 == -10 && y1 == 0 && x2 == -10 && y2 == 31,
"line outside to the left was incorrectly clipped: %d,%d - %d,%d",
x1, y1, x2, y2);

x1 = 40;
y1 = 0;
x2 = 40;
y2 = 31;
clipped = SDL_IntersectRectAndLine(&rect, &x1, &y1, &x2, &y2);
SDL_ATvassert( !clipped &&
x1 == 40 && y1 == 0 && x2 == 40 && y2 == 31,
"line outside to the right was incorrectly clipped: %d,%d - %d,%d",
x1, y1, x2, y2);

x1 = 0;
y1 = -10;
x2 = 31;
y2 = -10;
clipped = SDL_IntersectRectAndLine(&rect, &x1, &y1, &x2, &y2);
SDL_ATvassert( !clipped &&
x1 == 0 && y1 == -10 && x2 == 31 && y2 == -10,
"line outside above was incorrectly clipped: %d,%d - %d,%d",
x1, y1, x2, y2);

x1 = 0;
y1 = 40;
x2 = 31;
y2 = 40;
clipped = SDL_IntersectRectAndLine(&rect, &x1, &y1, &x2, &y2);
SDL_ATvassert( !clipped &&
x1 == 0 && y1 == 40 && x2 == 31 && y2 == 40,
"line outside below was incorrectly clipped: %d,%d - %d,%d",
x1, y1, x2, y2);

x1 = 0;
y1 = 0;
x2 = 31;
y2 = 31;
clipped = SDL_IntersectRectAndLine(&rect, &x1, &y1, &x2, &y2);
SDL_ATvassert( clipped &&
x1 == 0 && y1 == 0 && x2 == 31 && y2 == 31,
"line fully inside rect was clipped: %d,%d - %d,%d",
x1, y1, x2, y2);

x1 = -10;
y1 = 15;
x2 = 40;
y2 = 15;
clipped = SDL_IntersectRectAndLine(&rect, &x1, &y1, &x2, &y2);
SDL_ATvassert( clipped &&
x1 == 0 && y1 == 15 && x2 == 31 && y2 == 15,
"horizontal line rect was incorrectly clipped: %d,%d - %d,%d",
x1, y1, x2, y2);

x1 = -32;
y1 = -32;
x2 = 63;
y2 = 63;
clipped = SDL_IntersectRectAndLine(&rect, &x1, &y1, &x2, &y2);
SDL_ATvassert( clipped &&
x1 == 0 && y1 == 0 && x2 == 31 && y2 == 31,
"diagonal line to lower right was incorrectly clipped: %d,%d - %d,%d",
x1, y1, x2, y2);

x1 = 63;
y1 = 63;
x2 = -32;
y2 = -32;
clipped = SDL_IntersectRectAndLine(&rect, &x1, &y1, &x2, &y2);
SDL_ATvassert( clipped &&
x1 == 31 && y1 == 31 && x2 == 0 && y2 == 0,
"diagonal line to upper left was incorrectly clipped: %d,%d - %d,%d",
x1, y1, x2, y2);

x1 = 63;
y1 = -32;
x2 = -32;
y2 = 63;
clipped = SDL_IntersectRectAndLine(&rect, &x1, &y1, &x2, &y2);
SDL_ATvassert( clipped &&
x1 == 31 && y1 == 0 && x2 == 0 && y2 == 31,
"diagonal line to lower left was incorrectly clipped: %d,%d - %d,%d",
x1, y1, x2, y2);

x1 = -32;
y1 = 63;
x2 = 63;
y2 = -32;
clipped = SDL_IntersectRectAndLine(&rect, &x1, &y1, &x2, &y2);
SDL_ATvassert( clipped &&
x1 == 0 && y1 == 31 && x2 == 31 && y2 == 0,
"diagonal line to upper right was incorrectly clipped: %d,%d - %d,%d",
x1, y1, x2, y2);

SDL_ATend();
}


/**
* @brief Rect test entrypoint.
*/
#ifdef TEST_STANDALONE
int main( int argc, const char *argv[] )
{
(void) argc;
(void) argv;
#else /* TEST_STANDALONE */
int test_rect (void)
{
#endif /* TEST_STANDALONE */

SDL_ATinit( "Rect" );

rect_testIntersectRectAndLine();

return SDL_ATfinish();
}

0 comments on commit 678acaa

Please sign in to comment.