Commit 1ba0c161 authored by Sam Lantinga's avatar Sam Lantinga

Added an automated test for rectangle routines, currently only testing 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.

--HG--
extra : convert_revision : svn%3Ac70aab31-4412-0410-b14c-859654838e24/trunk%404285
parent 4672f074
...@@ -196,16 +196,40 @@ SDL_EnclosePoints(const SDL_Point * points, int count, const SDL_Rect * clip, ...@@ -196,16 +196,40 @@ SDL_EnclosePoints(const SDL_Point * points, int count, const SDL_Rect * clip,
return SDL_TRUE; 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_bool
SDL_IntersectRectAndLine(const SDL_Rect * rect, int *X1, int *Y1, int *X2, SDL_IntersectRectAndLine(const SDL_Rect * rect, int *X1, int *Y1, int *X2,
int *Y2) int *Y2)
{ {
int x, y;
int x1, y1; int x1, y1;
int x2, y2; int x2, y2;
int rectx1; int rectx1;
int recty1; int recty1;
int rectx2; int rectx2;
int recty2; int recty2;
int outcode1, outcode2;
if (!rect || !X1 || !Y1 || !X2 || !Y2) { if (!rect || !X1 || !Y1 || !X2 || !Y2) {
return SDL_FALSE; return SDL_FALSE;
...@@ -262,95 +286,57 @@ SDL_IntersectRectAndLine(const SDL_Rect * rect, int *X1, int *Y1, int *X2, ...@@ -262,95 +286,57 @@ SDL_IntersectRectAndLine(const SDL_Rect * rect, int *X1, int *Y1, int *X2,
return SDL_TRUE; return SDL_TRUE;
} }
else { /* More complicated Cohen-Sutherland algorithm */
/* The task of clipping a line with finite slope ratios in a fixed- outcode1 = ComputeOutCode(rect, x1, y1);
* precision coordinate space is not as immediately simple as it is outcode2 = ComputeOutCode(rect, x2, y2);
* with coordinates of arbitrary precision. If the ratio of slopes while (outcode1 || outcode2) {
* between the input line segment and the result line segment is not if (outcode1 & outcode2) {
* a whole number, you have in fact *moved* the line segment a bit, return SDL_FALSE;
* 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;
} }
if (*x_result == NULL) if (outcode1) {
return intersection; if (outcode1 & CODE_TOP) {
if (RISING(rectx1, top, rectx2) && RISING(yl, recty1, yh)) { y = recty1;
**(x_result++) = (int) top; x = x1 + ((x2 - x1) * (y - y1)) / (y2 - y1);
**(y_result++) = recty1; } else if (outcode1 & CODE_BOTTOM) {
intersection = SDL_TRUE; 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) if (outcode2) {
return intersection; if (outcode2 & CODE_TOP) {
if (RISING(rectx1, bottom, rectx2) && RISING(yl, recty2, yh)) { y = recty1;
**(x_result++) = (int) bottom; x = x1 + ((x2 - x1) * (y - y1)) / (y2 - y1);
**(y_result++) = recty2; } else if (outcode2 & CODE_BOTTOM) {
intersection = SDL_TRUE; 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;
} }
*X1 = x1;
return SDL_FALSE; *Y1 = y1;
*X2 = x2;
*Y2 = y2;
return SDL_TRUE;
} }
void void
......
...@@ -8,8 +8,9 @@ LDFLAGS := `sdl-config --libs` ...@@ -8,8 +8,9 @@ LDFLAGS := `sdl-config --libs`
#LDFLAGS := -lm -ldl -lesd -lpthread #LDFLAGS := -lm -ldl -lesd -lpthread
SRC := testsdl.c \ SRC := testsdl.c \
rwops/rwops.c \
platform/platform.c \ platform/platform.c \
rwops/rwops.c \
rect/rect.c \
surface/surface.c \ surface/surface.c \
render/render.c \ render/render.c \
audio/audio.c audio/audio.c
...@@ -17,8 +18,9 @@ COMMON_SRC := SDL_at.c common/common.c ...@@ -17,8 +18,9 @@ COMMON_SRC := SDL_at.c common/common.c
COMMON_INCLUDE := SDL_at.h COMMON_INCLUDE := SDL_at.h
TESTS_ALL := testsdl \ TESTS_ALL := testsdl \
rwops/rwops \
platform/platform \ platform/platform \
rwops/rwops \
rect/rect \
surface/surface \ surface/surface \
render/render \ render/render \
audio/audio audio/audio
...@@ -35,11 +37,14 @@ test: all ...@@ -35,11 +37,14 @@ test: all
testsdl: $(SRC) $(COMMON_SRC) testsdl: $(SRC) $(COMMON_SRC)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(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) rwops/rwops: rwops/rwops.c $(COMMON_INCLUDE) $(COMMON_SRC)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ rwops/rwops.c $(COMMON_SRC) -DTEST_STANDALONE $(CC) $(CFLAGS) $(LDFLAGS) -o $@ rwops/rwops.c $(COMMON_SRC) -DTEST_STANDALONE
platform/platform: platform/platform.c $(COMMON_INCLUDE) $(COMMON_SRC) rect/rect: rect/rect.c $(COMMON_INCLUDE) $(COMMON_SRC)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ platform/platform.c $(COMMON_SRC) -DTEST_STANDALONE $(CC) $(CFLAGS) $(LDFLAGS) -o $@ rect/rect.c $(COMMON_SRC) -DTEST_STANDALONE
surface/surface: surface/surface.c $(COMMON_INCLUDE) $(COMMON_SRC) surface/surface: surface/surface.c $(COMMON_INCLUDE) $(COMMON_SRC)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ surface/surface.c $(COMMON_SRC) -DTEST_STANDALONE $(CC) $(CFLAGS) $(LDFLAGS) -o $@ surface/surface.c $(COMMON_SRC) -DTEST_STANDALONE
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
# define _TEST_PLATFORM # define _TEST_PLATFORM
const char *platform_getPlatform (void);
int test_platform (void); int test_platform (void);
......
/**
* 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();
}
/**
* Part of SDL test suite.
*
* Written by Edgar Simo "bobbens"
*
* Released under Public Domain.
*/
#ifndef _TEST_RECT
# define _TEST_RECT
int test_rect (void);
#endif /* _TEST_RECT */
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "platform/platform.h" #include "platform/platform.h"
#include "rwops/rwops.h" #include "rwops/rwops.h"
#include "rect/rect.h"
#include "surface/surface.h" #include "surface/surface.h"
#include "render/render.h" #include "render/render.h"
#include "audio/audio.h" #include "audio/audio.h"
...@@ -41,6 +42,7 @@ static int run_manual = 0; /**< Run manual tests. */ ...@@ -41,6 +42,7 @@ static int run_manual = 0; /**< Run manual tests. */
/* Automatic. */ /* Automatic. */
static int run_platform = 1; /**< Run platform tests. */ static int run_platform = 1; /**< Run platform tests. */
static int run_rwops = 1; /**< Run RWops tests. */ static int run_rwops = 1; /**< Run RWops tests. */
static int run_rect = 1; /**< Run rect tests. */
static int run_surface = 1; /**< Run surface tests. */ static int run_surface = 1; /**< Run surface tests. */
static int run_render = 1; /**< Run render tests. */ static int run_render = 1; /**< Run render tests. */
static int run_audio = 1; /**< Run audio tests. */ static int run_audio = 1; /**< Run audio tests. */
...@@ -55,206 +57,75 @@ static void parse_options( int argc, char *argv[] ); ...@@ -55,206 +57,75 @@ static void parse_options( int argc, char *argv[] );
/** /**
* @brief Displays program usage. * @brief Displays program usage.
*/ */
#ifdef NO_GETOPT
static void print_usage( const char *name )
{
}
#else
#if !defined(NO_GETOPT_LONG)
static void print_usage( const char *name ) static void print_usage( const char *name )
{ {
printf("Usage: %s [OPTIONS]\n", name); printf("Usage: %s [OPTIONS]\n", name);
printf("Options are:\n"); printf("Options are:\n");
printf(" -m, --manual enables tests that require user interaction\n"); printf(" -m, --manual enables tests that require user interaction\n");
printf(" -p, --noplatform do not run the platform tests\n"); printf(" --noplatform do not run the platform tests\n");
printf(" -o, --norwops do not run the rwops tests\n"); printf(" --norwops do not run the rwops tests\n");
printf(" -s, --nosurface do not run the surface tests\n"); printf(" --norect do not run the rect tests\n");
printf(" -r, --norender do not run the render tests\n"); printf(" --nosurface do not run the surface tests\n");
printf(" -a, --noaudio do not run the audio tests\n"); printf(" --norender do not run the render tests\n");
printf(" --noaudio do not run the audio tests\n");
printf(" -v, --verbose increases verbosity level by 1 for each -v\n"); printf(" -v, --verbose increases verbosity level by 1 for each -v\n");
printf(" -q, --quiet only displays errors\n"); printf(" -q, --quiet only displays errors\n");
printf(" -h, --help display this message and exit\n"); printf(" -h, --help display this message and exit\n");
} }
#endif /* !NO_GETOPT_LONG */
#if defined(NO_GETOPT_LONG)
static void print_usage( const char *name )
{
printf("Usage: %s [OPTIONS]\n", name);
printf("Options are:\n");
printf(" -m, enables tests that require user interaction\n");
printf(" -p, do not run the platform tests\n");
printf(" -o, do not run the rwops tests\n");
printf(" -s, do not run the surface tests\n");
printf(" -r, do not run the render tests\n");
printf(" -a, do not run the audio tests\n");
printf(" -v, increases verbosity level by 1 for each -v\n");
printf(" -q, only displays errors\n");
printf(" -h, display this message and exit\n");
}
#endif /* NO_GETOPT_LONG */
#endif /* NO_GETOPT */
/** /**
* @brief Handles the options. * @brief Handles the options.
*/ */
#ifdef NO_GETOPT
static void parse_options( int argc, char *argv[] )
{
}
#else
#if !defined(NO_GETOPT_LONG)
static void parse_options( int argc, char *argv[] ) static void parse_options( int argc, char *argv[] )
{ {
static struct option long_options[] = {
{ "manual", no_argument, 0, 'm' },
{ "noplatform", no_argument, 0, 'p' },
{ "norwops", no_argument, 0, 'o' },
{ "nosurface", no_argument, 0, 's' },
{ "norender", no_argument, 0, 'r' },
{ "noaudio", no_argument, 0, 'a' },
{ "verbose", no_argument, 0, 'v' },
{ "quiet", no_argument, 0, 'q' },
{ "help", no_argument, 0, 'h' },
{NULL,0,0,0}
};
int option_index = 0;
int c = 0;
int i; int i;
const char *str;
/* Iterate over options. */
while ((c = getopt_long( argc, argv,
"mposravqh",
long_options, &option_index)) != -1) {
/* Handle options. */
switch (c) {
case 0:
str = long_options[option_index].name;
if (strcmp(str,"noplatform")==0)
run_platform = 0;
else if (strcmp(str,"norwops")==0)
run_rwops = 0;
else if (strcmp(str,"nosurface")==0)
run_surface = 0;
else if (strcmp(str,"norender")==0)
run_render = 0;
else if (strcmp(str,"noaudio")==0)
run_audio = 0;
break;
/* Manual. */
case 'm':
run_manual = 1;
break;
/* No platform. */
case 'p':
run_platform = 0;
break;
/* No rwops. */
case 'o':
run_rwops = 0;
break;
/* No surface. */
case 's':
run_surface = 0;
break;
/* No render. */
case 'r':
run_render = 0;
break;
/* No audio. */
case 'a':
run_audio = 0;
break;
/* Verbosity. */
case 'v':
SDL_ATgeti( SDL_AT_VERBOSE, &i );
SDL_ATseti( SDL_AT_VERBOSE, i+1 );
break;
/* Quiet. */ for (i = 1; i < argc; ++i) {
case 'q': const char *arg = argv[i];
SDL_ATseti( SDL_AT_QUIET, 1 ); if (SDL_strcmp(arg, "-m") == 0 || SDL_strcmp(arg, "--manual") == 0) {
break; run_manual = 1;
continue;
/* Help. */
case 'h':
print_usage( argv[0] );
exit(EXIT_SUCCESS);
} }
} if (SDL_strcmp(arg, "-v") == 0 || SDL_strcmp(arg, "--verbose") == 0) {
} int level;
#endif /* !NO_GETOPT_LONG */ SDL_ATgeti( SDL_AT_VERBOSE, &level );
SDL_ATseti( SDL_AT_VERBOSE, level+1 );
#if defined(NO_GETOPT_LONG) continue;
static void parse_options( int argc, char *argv[] ) }
{ if (SDL_strcmp(arg, "-q") == 0 || SDL_strcmp(arg, "--quiet") == 0) {
static char* short_options="mposravqh"; SDL_ATseti( SDL_AT_QUIET, 1 );
int c = 0; continue;
int i; }
if (SDL_strcmp(arg, "--noplatform") == 0) {
/* Iterate over options. */ run_platform = 0;
while ((c = getopt(argc, argv, short_options)) != -1) { continue;
/* Handle options. */
switch (c) {
/* Manual. */
case 'm':
run_manual = 1;
break;
/* No platform. */
case 'p':
run_platform = 0;
break;
/* No rwops. */
case 'o':
run_rwops = 0;
break;
/* No surface. */
case 's':
run_surface = 0;
break;
/* No render. */
case 'r':
run_render = 0;
break;
/* No audio. */
case 'a':
run_audio = 0;
break;
/* Verbosity. */
case 'v':
SDL_ATgeti( SDL_AT_VERBOSE, &i );
SDL_ATseti( SDL_AT_VERBOSE, i+1 );
break;
/* Quiet. */
case 'q':
SDL_ATseti( SDL_AT_QUIET, 1 );
break;
/* Help. */
case 'h':
print_usage( argv[0] );
exit(EXIT_SUCCESS);
} }
if (SDL_strcmp(arg, "--norwops") == 0) {
run_rwops = 0;
continue;
}
if (SDL_strcmp(arg, "--norect") == 0) {
run_rect = 0;
continue;
}
if (SDL_strcmp(arg, "--nosurface") == 0) {
run_surface = 0;
continue;
}
if (SDL_strcmp(arg, "--norender") == 0) {
run_render = 0;
continue;
}
if (SDL_strcmp(arg, "--noaudio") == 0) {
run_audio = 0;
continue;
}
/* Print help and exit! */
print_usage( argv[0] );
exit(EXIT_FAILURE);
} }
} }
#endif /* NO_GETOPT_LONG */
#endif /* NO_GETOPT */
/** /**
* @brief Main entry point. * @brief Main entry point.
...@@ -282,6 +153,8 @@ int main( int argc, char *argv[] ) ...@@ -282,6 +153,8 @@ int main( int argc, char *argv[] )
failed += test_platform(); failed += test_platform();
if (run_rwops) if (run_rwops)
failed += test_rwops(); failed += test_rwops();
if (run_rect)
failed += test_rect();
if (run_surface) if (run_surface)
failed += test_surface(); failed += test_surface();
if (run_render) if (run_render)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment