Mostly works now.
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 17 Jan 2008 10:46:29 +0000 (21:46 +1100)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 17 Jan 2008 10:46:29 +0000 (21:46 +1100)
prpong.c

index c62d0fcced8827bfc3881fe0e0ee2b03a17257ac..09d5113cf191efce46edfae000300ebe14a708ca 100644 (file)
--- a/prpong.c
+++ b/prpong.c
@@ -4,66 +4,62 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdbool.h>
+#include <sys/time.h>
+#include <time.h>
+#include <limits.h>
 #include "stdrusty.h"
+#include "list/list.h"
 #include <SDL/SDL.h>
+#include <cwiid.h>
 
-#define MAX_X 512
-#define MAX_Y 512
+#define MAX_X CWIID_IR_X_MAX
+#define MAX_Y CWIID_IR_Y_MAX
 
 /* Obtained from http://tog.acm.org/GraphicsGems/gemsiii/insectc.c */
 /* Faster Line Segment Intersection   */
 /* Franklin Antonio                   */
 
-/* return values */
-#define DONT_INTERSECT 0
-#define DO_INTERSECT   1
-#define PARALLEL       2
-
-/* The SAME_SIGNS macro assumes arithmetic where the exclusive-or    */
-/* operation will work on sign bits.  This works for twos-complement,*/
-/* and most other machine arithmetic.                                */
-#define SAME_SIGNS( a, b )                                             \
-       (((long) ((unsigned long) a ^ (unsigned long) b)) >= 0 )
-
-
 /* The use of some short working variables allows this code to run   */
 /* faster on 16-bit computers, but is not essential.  It should not  */
 /* affect operation on 32-bit computers.  The short working variables*/
 /* to not restrict the range of valid input values, as these were    */
 /* constrained in any case, due to algorithm restrictions.           */
