From 2915370b7d41bd5ab5a3eae7a2166e2e022633b4 Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Sat, 7 Jan 2017 15:29:12 -0500 Subject: puzzles: add cursor interface to untangle - makes it playable - submitted to upstream Change-Id: Ib672ee374d500a994bc8e78eb01fea529ec5132f --- apps/plugins/puzzles/SOURCES.games | 2 +- apps/plugins/puzzles/untangle.c | 155 +++++++++++++++++++++++++++++++++++-- 2 files changed, 151 insertions(+), 6 deletions(-) diff --git a/apps/plugins/puzzles/SOURCES.games b/apps/plugins/puzzles/SOURCES.games index 26f74e4005..b16cf0f70d 100644 --- a/apps/plugins/puzzles/SOURCES.games +++ b/apps/plugins/puzzles/SOURCES.games @@ -38,7 +38,7 @@ twiddle.c /*undead.c*/ /*unequal.c*/ unruly.c -/*untangle.c*/ +untangle.c /* disabled for now */ /*unfinished/group.c*/ diff --git a/apps/plugins/puzzles/untangle.c b/apps/plugins/puzzles/untangle.c index 67da03d8c0..839013809b 100644 --- a/apps/plugins/puzzles/untangle.c +++ b/apps/plugins/puzzles/untangle.c @@ -39,6 +39,7 @@ #define CIRCLE_RADIUS 6 #define DRAG_THRESHOLD (CIRCLE_RADIUS * 2) #define PREFERRED_TILESIZE 64 +#define CURSOR_GRANULARITY 5 #define FLASH_TIME 0.30F #define ANIM_TIME 0.13F @@ -54,6 +55,7 @@ enum { COL_OUTLINE, COL_POINT, COL_DRAGPOINT, + COL_CURSORPOINT, COL_NEIGHBOUR, COL_FLASH1, COL_FLASH2, @@ -1038,6 +1040,11 @@ static char *game_text_format(const game_state *state) struct game_ui { int dragpoint; /* point being dragged; -1 if none */ + + int cursorpoint; /* point being highlighted, but + * not dragged by the cursor, + * again -1 if none */ + point newpoint; /* where it's been dragged to so far */ int just_dragged; /* reset in game_changed_state */ int just_moved; /* _set_ in game_changed_state */ @@ -1048,6 +1055,7 @@ static game_ui *new_ui(const game_state *state) { game_ui *ui = snew(game_ui); ui->dragpoint = -1; + ui->cursorpoint = -1; ui->just_moved = ui->just_dragged = FALSE; return ui; } @@ -1076,7 +1084,7 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate, struct game_drawstate { long tilesize; - int bg, dragpoint; + int bg, dragpoint, cursorpoint; long *x, *y; }; @@ -1150,6 +1158,135 @@ static char *interpret_move(const game_state *state, game_ui *ui, ui->just_dragged = TRUE; return dupstr(buf); } + else if(IS_CURSOR_MOVE(button)) + { + if(ui->dragpoint < 0) + { + if(ui->cursorpoint < 0) + { + ui->cursorpoint = 0; + return ""; + } + + /* We're selecting a point here. */ + /* Search all the points and find the closest one (2-D) in + * the given direction. */ + int i, best; + long bestd; + + /* + * Begin drag. We drag the vertex _nearest_ to the pointer, + * just in case one is nearly on top of another and we want + * to drag the latter. However, we drag nothing at all if + * the nearest vertex is outside DRAG_THRESHOLD. + */ + best = -1; + bestd = 0; + + for (i = 0; i < n; i++) { + if(i == ui->cursorpoint) + continue; + + long px = state->pts[i].x * ds->tilesize / state->pts[i].d; + long py = state->pts[i].y * ds->tilesize / state->pts[i].d; + long dx = px - state->pts[ui->cursorpoint].x * ds->tilesize / state->pts[ui->cursorpoint].d; + long dy = py - state->pts[ui->cursorpoint].y * ds->tilesize / state->pts[ui->cursorpoint].d; + long d = dx*dx + dy*dy; + + /* Figure out if this point falls into a 90 degree + * range extending from the current point */ + + float angle = atan2(-dy, dx); /* adjust for raster coordinates */ + + /* offset to [0..2*PI] */ + if(angle < 0) + angle += 2*PI; + + int right_direction = FALSE; + + if((button == CURSOR_UP && (1*PI/4 <= angle && angle <= 3*PI/4)) || + (button == CURSOR_LEFT && (3*PI/4 <= angle && angle <= 5*PI/4)) || + (button == CURSOR_DOWN && (5*PI/4 <= angle && angle <= 7*PI/4)) || + (button == CURSOR_RIGHT && (angle >= 7*PI/4 || angle <= 1*PI/4))) + right_direction = TRUE; + + if ((best == -1 || bestd > d) && right_direction) { + best = i; + bestd = d; + } + } + + if(best >= 0) + { + ui->cursorpoint = best; + return ""; + } + } + else if(ui->dragpoint >= 0) + { + /* dragging */ + switch(button) + { + case CURSOR_UP: + ui->newpoint.y -= ds->tilesize / CURSOR_GRANULARITY; + return ""; + case CURSOR_DOWN: + ui->newpoint.y += ds->tilesize / CURSOR_GRANULARITY; + return ""; + case CURSOR_LEFT: + ui->newpoint.x -= ds->tilesize / CURSOR_GRANULARITY; + return ""; + case CURSOR_RIGHT: + ui->newpoint.x += ds->tilesize / CURSOR_GRANULARITY; + return ""; + default: + break; + } + } + } + else if(IS_CURSOR_SELECT(button)) + { + if(ui->dragpoint < 0 && ui->cursorpoint >= 0) + { + /* begin drag */ + ui->dragpoint = ui->cursorpoint; + ui->cursorpoint = -1; + ui->newpoint.x = state->pts[ui->dragpoint].x * ds->tilesize / state->pts[ui->dragpoint].d; + ui->newpoint.y = state->pts[ui->dragpoint].y * ds->tilesize / state->pts[ui->dragpoint].d; + ui->newpoint.d = ds->tilesize; + return ""; + } + else if(ui->dragpoint >= 0) + { + /* end drag */ + int p = ui->dragpoint; + char buf[80]; + + ui->cursorpoint = ui->dragpoint; + ui->dragpoint = -1; /* terminate drag, no matter what */ + + /* + * First, see if we're within range. The user can cancel a + * drag by dragging the point right off the window. + */ + if (ui->newpoint.x < 0 || + ui->newpoint.x >= (long)state->w*ui->newpoint.d || + ui->newpoint.y < 0 || + ui->newpoint.y >= (long)state->h*ui->newpoint.d) + return ""; + + /* + * We aren't cancelling the drag. Construct a move string + * indicating where this point is going to. + */ + sprintf(buf, "P%d:%ld,%ld/%ld", p, + ui->newpoint.x, ui->newpoint.y, ui->newpoint.d); + ui->just_dragged = TRUE; + return dupstr(buf); + } + else if(ui->cursorpoint < 0) + ui->cursorpoint = 0; + } return NULL; } @@ -1241,6 +1378,10 @@ static float *game_colours(frontend *fe, int *ncolours) ret[COL_DRAGPOINT * 3 + 1] = 1.0F; ret[COL_DRAGPOINT * 3 + 2] = 1.0F; + ret[COL_CURSORPOINT * 3 + 0] = 0.5F; + ret[COL_CURSORPOINT * 3 + 1] = 0.5F; + ret[COL_CURSORPOINT * 3 + 2] = 0.5F; + ret[COL_NEIGHBOUR * 3 + 0] = 1.0F; ret[COL_NEIGHBOUR * 3 + 1] = 0.0F; ret[COL_NEIGHBOUR * 3 + 2] = 0.0F; @@ -1269,6 +1410,7 @@ static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state) ds->x[i] = ds->y[i] = -1; ds->bg = -1; ds->dragpoint = -1; + ds->cursorpoint = -1; return ds; } @@ -1345,7 +1487,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, ds->y[i] = y; } - if (ds->bg == bg && ds->dragpoint == ui->dragpoint && !points_moved) + if (ds->bg == bg && ds->dragpoint == ui->dragpoint && ds->cursorpoint == ui->cursorpoint && !points_moved) return; /* nothing to do */ ds->dragpoint = ui->dragpoint; @@ -1373,15 +1515,18 @@ static void game_redraw(drawing *dr, game_drawstate *ds, * When dragging, we should not only vary the colours, but * leave the point being dragged until last. */ - for (j = 0; j < 3; j++) { + for (j = 0; j < 4; j++) { int thisc = (j == 0 ? COL_POINT : - j == 1 ? COL_NEIGHBOUR : COL_DRAGPOINT); + (j == 1 ? COL_NEIGHBOUR : + j == 2 ? COL_CURSORPOINT : COL_DRAGPOINT)); for (i = 0; i < state->params.n; i++) { int c; if (ui->dragpoint == i) { c = COL_DRAGPOINT; - } else if (ui->dragpoint >= 0 && + } else if(ui->cursorpoint == i) { + c = COL_CURSORPOINT; + } else if (ui->dragpoint >= 0 && isedge(state->graph->edges, ui->dragpoint, i)) { c = COL_NEIGHBOUR; } else { -- cgit v1.2.3