From 678acaa076723c50207f7dd2b07341bef8d35c3d Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 11 Dec 2009 09:22:34 +0000 Subject: [PATCH] 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. --- src/video/SDL_rect.c | 152 +++++++++---------- test/automated/Makefile | 13 +- test/automated/platform/platform.h | 1 - test/automated/rect/rect.c | 156 +++++++++++++++++++ test/automated/rect/rect.h | 18 +++ test/automated/testsdl.c | 231 +++++++---------------------- 6 files changed, 304 insertions(+), 267 deletions(-) create mode 100644 test/automated/rect/rect.c create mode 100644 test/automated/rect/rect.h diff --git a/src/video/SDL_rect.c b/src/video/SDL_rect.c index 80a71b3da..52948f079 100644 --- a/src/video/SDL_rect.c +++ b/src/video/SDL_rect.c @@ -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; @@ -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 diff --git a/test/automated/Makefile b/test/automated/Makefile index 7ea6d1506..02e26d3f6 100644 --- a/test/automated/Makefile +++ b/test/automated/Makefile @@ -8,8 +8,9 @@ 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 @@ -17,8 +18,9 @@ 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 @@ -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 diff --git a/test/automated/platform/platform.h b/test/automated/platform/platform.h index bb7e3499c..d8310e0c0 100644 --- a/test/automated/platform/platform.h +++ b/test/automated/platform/platform.h @@ -11,7 +11,6 @@ # define _TEST_PLATFORM -const char *platform_getPlatform (void); int test_platform (void); diff --git a/test/automated/rect/rect.c b/test/automated/rect/rect.c new file mode 100644 index 000000000..3ae2fc534 --- /dev/null +++ b/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(); +} diff --git a/test/automated/rect/rect.h b/test/automated/rect/rect.h new file mode 100644 index 000000000..9a0806151 --- /dev/null +++ b/test/automated/rect/rect.h @@ -0,0 +1,18 @@ +/** + * 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 */ + diff --git a/test/automated/testsdl.c b/test/automated/testsdl.c index cb90647a8..c38fbda3d 100644 --- a/test/automated/testsdl.c +++ b/test/automated/testsdl.c @@ -12,6 +12,7 @@ #include "platform/platform.h" #include "rwops/rwops.h" +#include "rect/rect.h" #include "surface/surface.h" #include "render/render.h" #include "audio/audio.h" @@ -41,6 +42,7 @@ static int run_manual = 0; /**< Run manual tests. */ /* Automatic. */ static int run_platform = 1; /**< Run platform 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_render = 1; /**< Run render tests. */ static int run_audio = 1; /**< Run audio tests. */ @@ -55,206 +57,75 @@ static void parse_options( int argc, char *argv[] ); /** * @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 ) { printf("Usage: %s [OPTIONS]\n", name); printf("Options are:\n"); printf(" -m, --manual enables tests that require user interaction\n"); - printf(" -p, --noplatform do not run the platform tests\n"); - printf(" -o, --norwops do not run the rwops tests\n"); - printf(" -s, --nosurface do not run the surface tests\n"); - printf(" -r, --norender do not run the render tests\n"); - printf(" -a, --noaudio do not run the audio tests\n"); + printf(" --noplatform do not run the platform tests\n"); + printf(" --norwops do not run the rwops tests\n"); + printf(" --norect do not run the rect tests\n"); + printf(" --nosurface do not run the surface 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(" -q, --quiet only displays errors\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. */ -#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 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; - 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. */ - case 'q': - SDL_ATseti( SDL_AT_QUIET, 1 ); - break; - - /* Help. */ - case 'h': - print_usage( argv[0] ); - exit(EXIT_SUCCESS); + for (i = 1; i < argc; ++i) { + const char *arg = argv[i]; + if (SDL_strcmp(arg, "-m") == 0 || SDL_strcmp(arg, "--manual") == 0) { + run_manual = 1; + continue; } - } -} -#endif /* !NO_GETOPT_LONG */ - -#if defined(NO_GETOPT_LONG) -static void parse_options( int argc, char *argv[] ) -{ - static char* short_options="mposravqh"; - int c = 0; - int i; - - /* Iterate over options. */ - while ((c = getopt(argc, argv, short_options)) != -1) { - /* 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, "-v") == 0 || SDL_strcmp(arg, "--verbose") == 0) { + int level; + SDL_ATgeti( SDL_AT_VERBOSE, &level ); + SDL_ATseti( SDL_AT_VERBOSE, level+1 ); + continue; + } + if (SDL_strcmp(arg, "-q") == 0 || SDL_strcmp(arg, "--quiet") == 0) { + SDL_ATseti( SDL_AT_QUIET, 1 ); + continue; + } + if (SDL_strcmp(arg, "--noplatform") == 0) { + run_platform = 0; + continue; } + 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. @@ -282,6 +153,8 @@ int main( int argc, char *argv[] ) failed += test_platform(); if (run_rwops) failed += test_rwops(); + if (run_rect) + failed += test_rect(); if (run_surface) failed += test_surface(); if (run_render)