#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;
}
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.
#if 1
struct coord
{
- long x;
- long y;
+ double x;
+ double y;
};
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++) {
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,
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);
}
/* 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);
/* 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[] = {
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 ) {
/* 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;
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);
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