-static int lines_intersect(long x1, long y1, long x2, long y2, long x3, long y3,
-                          long x4,long y4, long *x, long *y) 
+static bool lines_intersect(double x1, double y1,
+                           double x2, double y2,
+                           double x3, double y3,
+                           double x4, double y4,
+                           double *x, double *y) 
 {
 
-       long Ax,Bx,Cx,Ay,By,Cy,d,e,f,num,offset;
-       short x1lo,x1hi,y1lo,y1hi;
+       double Ax,Bx,Cx,Ay,By,Cy,d,e,f,num;
+       double x1lo,x1hi,y1lo,y1hi;
 
        Ax = x2-x1;
        Bx = x3-x4;
 
        if(Ax<0) {                                              /* X bound box test*/
-               x1lo=(short)x2; x1hi=(short)x1;
+               x1lo=x2; x1hi=x1;
        } else {
-               x1hi=(short)x2; x1lo=(short)x1;
+               x1hi=x2; x1lo=x1;
        }
        if(Bx>0) {
-               if(x1hi < (short)x4 || (short)x3 < x1lo) return DONT_INTERSECT;
+               if(x1hi < x4 || x3 < x1lo) return false;
        } else {
-               if(x1hi < (short)x3 || (short)x4 < x1lo) return DONT_INTERSECT;
+               if(x1hi < x3 || x4 < x1lo) return false;
        }
 
        Ay = y2-y1;
        By = y3-y4;
 
        if(Ay<0) {                                              /* Y bound box test*/
-               y1lo=(short)y2; y1hi=(short)y1;
+               y1lo=y2; y1hi=y1;
        } else {
-               y1hi=(short)y2; y1lo=(short)y1;
+               y1hi=y2; y1lo=y1;
        }
        if(By>0) {
-               if(y1hi < (short)y4 || (short)y3 < y1lo) return DONT_INTERSECT;
+               if(y1hi < y4 || y3 < y1lo) return false;
        } else {
-               if(y1hi < (short)y3 || (short)y4 < y1lo) return DONT_INTERSECT;
+               if(y1hi < y3 || y4 < y1lo) return false;
        }
 
 
@@ -72,32 +68,30 @@ static int lines_intersect(long x1, long y1, long x2, long y2, long x3, long y3,
        d = By*Cx - Bx*Cy;                                      /* alpha numerator*/
        f = Ay*Bx - Ax*By;                                      /* both denominator*/
        if(f>0) {                                               /* alpha tests*/
-               if(d<0 || d>f) return DONT_INTERSECT;
+               if(d<0 || d>f) return false;
        } else {
-               if(d>0 || d<f) return DONT_INTERSECT;
+               if(d>0 || d<f) return false;
        }
 
        e = Ax*Cy - Ay*Cx;                                      /* beta numerator*/
        if(f>0) {                                               /* beta tests*/
-               if(e<0 || e>f) return DONT_INTERSECT;
+               if(e<0 || e>f) return false;
        } else {
-               if(e>0 || e<f) return DONT_INTERSECT;
+               if(e>0 || e<f) return false;
        }
 
 /*compute intersection coordinates*/
 
-       if(f==0) return PARALLEL;
+       if(f==0) return false;
        num = d*Ax;                                             /* numerator */
-       offset = SAME_SIGNS(num,f) ? f/2 : -f/2;                /* round direction*/
-       *x = x1 + (num+offset) / f;                             /* intersection x */
+       *x = x1 + num / f;                              /* intersection x */
 
        num = d*Ay;
-       offset = SAME_SIGNS(num,f) ? f/2 : -f/2;
-       *y = y1 + (num+offset) / f;                             /* intersection y */
+       *y = y1 + num / f;                              /* intersection y */
 
-       return DO_INTERSECT;
+       return true;
 }
-       
+
 // A set of very useful macros that you will find in most
 // code that I write whether I use them in a program or
 // not.
@@ -481,8 +475,8 @@ static void line(SDL_Surface *s,
 #if 1
 struct coord
 {
-       long x;
-       long y;
+       double x;
+       double y;
 };
 
 struct ball
@@ -494,12 +488,18 @@ struct ball
 struct line_segment
 {
        struct coord start, end;
+       struct list_node list;
+       bool ignore;
+       struct timeval expires;
 };
 
-static void thick_line(SDL_Surface *s, const struct line_segment *l)
+static void thick_line(SDL_Surface *s, const struct line_segment *l,
+                      unsigned color)
 {
        struct line_segment adj;
-       int x, y;
+       int x, y, minx = MAX_X, miny = MAX_Y, maxx = 0, maxy = 0;
+
+       SDL_LockSurface(s);
 
        /* Cheap hack */
        for (x = -1; x < 2; x++) {
@@ -509,10 +509,38 @@ static void thick_line(SDL_Surface *s, const struct line_segment *l)
                        adj.start.y = l->start.y + y;
                        adj.end.y = l->end.y + y;
 
+                       if (adj.start.x < minx)
+                               minx = adj.start.x;
+                       if (adj.start.x > maxx)
+                               maxx = adj.start.x;
+                       if (adj.start.y < miny)
+                               miny = adj.start.y;
+                       if (adj.start.y > maxy)
+                               maxy = adj.start.y;
+                       if (adj.end.x < minx)
+                               minx = adj.end.x;
+                       if (adj.end.x > maxx)
+                               maxx = adj.end.x;
+                       if (adj.end.y < miny)
+                               miny = adj.end.y;
+                       if (adj.end.y > maxy)
+                               maxy = adj.end.y;
+
                        line(s, adj.start.x, adj.start.y, adj.end.x, adj.end.y,
-                            0);
+                            color);
                }
        }
+
+       if (minx < 0)
+               minx = 0;
+       if (miny < 0)
+               miny = 0;
+       if (maxx >= MAX_X)
+               maxx = MAX_X-1;
+       if (maxy >= MAX_Y)
+               maxy = MAX_Y-1;
+       SDL_UpdateRect(s, minx, miny, maxx-minx, maxy-miny);
+       SDL_UnlockSurface(s);
 }
 
 static bool intersect(const struct coord *c1, const struct coord *c2,
@@ -521,12 +549,12 @@ static bool intersect(const struct coord *c1, const struct coord *c2,
        return lines_intersect(c1->x, c1->y, c2->x, c2->y,
                               seg->start.x, seg->start.y,
                               seg->end.x, seg->end.y,
-                              &intersect->x, &intersect->y) == DO_INTERSECT;
+                              &intersect->x, &intersect->y);
 }
 
 static double dist(const struct coord *c1, const struct coord *c2)
 {
-       long x = (c1->x - c2->x), y = (c1->y - c2->y);
+       float x = (c1->x - c2->x), y = (c1->y - c2->y);
        return sqrt(x * x + y * y);
 }
 
