1
0
Fork 0
forked from len0rd/rockbox

puzzles: antialiased line drawing and optional "shortcuts" for undo/redo

- line drawing should eventually be moved to xlcd, but for now it's very nonportable code
- fixes a minor issue with the configuration screens

Change-Id: I897f01b7210cbbec32665c2bc67920c965ea0bec
This commit is contained in:
Franklin Wei 2017-01-13 19:11:33 -05:00
parent 79e8cd4cfe
commit 8e4429853d
3 changed files with 319 additions and 111 deletions

View file

@ -43,8 +43,8 @@ ROCKS += $(PUZZLES_ROCKS)
endif endif
# Hack to suppress all warnings: # Hack to suppress all warnings:
PUZZLESFLAGS = $(filter-out -O%,$(PLUGINFLAGS)) -Os \ PUZZLESFLAGS = $(filter-out -O%,$(PLUGINFLAGS)) -O3 \
-Wno-unused-parameter -Wno-sign-compare -Wno-strict-aliasing -w \ -Wno-unused-parameter -Wno-sign-compare -Wno-strict-aliasing -w \
-DFOR_REAL -I$(PUZZLES_SRCDIR) -DFOR_REAL -I$(PUZZLES_SRCDIR)
ifdef PUZZLES_COMBINED ifdef PUZZLES_COMBINED
PUZZLESFLAGS += -DCOMBINED PUZZLESFLAGS += -DCOMBINED

View file

