#include "list/list.h"
#include <SDL/SDL.h>
#include <cwiid.h>
+#include <assert.h>
+#include <unistd.h>
#define MAX_X CWIID_IR_X_MAX
#define MAX_Y CWIID_IR_Y_MAX
+struct coord
+{
+ double x;
+ double y;
+};
+
/* Obtained from http://tog.acm.org/GraphicsGems/gemsiii/insectc.c */
/* Faster Line Segment Intersection */
/* Franklin Antonio */
-
-/* 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 bool lines_intersect(double x1, double y1,
- double x2, double y2,
- double x3, double y3,
- double x4, double y4,
- double *x, double *y)
+static bool lines_intersect(const struct coord *start1,
+ const struct coord *start2,
+ const struct coord *start3,
+ const struct coord *start4,
+ struct coord *intersect)
{
-
double Ax,Bx,Cx,Ay,By,Cy,d,e,f,num;
- double x1lo,x1hi,y1lo,y1hi;
+ struct coord lo, hi;
- Ax = x2-x1;
- Bx = x3-x4;
+ Ax = start2->x - start1->x;
+ Bx = start3->x - start4->x;
- if(Ax<0) { /* X bound box test*/
- x1lo=x2; x1hi=x1;
+ if(Ax<0) { /* X bound box test*/
+ lo.x = start2->x; hi.x = start1->x;
} else {
- x1hi=x2; x1lo=x1;
+ hi.x = start2->x; lo.x = start1->x;
}
if(Bx>0) {
- if(x1hi < x4 || x3 < x1lo) return false;
+ if(hi.x < start4->x || start3->x < lo.x) return false;
} else {
- if(x1hi < x3 || x4 < x1lo) return false;
+ if(hi.x < start3->x || start4->x < lo.x) return false;
}
- Ay = y2-y1;
- By = y3-y4;
+ Ay = start2->y - start1->y;
+ By = start3->y - start4->y;
- if(Ay<0) { /* Y bound box test*/
- y1lo=y2; y1hi=y1;
+ if(Ay<0) { /* Y bound box test*/
+ lo.y = start2->y; hi.y = start1->y;
} else {
- y1hi=y2; y1lo=y1;
+ hi.y = start2->y; lo.y = start1->y;
}
if(By>0) {
- if(y1hi < y4 || y3 < y1lo) return false;
+ if(hi.y < start4->y || start3->y < lo.y) return false;
} else {
- if(y1hi < y3 || y4 < y1lo) return false;
+ if(hi.y < start3->y || start4->y < lo.y) return false;
}
- Cx = x1-x3;
- Cy = y1-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 false;
+ Cx = start1->x - start3->x;
+ Cy = start1->y - start3->y;
+ 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 false;
} else {
- if(d>0 || d<f) return false;
+ 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 false;
+ e = Ax*Cy - Ay*Cx; /* beta numerator*/
+ if (f > 0) { /* beta tests*/
+ if (e < 0 || e > f) return false;
} else {
- if(e>0 || e<f) return false;
+ if (e > 0 || e < f) return false;
}
/*compute intersection coordinates*/
+ /* Don't worry about tiny values of f (< 1/2 pixel across screen) */
+ if (fabs(f) < 1.0 / (2.0 * MAX_X * MAX_Y))
+ return false;
- if(f==0) return false;
- num = d*Ax; /* numerator */
- *x = x1 + num / f; /* intersection x */
+ num = d*Ax; /* numerator */
+ intersect->x = start1->x + num / f; /* intersection x */
num = d*Ay;
- *y = y1 + num / f; /* intersection y */
+ intersect->y = start1->y + num / f; /* intersection y */
return true;
}
}
}
-#if 1
-struct coord
-{
- double x;
- double y;
-};
-
struct ball
{
struct coord pos;
static bool intersect(const struct coord *c1, const struct coord *c2,
const struct line_segment *seg, struct coord *intersect)
{
- 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);
+ return lines_intersect(c1, c2, &seg->start, &seg->end, intersect);
}
static double dist(const struct coord *c1, const struct coord *c2)
{
- float x = (c1->x - c2->x), y = (c1->y - c2->y);
+ double x = (c1->x - c2->x), y = (c1->y - c2->y);
return sqrt(x * x + y * y);
}
}
static void bounce(struct ball *ball, const struct line_segment *line,
- struct coord *remainder)
+ struct coord *move, double increase_speed)
{
double len, speed, lang, iang, oang;
struct coord isect, new;
- new.x = ball->pos.x + ball->move.x;
- new.y = ball->pos.y + ball->move.y;
+ new.x = ball->pos.x + move->x;
+ new.y = ball->pos.y + move->y;
/* How far were we supposed to move ball? */
- len = sqrt(remainder->x * remainder->x + remainder->y * remainder->y);
+ len = sqrt(move->x * move->x + move->y * move->y);
/* How far is it to intersection? */
- intersect(&ball->pos, &new, line, &isect);
+ if (!intersect(&ball->pos, &new, line, &isect))
+ errx(1, "No intersection any more?\n");;
len -= dist(&ball->pos, &isect);
/* Move ball to intersection. */
rad_to_deg(iang),
rad_to_deg(oang));
- /* Set new direction for ball, at same speed */
+ /* Set new direction for ball, at slightly increased speed */
speed = sqrt(ball->move.x * ball->move.x + ball->move.y * ball->move.y);
- speed *= 1.01;
+ speed += increase_speed;
ball->move.x = sin(oang) * speed;
ball->move.y = cos(oang) * speed;
- /* Set remainder. */
- remainder->x = sin(oang) * len;
- remainder->y = cos(oang) * len;
- printf("len = %f, remainder = %f,%f\n", len, remainder->x, remainder->y);
+ /* Set move. */
+ move->x = sin(oang) * len;
+ move->y = cos(oang) * len;
+ printf("len = %f, move = %f,%f\n", len, move->x, move->y);
}
static struct line_segment border[] = {
i->ignore = false;
}
+static uint16_t *find_valid_point(struct cwiid_state *state)
+{
+ unsigned int i;
+
+ for (i = 0; i < CWIID_IR_SRC_COUNT; i++) {
+ if (state->ir_src[i].valid)
+ return state->ir_src[i].pos;
+ }
+ return NULL;
+}
+
+static void calibrate(SDL_Surface *screen,
+ cwiid_wiimote_t *wiimote,
+ struct coord *calib, unsigned x, unsigned y)
+{
+ uint16_t *pos;
+ struct cwiid_state state;
+ unsigned int last_x = MAX_X, last_y = MAX_Y, count = 0;
+
+ /* Must see it for a full half second. */
+ while (count < 20) {
+ SDL_Event event;
+
+ if (cwiid_get_state(wiimote, &state))
+ errx(1, "No wii state");
+
+ pos = find_valid_point(&state);
+ if (!pos || pos[0] != last_x || pos[1] != last_y)
+ count = 0;
+
+ if (pos) {
+ last_x = pos[0];
+ last_y = pos[1];
+ count++;
+ }
+
+ SDL_Delay(25);
+ if (SDL_PollEvent(&event)
+ && (event.type == SDL_QUIT
+ || (event.type == SDL_KEYDOWN
+ && event.key.keysym.sym == SDLK_ESCAPE))) {
+ SDL_Quit();
+ exit(0);
+ }
+ }
+
+ calib->x = last_x;
+ calib->y = last_y;
+ printf("Calibration point: %u,%u\n", last_x, last_y);
+}
+
+static bool map_coord(unsigned int x, unsigned int y,
+ const struct coord calib[],
+ struct coord *res)
+{
+ struct coord line_start, line_end;
+ struct coord pos = { x, y }, intersect;
+ double d1, d2;
+
+ /* Calibration layout:
+ *
+ * 0 1
+ *
+ * 3 2
+ *
+ * We figure out the distance to each side, and then map using:
+ *
+ * x = d1 / (d1 + d2) * (rawend - rawstart) + rawstart
+ */
+ line_start.x = 0;
+ line_end.x = MAX_X-1;
+ line_start.y = line_end.y = y;
+
+ if (!lines_intersect(&calib[0], &calib[3], &line_start, &line_end,
+ &intersect))
+ return false;
+ d1 = dist(&pos, &intersect);
+ if (!lines_intersect(&calib[1], &calib[2], &line_start, &line_end,
+ &intersect))
+ return false;
+ d2 = dist(&pos, &intersect);
+ res->x = d1 / (d1 + d2) * MAX_X;
+
+ line_start.y = 0;
+ line_end.y = MAX_Y-1;
+ line_start.x = line_end.x = x;
+
+ if (!lines_intersect(&calib[0], &calib[1], &line_start, &line_end,
+ &intersect))
+ return false;
+ d1 = dist(&pos, &intersect);
+ if (!lines_intersect(&calib[3], &calib[2], &line_start, &line_end,
+ &intersect))
+ return false;
+ d2 = dist(&pos, &intersect);
+ res->y = d1 / (d1 + d2) * MAX_Y;
+
+ return true;
+}
+
+static struct line_segment *new_line(unsigned int startx, unsigned int starty,
+ unsigned int endx, unsigned int endy,
+ const struct coord calib[])
+{
+ struct line_segment *l = malloc(sizeof(*l));
+
+ l->ignore = false;
+ if (!map_coord(startx, starty, calib, &l->start)
+ || !map_coord(endx, endy, calib, &l->end)) {
+ free(l);
+ return NULL;
+ }
+ return l;
+}
+
+
int main(int argc, char *argv[])
{
struct ball ball;
SDL_Surface *screen, *ballsur, *savesur = NULL;
Uint8 video_bpp;
Uint32 videoflags;
- unsigned int i;
+ unsigned int i, time_since_last_ir = INT_MAX;
SDL_Rect rect;
- bool creating_lines = false;
- struct timeval line_life = { 5, 0 };
+ struct timeval line_life = { 3, 0 };
+ cwiid_wiimote_t *wiimote;
+ struct cwiid_state state;
+ struct coord calib[4];
+ bdaddr_t addr = *BDADDR_ANY;
+ bool mouse = false, needs_calibration = true, drawing = false;
LIST_HEAD(lines);
+ struct cwiid_ir_src *ir, last_ir = { .valid = 0 };
+ struct line_segment *n;
+
+ videoflags = SDL_SWSURFACE;
+
+ if (argv[1] && streq(argv[1], "--fullscreen")) {
+ videoflags |= SDL_FULLSCREEN;
+ argc--;
+ argv++;
+ }
+
+ if (argv[1] && strstarts(argv[1], "--calib=")) {
+ needs_calibration = false;
+ if (sscanf(argv[1], "--calib=%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
+ &calib[0].x, &calib[0].y,
+ &calib[1].x, &calib[1].y,
+ &calib[2].x, &calib[2].y,
+ &calib[3].x, &calib[3].y) != 8)
+ errx(1, "Not enough calibration points");
+ argc--;
+ argv++;
+ }
+
+ if (argv[1] && streq(argv[1], "--mouse")) {
+ mouse = true;
+ needs_calibration = false;
+ calib[0].x = calib[0].y = 0;
+ calib[1].x = MAX_X - 1;
+ calib[1].y = 0;
+ calib[2].x = MAX_X - 1;
+ calib[2].y = MAX_Y - 1;
+ calib[3].x = 0;
+ calib[3].y = MAX_Y - 1;
+ argc--;
+ argv++;
+ }
if (argc != 5)
- errx(1, "Usage: %s ballx bally ballangle ballspeed\n"
+ errx(1, "Usage: %s [--fullscreen] [--calib=x,y,x,y,x,y,x,y] [--mouse] ballx bally ballangle ballspeed\n"
" Where ballangle is 0 for north, 90 for east, etc\n",
argv[0]);
printf("ball move = %f,%f\n", ball.move.x, ball.move.y);
/* Initialize SDL */
- if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
return(1);
}
video_bpp = 0;
- videoflags = SDL_SWSURFACE;
- /* Set 640x480 video mode */
+ if (!mouse) {
+ printf("Put Wiimote in discoverable mode (press 1+2)\n");
+ wiimote = cwiid_open(&addr, 0);
+ if (!wiimote)
+ errx(1, "Can't find the Wiimote");
+
+ if (cwiid_set_rpt_mode(wiimote, CWIID_RPT_IR))
+ errx(1, "Can't set IR repeat mode");
+ }
+
if ( (screen=SDL_SetVideoMode(MAX_X,MAX_Y,video_bpp,videoflags)) == NULL ) {
errx(1, "Couldn't set %dx%dx%d video mode: %s",
MAX_X, MAX_Y,video_bpp, SDL_GetError());
SDL_UnlockSurface(screen);
SDL_UpdateRect(screen, 0, 0, 0, 0);
+ if (needs_calibration) {
+ /* Calibration */
+ calibrate(screen, wiimote, &calib[0], 0, 0);
+ calibrate(screen, wiimote, &calib[1], MAX_X - 1, 0);
+ calibrate(screen, wiimote, &calib[2], MAX_X - 1, MAX_Y - 1);
+ calibrate(screen, wiimote, &calib[3], 0, MAX_Y - 1);
+ }
+
/* Draw borders, put them in list. */
for (i = 0; i < ARRAY_SIZE(border); i++) {
border[i].ignore = false;
double best_d;
SDL_Event event;
struct timeval now;
+ bool ignored = false;
gettimeofday(&now, NULL);
/* Expire lines */
list_for_each_safe(&lines, l, next, list) {
if (timercmp(&l->expires, &now, <)) {
+ printf("Deleting line %p\n", l);
list_del(&l->list);
/* FIXME: Undraw properly. */
thick_line(screen, l, 0xFFFFFFFF);
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);
+ /* Only increase speed on *first* bounce, to avoid
+ * mega speed increases. */
+ bounce(&ball, best_l, &move, ignored ? 0.0 : 0.1);
/* If we moved, stop ignoring lines. */
- if (move.x > 0.001 || move.y > 0.001)
+ if (ignored && (move.x > 0.001 || move.y > 0.001))
clear_ignore(&lines);
/* Don't hit the same line twice. */
printf("Ignoring that line\n");
- best_l->ignore = true;
+ best_l->ignore = ignored = true;
goto again;
}
- if (move.x > 0.001 || move.y > 0.001) {
+ if (ignored && (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);
+ printf("Moving by %f,%f to %f,%f\n",
+ move.x, move.y, new.x, new.y);
}
/* Restore what was there before ball. */
SDL_Delay(25);
while (SDL_PollEvent(&event)) {
- if (event.type == SDL_QUIT) {
+ if (event.type == SDL_QUIT
+ || (event.type == SDL_KEYDOWN
+ && event.key.keysym.sym == SDLK_ESCAPE)) {
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);
+ if (mouse) {
+ switch (event.type) {
+ case SDL_MOUSEBUTTONDOWN:
+ drawing = true;
+ break;
+ case SDL_MOUSEBUTTONUP:
+ drawing = false;
+ break;
+ case SDL_MOUSEMOTION:
+ if (!drawing)
+ break;
+
+ n = new_line(event.motion.x,
+ event.motion.y,
+ event.motion.x
+ + event.motion.xrel,
+ event.motion.y
+ + event.motion.yrel,
+ calib);
+
+ if (n) {
+ timeradd(&now, &line_life,
+ &n->expires);
+ list_add_tail(&lines, &n->list);
+ thick_line(screen, n, 0);
+ }
+ }
}
}
+
+ if (mouse)
+ continue;
+
+ if (cwiid_get_state(wiimote, &state))
+ errx(1, "No wii state");
+
+ /* Find biggest state. */
+ ir = &state.ir_src[0];
+ for (i = 0; i < CWIID_IR_SRC_COUNT; i++) {
+ if (!state.ir_src[i].valid)
+ continue;
+ if (!ir->valid || state.ir_src[i].size > ir->size)
+ ir = &state.ir_src[0];
+ }
+
+ if (ir->valid) {
+ /* Give it some slack for missing one or two... */
+ if (time_since_last_ir <= 5) {
+ n = new_line(last_ir.pos[0],
+ last_ir.pos[1],
+ ir->pos[0],
+ ir->pos[1],
+ calib);
+ if (n) {
+ timeradd(&now, &line_life, &n->expires);
+ list_add_tail(&lines, &n->list);
+ thick_line(screen, n, 0);
+ }
+ }
+ time_since_last_ir = 0;
+ last_ir = *ir;
+ }
+ time_since_last_ir++;
}
}
-#endif