@@ -553,6 +581,7 @@ static void bounce(struct ball *ball, const struct line_segment *line,
 
        /* Move ball to intersection. */
        ball->pos = isect;
+       printf("ball is now at %f,%f\n", ball->pos.x, ball->pos.y);
 
        /* Outgoing angle = 2 * line angle - incident angle. */
        lang = atan2(line->end.x - line->start.x, line->end.y - line->start.y);
@@ -565,13 +594,14 @@ static void bounce(struct ball *ball, const struct line_segment *line,
 
        /* Set new direction for ball, at same speed */
        speed = sqrt(ball->move.x * ball->move.x + ball->move.y * ball->move.y);
-       ball->move.x = round(sin(oang) * speed);
-       ball->move.y = round(cos(oang) * speed);
+       speed *= 1.01;
+       ball->move.x = sin(oang) * speed;
+       ball->move.y = cos(oang) * speed;
 
        /* Set remainder. */
-       remainder->x = round(sin(oang) * len);
-       remainder->y = round(cos(oang) * len);
-       printf("len = %f, remainder = %li,%li\n", len, remainder->x, remainder->y);
+       remainder->x = sin(oang) * len;
+       remainder->y = cos(oang) * len;
+       printf("len = %f, remainder = %f,%f\n", len, remainder->x, remainder->y);
 }
 
 static struct line_segment border[] = {
@@ -617,35 +647,36 @@ static SDL_Surface *ball_surface(SDL_Surface *screen)
        return ball;
 }
 
+static void clear_ignore(struct list_head *lines)
+{
+       struct line_segment *i;
+       printf("Unignoring...\n");
+       list_for_each(lines, i, list)
+               i->ignore = false;
+}
+
 int main(int argc, char *argv[])
 {
        struct ball ball;
-       struct line_segment lines[ARRAY_SIZE(border) + 1];
-       static int isect_count = 0;
        SDL_Surface *screen, *ballsur, *savesur = NULL;
        Uint8  video_bpp;
        Uint32 videoflags;
-       int i;
-       SDL_Event event;
-       struct line_segment *last_l = NULL;
+       unsigned int i;
        SDL_Rect rect;
+       bool creating_lines = false;
+       struct timeval line_life = { 5, 0 };
+       LIST_HEAD(lines);
 
-       if (argc != 9)
-               errx(1, "Usage: %s ballx bally ballangle ballspeed linestartx linestarty lineendx linendy\n"
+       if (argc != 5)
+               errx(1, "Usage: %s ballx bally ballangle ballspeed\n"
                     "   Where ballangle is 0 for north, 90 for east, etc\n",
                        argv[0]);
 
        ball.pos.x = atol(argv[1]);
        ball.pos.y = atol(argv[2]);
-       ball.move.x = roundf(sin(deg_to_rad(atof(argv[3]))) * atol(argv[4]));
-       ball.move.y = roundf(cos(deg_to_rad(atof(argv[3]))) * atol(argv[4]));
-       printf("ball move = %li,%li\n", ball.move.x, ball.move.y);
-
-       memcpy(lines, border, sizeof(border));
-       lines[ARRAY_SIZE(border)].start.x = atof(argv[5]);
-       lines[ARRAY_SIZE(border)].start.y = atof(argv[6]);
-       lines[ARRAY_SIZE(border)].end.x = atof(argv[7]);
-       lines[ARRAY_SIZE(border)].end.y = atof(argv[8]);
+       ball.move.x = sin(deg_to_rad(atof(argv[3]))) * atol(argv[4]);
+       ball.move.y = cos(deg_to_rad(atof(argv[3]))) * atol(argv[4]);
+       printf("ball move = %f,%f\n", ball.move.x, ball.move.y);
 
        /* Initialize SDL */
        if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
@@ -670,28 +701,38 @@ int main(int argc, char *argv[])
        /* White. */
        memset(screen->pixels, 0xFF, screen->pitch * screen->h);
 
-       /* Draw lines. */
-       for (i = 0; i < ARRAY_SIZE(lines); i++)
-               thick_line(screen, &lines[i]);
-
        SDL_UnlockSurface(screen);
        SDL_UpdateRect(screen, 0, 0, 0, 0);
 
+       /* Draw borders, put them in list. */
+       for (i = 0; i < ARRAY_SIZE(border); i++) {
+               border[i].ignore = false;
+               border[i].expires.tv_sec = LONG_MAX;
+               border[i].expires.tv_usec = 0;
+               list_add(&lines, &border[i].list);
+               thick_line(screen, &border[i], 0);
+       }
+
        ballsur = ball_surface(screen);
 
-       do {
+       for (;;) {
                struct coord new, isect, move = ball.move;
-               struct line_segment *best_l;
-               unsigned i;
+               struct line_segment *best_l, *l, *next;
                double best_d;
-#if 0
-               SDL_Rect rect;
-
-               rect.x = ball.pos.x - ballsur->w/2;
-               rect.y = ball.pos.y - ballsur->h/2;
-               rect.w = ballsur->w;
-               rect.h = ballsur->h;
-#endif
+               SDL_Event event;
+               struct timeval now;
+
+               gettimeofday(&now, NULL);
+
+               /* Expire lines */
+               list_for_each_safe(&lines, l, next, list) {
+                       if (timercmp(&l->expires, &now, <)) {
+                               list_del(&l->list);
+                               /* FIXME: Undraw properly. */
+                               thick_line(screen, l, 0xFFFFFFFF);
+                               free(l);
+                       }
+               }
 
        again:
                best_d = MAX_X + MAX_Y;
@@ -699,32 +740,38 @@ int main(int argc, char *argv[])
                new.x = ball.pos.x + move.x;
                new.y = ball.pos.y + move.y;
 
-               printf("Ball at %li,%li (%li,%li)\n", ball.pos.x, ball.pos.y,
-                      move.x, move.y);
-
-               for (i = 0; i < ARRAY_SIZE(lines); i++) {
-                       if (last_l == &lines[i])
-                               continue;
-                       if (intersect(&ball.pos, &new, &lines[i], &isect)) {
+               list_for_each(&lines, l, list) {
+                       if (!l->ignore && intersect(&ball.pos, &new, l, &isect)) {
                                double d;
                                d = dist(&ball.pos, &isect);
                                if (d < best_d) {
-                                       best_l = &lines[i];
+                                       best_l = l;
                                        best_d = d;
                                }
                        }
                }
+
                if (best_l) {
-                       printf("Ball bouncing off (%lu,%lu %lu,%lu)!\n",
+                       printf("Ball bouncing off (%f,%f %f,%f)!\n",
                               best_l->start.x, best_l->start.y,
                               best_l->end.x, best_l->end.y);
                        bounce(&ball, best_l, &move);
+
+                       /* If we moved, stop ignoring lines. */
+                       if (move.x > 0.001 || move.y > 0.001)
+                               clear_ignore(&lines);
+
                        /* Don't hit the same line twice. */
-                       last_l = best_l;
-                       isect_count++;
+                       printf("Ignoring that line\n");
+                       best_l->ignore = true;
                        goto again;
                }
 
+               if (move.x > 0.001 || move.y > 0.001) {
+                       clear_ignore(&lines);
+                       printf("Moving by %f,%f to %f,%f\n", move.x, move.y, new.x, new.y);
+               }
+
                /* Restore what was there before ball. */
                if (savesur) {
                        SDL_BlitSurface(savesur, NULL, screen, &rect);
@@ -747,9 +794,30 @@ int main(int argc, char *argv[])
                rect.x = new.x - ballsur->w/2;
                rect.y = new.y - ballsur->h/2;
                ball.pos = new;
-               SDL_Delay(5);
-       } while (!SDL_PollEvent(&event) || event.type != SDL_QUIT);
-       SDL_Quit();
-       return 0;
+               SDL_Delay(25);
+
+               while (SDL_PollEvent(&event)) {
+                       if (event.type == SDL_QUIT) {
+                               SDL_Quit();
+                               return 0;
+                       }
+                       if (event.type == SDL_MOUSEBUTTONDOWN)
+                               creating_lines = true;
+                       else if (event.type == SDL_MOUSEBUTTONUP)
+                               creating_lines = false;
+
+                       if (creating_lines && event.type == SDL_MOUSEMOTION) {
+                               struct line_segment *n = malloc(sizeof(*n));
+                               n->start.x = event.motion.x - event.motion.xrel;
+                               n->start.y = event.motion.y - event.motion.yrel;
+                               n->end.x = event.motion.x;
+                               n->end.y = event.motion.y;
+                               n->ignore = false;
+                               timeradd(&now, &line_life, &n->expires);
+                               list_add_tail(&lines, &n->list);
+                               thick_line(screen, n, 0);
+                       }
+               }
+       }
 }
 #endif