@ -30,6 +30,7 @@
#include "lib/playback_control.h" #include "lib/playback_control.h"
#endif #endif
#include "lib/xlcd.h" #include "lib/xlcd.h"
#include "fixedpoint.h"
/* how many ticks between timer callbacks */ /* how many ticks between timer callbacks */
#define TIMER_INTERVAL (HZ / 50) #define TIMER_INTERVAL (HZ / 50)
@ -75,7 +76,7 @@ extern bool audiobuf_available;
static struct settings_t { static struct settings_t {
int slowmo_factor; int slowmo_factor;
bool bulk, timerflash, clipoff; bool bulk, timerflash, clipoff, shortcuts, no_aa;
} settings; } settings;
/* clipping is implemented through viewports and offsetting /* clipping is implemented through viewports and offsetting
@ -179,14 +180,129 @@ static void rb_draw_rect(void *handle, int x, int y, int w, int h, int color)
rb->lcd_fillrect(x, y, w, h); rb->lcd_fillrect(x, y, w, h);
} }
#define SWAP(a, b, t) do { t = a; a = b; b = t; } while(0);
#define fp_fpart(f, bits) ((f) & ((1 << (bits)) - 1))
#define fp_rfpart(f, bits) ((1 << (bits)) - fp_fpart(f, bits))
#define FRACBITS 16
/* most of our time drawing lines is spent in this function! */
static inline void plot(unsigned x, unsigned y, unsigned long a,
unsigned long r1, unsigned long g1, unsigned long b1,
unsigned cl, unsigned cr, unsigned cu, unsigned cd)
{
/* This is really quite possibly the least efficient way of doing
this. A better way would be in draw_antialiased_line(), but the
problem with that is that the algorithms I investigated at
least were incorrect at least part of the time and didn't make
drawing much faster overall. */
if(!(cl <= x && x < cr && cu <= y && y < cd))
return;
fb_data *ptr = rb->lcd_framebuffer + y * LCD_WIDTH + x;
fb_data orig = *ptr;
unsigned long r2, g2, b2;
r2 = RGB_UNPACK_RED(orig);
g2 = RGB_UNPACK_GREEN(orig);
b2 = RGB_UNPACK_BLUE(orig);
unsigned long r, g, b;
r = ((r1 * a) + (r2 * (256 - a))) >> 8;
g = ((g1 * a) + (g2 * (256 - a))) >> 8;
b = ((b1 * a) + (b2 * (256 - a))) >> 8;
*ptr = LCD_RGBPACK(r, g, b);
}
#undef ABS
#define ABS(a) ((a)<0?-(a):(a))
/* speed benchmark: 34392 lines/sec vs 112687 non-antialiased
* lines/sec at full optimization on ipod6g */
/* expects UN-OFFSET coordinates, directly access framebuffer */
static void draw_antialiased_line(int x0, int y0, int x1, int y1)
{
/* fixed-point Wu's algorithm, modified for integer-only endpoints */
/* passed to plot() to avoid re-calculation */
unsigned short l = 0, r = LCD_WIDTH, u = 0, d = LCD_HEIGHT;
if(clipped)
{
l = clip_rect.x;
r = clip_rect.x + clip_rect.width;
u = clip_rect.y;
d = clip_rect.y + clip_rect.height;
}
bool steep = ABS(y1 - y0) > ABS(x1 - x0);
int tmp;
if(steep)
{
SWAP(x0, y0, tmp);
SWAP(x1, y1, tmp);
}
if(x0 > x1)
{
SWAP(x0, x1, tmp);
SWAP(y0, y1, tmp);
}
int dx, dy;
dx = x1 - x0;
dy = y1 - y0;
if(!(dx << FRACBITS))
return; /* bail out */
long gradient = fp_div(dy << FRACBITS, dx << FRACBITS, FRACBITS);
long intery = (y0 << FRACBITS);
unsigned color = rb->lcd_get_foreground();
unsigned long r1, g1, b1;
r1 = RGB_UNPACK_RED(color);
g1 = RGB_UNPACK_GREEN(color);
b1 = RGB_UNPACK_BLUE(color);
/* main loop */
if(steep)
{
for(int x = x0; x <= x1; ++x, intery += gradient)
{
unsigned y = intery >> FRACBITS;
unsigned alpha = fp_fpart(intery, FRACBITS) >> (FRACBITS - 8);
plot(y, x, (1 << 8) - alpha, r1, g1, b1, l, r, u, d);
plot(y + 1, x, alpha, r1, g1, b1, l, r, u, d);
}
}
else
{
for(int x = x0; x <= x1; ++x, intery += gradient)
{
unsigned y = intery >> FRACBITS;
unsigned alpha = fp_fpart(intery, FRACBITS) >> (FRACBITS - 8);
plot(x, y, (1 << 8) - alpha, r1, g1, b1, l, r, u, d);
plot(x, y + 1, alpha, r1, g1, b1, l, r, u, d);
}
}
}
static void rb_draw_line(void *handle, int x1, int y1, int x2, int y2, static void rb_draw_line(void *handle, int x1, int y1, int x2, int y2,
int color) int color)
{ {
LOGF("rb_draw_line(%d, %d, %d, %d, %d)", x1, y1, x2, y2, color); LOGF("rb_draw_line(%d, %d, %d, %d, %d)", x1, y1, x2, y2, color);
offset_coords(&x1, &y1);
offset_coords(&x2, &y2);
rb_color(color); rb_color(color);
rb->lcd_drawline(x1, y1, x2, y2); if(settings.no_aa)
{
offset_coords(&x1, &y1);
offset_coords(&x2, &y2);
rb->lcd_drawline(x1, y1, x2, y2);
}
else
draw_antialiased_line(x1, y1, x2, y2);
} }
/* /*
@ -351,12 +467,15 @@ static void rb_draw_poly(void *handle, int *coords, int npoints,
y1 = coords[2 * (i - 1) + 1]; y1 = coords[2 * (i - 1) + 1];
x2 = coords[2 * i]; x2 = coords[2 * i];
y2 = coords[2 * i + 1]; y2 = coords[2 * i + 1];
offset_coords(&x1, &y1); if(settings.no_aa)
offset_coords(&x2, &y2); {
rb->lcd_drawline(x1, y1, offset_coords(&x1, &y1);
x2, y2); offset_coords(&x2, &y2);
//rb->lcd_update(); rb->lcd_drawline(x1, y1,
//rb->sleep(HZ/2); x2, y2);
}
else
draw_antialiased_line(x1, y1, x2, y2);
} }
int x1, y1, x2, y2; int x1, y1, x2, y2;
@ -364,11 +483,16 @@ static void rb_draw_poly(void *handle, int *coords, int npoints,
y1 = coords[1]; y1 = coords[1];
x2 = coords[2 * (npoints - 1)]; x2 = coords[2 * (npoints - 1)];
y2 = coords[2 * (npoints - 1) + 1]; y2 = coords[2 * (npoints - 1) + 1];
offset_coords(&x1, &y1); if(settings.no_aa)
offset_coords(&x2, &y2); {
offset_coords(&x1, &y1);
offset_coords(&x2, &y2);
rb->lcd_drawline(x1, y1, rb->lcd_drawline(x1, y1,
x2, y2); x2, y2);
}
else
draw_antialiased_line(x1, y1, x2, y2);
} }
static void rb_draw_circle(void *handle, int cx, int cy, int radius, static void rb_draw_circle(void *handle, int cx, int cy, int radius,
@ -394,25 +518,6 @@ struct blitter {
struct bitmap bmp; struct bitmap bmp;
}; };
static blitter *rb_blitter_new(void *handle, int w, int h)
{
LOGF("rb_blitter_new");
blitter *b = snew(blitter);
b->bmp.width = w;
b->bmp.height = h;
b->bmp.data = smalloc(w * h * sizeof(fb_data));
b->have_data = false;
return b;
}
static void rb_blitter_free(void *handle, blitter *bl)
{
LOGF("rb_blitter_free");
sfree(bl->bmp.data);
sfree(bl);
return;
}
/* originally from emcc.c */ /* originally from emcc.c */
static void trim_rect(int *x, int *y, int *w, int *h) static void trim_rect(int *x, int *y, int *w, int *h)
{ {
@ -430,10 +535,10 @@ static void trim_rect(int *x, int *y, int *w, int *h)
y1 = *y + *h; y1 = *y + *h;
/* Clip each coordinate at both extremes of the canvas */ /* Clip each coordinate at both extremes of the canvas */
x0 = (x0 < 0 ? 0 : x0 > LCD_WIDTH ? LCD_WIDTH : x0); x0 = (x0 < 0 ? 0 : x0 > LCD_WIDTH - 1 ? LCD_WIDTH - 1: x0);
x1 = (x1 < 0 ? 0 : x1 > LCD_WIDTH ? LCD_WIDTH : x1); x1 = (x1 < 0 ? 0 : x1 > LCD_WIDTH - 1 ? LCD_WIDTH - 1: x1);
y0 = (y0 < 0 ? 0 : y0 > LCD_HEIGHT ? LCD_HEIGHT : y0); y0 = (y0 < 0 ? 0 : y0 > LCD_HEIGHT - 1 ? LCD_HEIGHT - 1: y0);
y1 = (y1 < 0 ? 0 : y1 > LCD_HEIGHT ? LCD_HEIGHT : y1); y1 = (y1 < 0 ? 0 : y1 > LCD_HEIGHT - 1 ? LCD_HEIGHT - 1: y1);
/* Transform back into x,y,w,h to return */ /* Transform back into x,y,w,h to return */
*x = x0; *x = x0;
@ -442,6 +547,25 @@ static void trim_rect(int *x, int *y, int *w, int *h)
*h = y1 - y0; *h = y1 - y0;
} }
static blitter *rb_blitter_new(void *handle, int w, int h)
{
LOGF("rb_blitter_new");
blitter *b = snew(blitter);
b->bmp.width = w;
b->bmp.height = h;
b->bmp.data = smalloc(w * h * sizeof(fb_data));
b->have_data = false;
return b;
}
static void rb_blitter_free(void *handle, blitter *bl)
{
LOGF("rb_blitter_free");
sfree(bl->bmp.data);
sfree(bl);
return;
}
/* copy a section of the framebuffer */ /* copy a section of the framebuffer */
static void rb_blitter_save(void *handle, blitter *bl, int x, int y) static void rb_blitter_save(void *handle, blitter *bl, int x, int y)
{ {
@ -623,8 +747,6 @@ void get_random_seed(void **randseed, int *randseedsize)
*randseed = snew(long); *randseed = snew(long);
long seed = *rb->current_tick; long seed = *rb->current_tick;
rb->memcpy(*randseed, &seed, sizeof(seed)); rb->memcpy(*randseed, &seed, sizeof(seed));
//*(long*)*randseed = 42; // debug
//rb->splash(HZ, "DEBUG SEED ON");
*randseedsize = sizeof(long); *randseedsize = sizeof(long);
} }
@ -683,8 +805,7 @@ static int list_choose(const char *list_str, const char *title)
} }
} }
/* return value is only meaningful when type == C_STRING */ static void do_configure_item(config_item *cfg)
static bool do_configure_item(config_item *cfg)
{ {
switch(cfg->type) switch(cfg->type)
{ {
@ -698,11 +819,11 @@ static bool do_configure_item(config_item *cfg)
if(rb->kbd_input(newstr, MAX_STRLEN) < 0) if(rb->kbd_input(newstr, MAX_STRLEN) < 0)
{ {
sfree(newstr); sfree(newstr);
return false; break;
} }
sfree(cfg->sval); sfree(cfg->sval);
cfg->sval = newstr; cfg->sval = newstr;
return true; break;
} }
case C_BOOLEAN: case C_BOOLEAN:
{ {
@ -726,7 +847,6 @@ static bool do_configure_item(config_item *cfg)
fatal("bad type"); fatal("bad type");
break; break;
} }
return false;
} }
const char *config_formatter(int sel, void *data, char *buf, size_t len) const char *config_formatter(int sel, void *data, char *buf, size_t len)
@ -737,11 +857,13 @@ const char *config_formatter(int sel, void *data, char *buf, size_t len)
return buf; return buf;
} }
static void config_menu(void) static bool config_menu(void)
{ {
char *title; char *title;
config_item *config = midend_get_config(me, CFG_SETTINGS, &title); config_item *config = midend_get_config(me, CFG_SETTINGS, &title);
bool success = false;
if(!config) if(!config)
{ {
rb->splash(HZ, "Nothing to configure."); rb->splash(HZ, "Nothing to configure.");
@ -782,22 +904,16 @@ static void config_menu(void)
config_item old; config_item old;
int pos = rb->gui_synclist_get_sel_pos(&list); int pos = rb->gui_synclist_get_sel_pos(&list);
memcpy(&old, config + pos, sizeof(old)); memcpy(&old, config + pos, sizeof(old));
char *old_str; do_configure_item(config + pos);
if(old.type == C_STRING)
old_str = dupstr(old.sval);
bool freed_str = do_configure_item(config + pos);
char *err = midend_set_config(me, CFG_SETTINGS, config); char *err = midend_set_config(me, CFG_SETTINGS, config);
if(err) if(err)
{ {
rb->splash(HZ, err); rb->splash(HZ, err);
memcpy(config + pos, &old, sizeof(old)); memcpy(config + pos, &old, sizeof(old));
if(freed_str)
config[pos].sval = old_str;
} }
else if(old.type == C_STRING) else
{ {
/* success, and we duplicated the old string, so free it */ success = true;
sfree(old_str);
} }
break; break;
} }
@ -813,6 +929,7 @@ static void config_menu(void)
done: done:
sfree(title); sfree(title);
free_cfg(config); free_cfg(config);
return success;
} }
const char *preset_formatter(int sel, void *data, char *buf, size_t len) const char *preset_formatter(int sel, void *data, char *buf, size_t len)
@ -824,12 +941,12 @@ const char *preset_formatter(int sel, void *data, char *buf, size_t len)
return buf; return buf;
} }
static void presets_menu(void) static bool presets_menu(void)
{ {
if(!midend_num_presets(me)) if(!midend_num_presets(me))
{ {
rb->splash(HZ, "No presets!"); rb->splash(HZ, "No presets!");
return; return false;
} }
/* display a list */ /* display a list */
@ -843,9 +960,8 @@ static void presets_menu(void)
int current = midend_which_preset(me); int current = midend_which_preset(me);
rb->gui_synclist_select_item(&list, current >= 0 ? current : 0); rb->gui_synclist_select_item(&list, current >= 0 ? current : 0);
bool done = false;
rb->gui_synclist_set_title(&list, "Game Type", NOICON); rb->gui_synclist_set_title(&list, "Game Type", NOICON);
while (!done) while(1)
{ {
rb->gui_synclist_draw(&list); rb->gui_synclist_draw(&list);
int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK); int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
@ -860,13 +976,11 @@ static void presets_menu(void)
game_params *params; game_params *params;
midend_fetch_preset(me, sel, &junk, &params); midend_fetch_preset(me, sel, &junk, &params);
midend_set_params(me, params); midend_set_params(me, params);
done = true; return true;
break;
} }
case ACTION_STD_PREV: case ACTION_STD_PREV:
case ACTION_STD_CANCEL: case ACTION_STD_CANCEL:
done = true; return false;
break;
default: default:
break; break;
} }
@ -948,6 +1062,37 @@ static void init_default_settings(void)
settings.slowmo_factor = 1; settings.slowmo_factor = 1;
settings.bulk = false; settings.bulk = false;
settings.timerflash = false; settings.timerflash = false;
settings.clipoff = false;
settings.shortcuts = false;
settings.no_aa = false;
}
static void bench_aa(void)
{
rb->sleep(0);
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(true);
#endif
int next = *rb->current_tick + HZ;
int i = 0;
while(*rb->current_tick < next)
{
draw_antialiased_line(0, 0, 20, 31);
++i;
}
rb->splashf(HZ, "%d AA lines/sec", i);
next = *rb->current_tick + HZ;
int j = 0;
while(*rb->current_tick < next)
{
rb->lcd_drawline(0, 0, 20, 31);
++j;
}
rb->splashf(HZ, "%d normal lines/sec", j);
rb->splashf(HZ, "Efficiency: %d%%", 100 * i / j);
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
} }
static void debug_menu(void) static void debug_menu(void)
@ -958,6 +1103,9 @@ static void debug_menu(void)
"Toggle bulk update", "Toggle bulk update",
"Toggle flash pixel on timer", "Toggle flash pixel on timer",
"Toggle clip", "Toggle clip",
"Toggle shortcuts",
"Toggle antialias",
"Benchmark antialias",
"Back"); "Back");
bool quit = false; bool quit = false;
int sel = 0; int sel = 0;
@ -988,6 +1136,15 @@ static void debug_menu(void)
settings.clipoff = !settings.clipoff; settings.clipoff = !settings.clipoff;
break; break;
case 5: case 5:
settings.shortcuts = !settings.shortcuts;
break;
case 6:
settings.no_aa = !settings.no_aa;
break;
case 7:
bench_aa();
break;
case 8:
default: default:
quit = true; quit = true;
break; break;
@ -1055,6 +1212,23 @@ static int pausemenu_cb(int action, const struct menu_item_ex *this_item)
return action; return action;
} }
static void clear_and_draw(void)
{
rb->lcd_clear_display();
rb->lcd_update();
midend_force_redraw(me);
draw_title();
}
static void reset_drawing(void)
{
rb->lcd_set_viewport(NULL);
rb->lcd_set_backdrop(NULL);
rb->lcd_set_foreground(LCD_BLACK);
rb->lcd_set_background(BG_COLOR);
}
static int pause_menu(void) static int pause_menu(void)
{ {
#define static auto #define static auto
@ -1139,19 +1313,27 @@ static int pause_menu(void)
playback_control(NULL); playback_control(NULL);
break; break;
case 9: case 9:
presets_menu(); if(presets_menu())
midend_new_game(me); {
fix_size(); midend_new_game(me);
quit = true; fix_size();
reset_drawing();
clear_and_draw();
quit = true;
}
break; break;
case 10: case 10:
debug_menu(); debug_menu();
break; break;
case 11: case 11:
config_menu(); if(config_menu())
midend_new_game(me); {
fix_size(); midend_new_game(me);
quit = true; fix_size();
reset_drawing();
clear_and_draw();
quit = true;
}
break; break;
#ifdef COMBINED #ifdef COMBINED
case 12: case 12:
@ -1257,8 +1439,8 @@ static int process_input(int tmo)
} }
LOGF("accepting event 0x%08x", button); LOGF("accepting event 0x%08x", button);
} }
/* not inertia: events fire on presses */ /* default is to ignore repeats except for untangle */
else else if(strcmp("Untangle", midend_which_game(me)->name))
{ {
/* start accepting input again after a release */ /* start accepting input again after a release */
if(!button) if(!button)
@ -1267,6 +1449,7 @@ static int process_input(int tmo)
return 0; return 0;
} }
/* ignore repeats */ /* ignore repeats */
/* Untangle gets special treatment */
if(!accept_input) if(!accept_input)
return 0; return 0;
accept_input = false; accept_input = false;
@ -1316,7 +1499,34 @@ static int process_input(int tmo)
case BTN_FIRE: case BTN_FIRE:
state = CURSOR_SELECT; state = CURSOR_SELECT;
break; break;
default:
break;
} }
if(settings.shortcuts)
{
static bool shortcuts_ok = true;
switch(button)
{
case BTN_LEFT | BTN_FIRE:
if(shortcuts_ok)
midend_process_key(me, 0, 0, 'u');
shortcuts_ok = false;
break;
case BTN_RIGHT | BTN_FIRE:
if(shortcuts_ok)
midend_process_key(me, 0, 0, 'r');
shortcuts_ok = false;
break;
case 0:
shortcuts_ok = true;
break;
default:
break;
}
}
LOGF("process_input done"); LOGF("process_input done");
LOGF("------------------"); LOGF("------------------");
return state; return state;
@ -1403,13 +1613,23 @@ static void write_wrapper(void *ptr, void *buf, int len)
rb->write(fd, buf, len); rb->write(fd, buf, len);
} }
static void clear_and_draw(void) static void init_colors(void)
{ {
rb->lcd_clear_display(); float *floatcolors = midend_colors(me, &ncolors);
rb->lcd_update();
midend_force_redraw(me); /* convert them to packed RGB */
draw_title(); colors = smalloc(ncolors * sizeof(unsigned));
unsigned *ptr = colors;
float *floatptr = floatcolors;
for(int i = 0; i < ncolors; ++i)
{
int r = 255 * *(floatptr++);
int g = 255 * *(floatptr++);
int b = 255 * *(floatptr++);
LOGF("color %d is %d %d %d", i, r, g, b);
*ptr++ = LCD_RGBPACK(r, g, b);
}
sfree(floatcolors);
} }
static char *init_for_game(const game *gm, int load_fd, bool draw) static char *init_for_game(const game *gm, int load_fd, bool draw)
@ -1431,26 +1651,9 @@ static char *init_for_game(const game *gm, int load_fd, bool draw)
fix_size(); fix_size();
float *floatcolors = midend_colors(me, &ncolors); init_colors();
/* convert them to packed RGB */ reset_drawing();
colors = smalloc(ncolors * sizeof(unsigned));
unsigned *ptr = colors;
float *floatptr = floatcolors;
for(int i = 0; i < ncolors; ++i)
{
int r = 255 * *(floatptr++);
int g = 255 * *(floatptr++);
int b = 255 * *(floatptr++);
LOGF("color %d is %d %d %d", i, r, g, b);
*ptr++ = LCD_RGBPACK(r, g, b);
}
sfree(floatcolors);
rb->lcd_set_viewport(NULL);
rb->lcd_set_backdrop(NULL);
rb->lcd_set_foreground(LCD_BLACK);
rb->lcd_set_background(BG_COLOR);
if(draw) if(draw)
{ {
@ -1690,22 +1893,26 @@ enum plugin_status plugin_start(const void *param)
playback_control(NULL); playback_control(NULL);
break; break;
case 5: case 5:
presets_menu(); if(presets_menu())
if(!load_success)
{ {
midend_new_game(me);
fix_size();
init_colors();
reset_drawing();
clear_and_draw(); clear_and_draw();
goto game_loop; goto game_loop;
} }
quit = true;
break; break;
case 6: case 6:
config_menu(); if(config_menu())
if(!load_success)
{ {
midend_new_game(me);
fix_size();
init_colors();
reset_drawing();
clear_and_draw(); clear_and_draw();
goto game_loop; goto game_loop;
} }
quit = true;
break; break;
case 8: case 8:
if(load_success) if(load_success)

View file

@ -1162,18 +1162,17 @@ static char *interpret_move(const game_state *state, game_ui *ui,
{ {
if(ui->dragpoint < 0) if(ui->dragpoint < 0)
{ {
if(ui->cursorpoint < 0)
{
ui->cursorpoint = 0;
return "";
}
/* We're selecting a point here. */ /* We're selecting a point here. */
/* Search all the points and find the closest one (2-D) in /* Search all the points and find the closest one (2-D) in
* the given direction. */ * the given direction. */
int i, best; int i, best;
long bestd; long bestd;
if(ui->cursorpoint < 0)
{
ui->cursorpoint = 0;
}
/* /*
* Begin drag. We drag the vertex _nearest_ to the pointer, * Begin drag. We drag the vertex _nearest_ to the pointer,
* just in case one is nearly on top of another and we want * just in case one is nearly on top of another and we want
@ -1196,7 +1195,7 @@ static char *interpret_move(const game_state *state, game_ui *ui,
/* Figure out if this point falls into a 90 degree /* Figure out if this point falls into a 90 degree
* range extending from the current point */ * range extending from the current point */
float angle = atan2(-dy, dx); /* adjust for raster coordinates */ float angle = atan2(-dy, dx); /* negate y to adjust for raster coordinates */
/* offset to [0..2*PI] */ /* offset to [0..2*PI] */
if(angle < 0) if(angle < 0)
@ -1494,6 +1493,8 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
ds->bg = bg; ds->bg = bg;
game_compute_size(&state->params, ds->tilesize, &w, &h); game_compute_size(&state->params, ds->tilesize, &w, &h);
clip(dr, 0, 0, w, h);
draw_rect(dr, 0, 0, w, h, bg); draw_rect(dr, 0, 0, w, h, bg);
/* /*