Some selection optimizations

- Add bounds, so that we don't process any locations outside
  as those locations are known to be unset
- The bounds are only recalculated if needed
- Replace instances of selection_not where we actually want
  a new selection with all locations set
This commit is contained in:
Pasi Kallinen
2022-08-26 12:31:25 +03:00
parent e815498f07
commit 5e9ed7a290
5 changed files with 203 additions and 31 deletions

View File

@@ -399,6 +399,8 @@ E NEARDATA struct savefile_info sfcap, sfrestinfo, sfsaveinfo;
struct selectionvar {
int wid, hei;
boolean bounds_dirty;
NhRect bounds; /* use selection_getbounds() */
char *map;
};

View File

@@ -2307,6 +2307,7 @@ extern void init_rect(void);
extern void free_rect(void);
extern NhRect *get_rect(NhRect *);
extern NhRect *rnd_rect(void);
extern void rect_bounds(NhRect, NhRect, NhRect *);
extern void remove_rect(NhRect *);
extern void add_rect(NhRect *);
extern void split_rects(NhRect *, NhRect *);
@@ -2582,7 +2583,9 @@ extern boolean load_special(const char *);
extern coordxy selection_getpoint(coordxy, coordxy, struct selectionvar *);
extern struct selectionvar *selection_new(void);
extern void selection_free(struct selectionvar *, boolean);
extern void selection_clear(struct selectionvar *, int);
extern struct selectionvar *selection_clone(struct selectionvar *);
extern void selection_getbounds(struct selectionvar *, NhRect *);
extern void set_selection_floodfillchk(int(*)(coordxy, coordxy));
extern void selection_floodfill(struct selectionvar *, coordxy, coordxy, boolean);
extern boolean pm_good_location(coordxy, coordxy, struct permonst *);

View File

@@ -234,7 +234,7 @@ l_selection_not(lua_State *L)
if (argc == 0) {
(void) l_selection_new(L);
sel = l_selection_check(L, 1);
selection_not(sel);
selection_clear(sel, 1);
} else {
sel = l_selection_check(L, 1);
(void) l_selection_clone(L);
@@ -252,9 +252,12 @@ l_selection_and(lua_State *L)
struct selectionvar *sela = l_selection_check(L, 1);
struct selectionvar *selb = l_selection_check(L, 2);
struct selectionvar *selr = l_selection_push_new(L);
NhRect rect;
for (x = 0; x < selr->wid; x++)
for (y = 0; y < selr->hei; y++) {
rect_bounds(sela->bounds, selb->bounds, &rect);
for (x = rect.lx; x <= rect.hx; x++)
for (y = rect.ly; y <= rect.hy; y++) {
int val = selection_getpoint(x, y, sela) & selection_getpoint(x, y, selb);
selection_setpoint(x, y, selr, val);
}
@@ -272,9 +275,12 @@ l_selection_or(lua_State *L)
struct selectionvar *sela = l_selection_check(L, 1);
struct selectionvar *selb = l_selection_check(L, 2);
struct selectionvar *selr = l_selection_push_new(L);
NhRect rect;
for (x = 0; x < selr->wid; x++)
for (y = 0; y < selr->hei; y++) {
rect_bounds(sela->bounds, selb->bounds, &rect);
for (x = rect.lx; x <= rect.hx; x++)
for (y = rect.ly; y <= rect.hy; y++) {
int val = selection_getpoint(x, y, sela) | selection_getpoint(x, y, selb);
selection_setpoint(x, y, selr, val);
}
@@ -292,9 +298,12 @@ l_selection_xor(lua_State *L)
struct selectionvar *sela = l_selection_check(L, 1);
struct selectionvar *selb = l_selection_check(L, 2);
struct selectionvar *selr = l_selection_push_new(L);
NhRect rect;
for (x = 0; x < selr->wid; x++)
for (y = 0; y < selr->hei; y++) {
rect_bounds(sela->bounds, selb->bounds, &rect);
for (x = rect.lx; x <= rect.hx; x++)
for (y = rect.ly; y <= rect.hy; y++) {
int val = selection_getpoint(x, y, sela) ^ selection_getpoint(x, y, selb);
selection_setpoint(x, y, selr, val);
}
@@ -313,15 +322,17 @@ l_selection_sub(lua_State *L)
struct selectionvar *sela = l_selection_check(L, 1);
struct selectionvar *selb = l_selection_check(L, 2);
struct selectionvar *selr = l_selection_push_new(L);
NhRect rect;
for (x = 0; x < selr->wid; x++) {
for (y = 0; y < selr->hei; y++) {
rect_bounds(sela->bounds, selb->bounds, &rect);
for (x = rect.lx; x <= rect.hx; x++)
for (y = rect.ly; y <= rect.hy; y++) {
coordxy a_pt = selection_getpoint(x, y, sela);
coordxy b_pt = selection_getpoint(x, y, selb);
int val = (a_pt ^ b_pt) & a_pt;
selection_setpoint(x, y, selr, val);
}
}
lua_remove(L, 1);
lua_remove(L, 1);
@@ -817,11 +828,13 @@ l_selection_iterate(lua_State *L)
int argc = lua_gettop(L);
struct selectionvar *sel = (struct selectionvar *) 0;
int x, y;
NhRect rect;
if (argc == 2 && lua_type(L, 2) == LUA_TFUNCTION) {
sel = l_selection_check(L, 1);
for (y = 0; y < sel->hei; y++)
for (x = 1; x < sel->wid; x++)
selection_getbounds(sel, &rect);
for (y = rect.ly; y <= rect.hy; y++)
for (x = max(1,rect.lx); x <= rect.hx; x++)
if (selection_getpoint(x, y, sel)) {
lua_pushvalue(L, 2);
lua_pushinteger(L, x - g.xstart);

View File

@@ -128,6 +128,16 @@ intersect(NhRect* r1, NhRect* r2, NhRect* r3)
return TRUE;
}
/* Put the rectangle containing both r1 and r2 into r3 */
void
rect_bounds(NhRect r1, NhRect r2, NhRect *r3)
{
r3->lx = min(r1.lx, r2.lx);
r3->ly = min(r1.ly, r2.ly);
r3->hx = max(r1.hx, r2.hx);
r3->hy = max(r1.hy, r2.hy);
}
/*
* Remove a rectangle from the list of free NhRect.
*/

View File

@@ -123,6 +123,7 @@ static const char *get_mkroom_name(int);
static int get_table_roomtype_opt(lua_State *, const char *, int);
static int get_table_traptype_opt(lua_State *, const char *, int);
static int get_traptype_byname(const char *);
static void selection_recalc_bounds(struct selectionvar *);
static lua_Integer get_table_intarray_entry(lua_State *, int, int);
static struct sp_coder *sp_level_coder_init(void);
@@ -4365,6 +4366,10 @@ selection_new(void)
tmps->wid = COLNO;
tmps->hei = ROWNO;
tmps->bounds_dirty = FALSE;
tmps->bounds.lx = COLNO;
tmps->bounds.ly = ROWNO;
tmps->bounds.hx = tmps->bounds.hy = 0;
tmps->map = (char *) alloc((COLNO * ROWNO) + 1);
(void) memset(tmps->map, 1, (COLNO * ROWNO));
tmps->map[(COLNO * ROWNO)] = '\0';
@@ -4385,6 +4390,24 @@ selection_free(struct selectionvar* sel, boolean freesel)
}
}
/* clear selection, setting all locations to value val */
void
selection_clear(struct selectionvar *sel, int val)
{
(void) memset(sel->map, 1 + val, (COLNO * ROWNO));
if (val) {
sel->bounds.lx = 0;
sel->bounds.ly = 0;
sel->bounds.hx = COLNO - 1;
sel->bounds.hy = ROWNO - 1;
} else {
sel->bounds.lx = COLNO;
sel->bounds.ly = ROWNO;
sel->bounds.hx = sel->bounds.hy = 0;
}
sel->bounds_dirty = FALSE;
}
struct selectionvar *
selection_clone(struct selectionvar* sel)
{
@@ -4396,6 +4419,98 @@ selection_clone(struct selectionvar* sel)
return tmps;
}
/* get boundary rect of selection sel into b */
void
selection_getbounds(struct selectionvar *sel, NhRect *b)
{
if (!sel || !b)
return;
selection_recalc_bounds(sel);
if (sel->bounds.lx >= sel->wid) {
b->lx = 0;
b->ly = 0;
b->hx = COLNO - 1;
b->hy = ROWNO - 1;
} else {
b->lx = sel->bounds.lx;
b->ly = sel->bounds.ly;
b->hx = sel->bounds.hx;
b->hy = sel->bounds.hy;
}
}
/* recalc the boundary of selection, if necessary */
static void
selection_recalc_bounds(struct selectionvar *sel)
{
coordxy x, y;
NhRect r;
if (!sel->bounds_dirty)
return;
sel->bounds.lx = COLNO;
sel->bounds.ly = ROWNO;
sel->bounds.hx = sel->bounds.hy = 0;
r.lx = r.ly = r.hx = r.hy = -1;
/* left */
for (x = 0; x < sel->wid; x++) {
for (y = 0; y < sel->hei; y++) {
if (selection_getpoint(x, y, sel)) {
r.lx = x;
break;
}
}
if (r.lx > -1)
break;
}
if (r.lx > -1) {
/* right */
for (x = sel->wid-1; x >= r.lx; x--) {
for (y = 0; y < sel->hei; y++) {
if (selection_getpoint(x, y, sel)) {
r.hx = x;
break;
}
}
if (r.hx > -1)
break;
}
/* top */
for (y = 0; y < sel->hei; y++) {
for (x = r.lx; x <= r.hx; x++) {
if (selection_getpoint(x, y, sel)) {
r.ly = y;
break;
}
}
if (r.ly > -1)
break;
}
/* bottom */
for (y = sel->hei-1; y >= r.ly; y--) {
for (x = r.lx; x <= r.hx; x++) {
if (selection_getpoint(x, y, sel)) {
r.hy = y;
break;
}
}
if (r.hy > -1)
break;
}
sel->bounds = r;
}
sel->bounds_dirty = FALSE;
}
coordxy
selection_getpoint(coordxy x, coordxy y, struct selectionvar* sel)
{
@@ -4415,6 +4530,15 @@ selection_setpoint(coordxy x, coordxy y, struct selectionvar* sel, int c)
if (x < 0 || y < 0 || x >= sel->wid || y >= sel->hei)
return;
if (c && !sel->bounds_dirty) {
if (sel->bounds.lx > x) sel->bounds.lx = x;
if (sel->bounds.ly > y) sel->bounds.ly = y;
if (sel->bounds.hx < x) sel->bounds.hx = x;
if (sel->bounds.hy < y) sel->bounds.hy = y;
} else {
sel->bounds_dirty = TRUE;
}
sel->map[sel->wid * y + x] = (char) (c + 1);
}
@@ -4436,14 +4560,17 @@ selection_filter_mapchar(struct selectionvar* ov, xint16 typ, int lit)
{
int x, y;
struct selectionvar *ret;
NhRect rect;
if (!ov)
return NULL;
ret = selection_new();
for (x = 1; x < ret->wid; x++)
for (y = 0; y < ret->hei; y++)
selection_getbounds(ov, &rect);
for (x = rect.lx; x <= rect.hx; x++)
for (y = rect.ly; y <= rect.hy; y++)
if (selection_getpoint(x, y, ov)
&& match_maptyps(typ, levl[x][y].typ)) {
switch (lit) {
@@ -4469,16 +4596,20 @@ selection_filter_percent(struct selectionvar* ov, int percent)
{
int x, y;
struct selectionvar *ret;
NhRect rect;
if (!ov)
return NULL;
ret = selection_new();
for (x = 0; x < ov->wid; x++)
for (y = 0; y < ov->hei; y++)
selection_getbounds(ov, &rect);
for (x = rect.lx; x <= rect.hx; x++)
for (y = rect.ly; y <= rect.hy; y++)
if (selection_getpoint(x, y, ov) && (rn2(100) < percent))
selection_setpoint(x, y, ret, 1);
return ret;
}
@@ -4488,16 +4619,19 @@ selection_rndcoord(struct selectionvar* ov, coordxy *x, coordxy *y, boolean remo
int idx = 0;
int c;
int dx, dy;
NhRect rect;
for (dx = 1; dx < ov->wid; dx++)
for (dy = 0; dy < ov->hei; dy++)
selection_getbounds(ov, &rect);
for (dx = rect.lx; dx <= rect.hx; dx++)
for (dy = rect.ly; dy <= rect.hy; dy++)
if (selection_getpoint(dx, dy, ov))
idx++;
if (idx) {
c = rn2(idx);
for (dx = 1; dx < ov->wid; dx++)
for (dy = 0; dy < ov->hei; dy++)
for (dx = rect.lx; dx <= rect.hx; dx++)
for (dy = rect.ly; dy <= rect.hy; dy++)
if (selection_getpoint(dx, dy, ov)) {
if (!c) {
*x = dx;
@@ -4526,6 +4660,7 @@ selection_do_grow(struct selectionvar* ov, int dir)
{
coordxy x, y;
struct selectionvar *tmp;
NhRect rect;
if (!ov)
return;
@@ -4535,8 +4670,10 @@ selection_do_grow(struct selectionvar* ov, int dir)
if (dir == W_RANDOM)
dir = random_wdir();
for (x = 1; x < ov->wid; x++)
for (y = 0; y < ov->hei; y++) {
selection_getbounds(ov, &rect);
for (x = max(0, rect.lx-1); x <= min(COLNO-1, rect.hx+1); x++)
for (y = max(0, rect.ly-1); y <= min(ROWNO-1, rect.hy+1); y++) {
/* note: dir is a mask of multiple directions, but the only
way to specify diagonals is by including the two adjacent
orthogonal directions, which effectively specifies three-
@@ -4557,8 +4694,10 @@ selection_do_grow(struct selectionvar* ov, int dir)
}
}
for (x = 1; x < ov->wid; x++)
for (y = 0; y < ov->hei; y++)
selection_getbounds(tmp, &rect);
for (x = rect.lx; x <= rect.hx; x++)
for (y = rect.ly; y <= rect.hy; y++)
if (selection_getpoint(x, y, tmp))
selection_setpoint(x, y, ov, 1);
@@ -4943,13 +5082,15 @@ selection_iterate(
genericptr_t arg)
{
coordxy x, y;
NhRect rect;
if (!ov)
return;
/* yes, this is very naive, but it's not _that_ expensive. */
for (x = 0; x < ov->wid; x++)
for (y = 0; y < ov->hei; y++)
selection_getbounds(ov, &rect);
for (x = rect.lx; x <= rect.hx; x++)
for (y = rect.ly; y <= rect.hy; y++)
if (isok(x,y) && selection_getpoint(x, y, ov))
(*func)(x, y, arg);
}
@@ -5278,6 +5419,7 @@ lspo_replace_terrain(lua_State *L)
lua_Integer x1, y1, x2, y2;
int chance;
int tolit;
NhRect rect;
create_des_coder();
@@ -5325,7 +5467,7 @@ lspo_replace_terrain(lua_State *L)
freesel = TRUE;
if (x1 == -1 && y1 == -1 && x2 == -1 && y2 == -1) {
(void) selection_not(sel);
(void) selection_clear(sel, 1);
} else {
coordxy rx1, ry1, rx2, ry2;
rx1 = x1, ry1 = y1, rx2 = x2, ry2 = y2;
@@ -5337,8 +5479,10 @@ lspo_replace_terrain(lua_State *L)
}
}
for (y = 0; y <= sel->hei; y++)
for (x = 1; x < sel->wid; x++)
selection_getbounds(sel, &rect);
for (x = max(1, rect.lx); x <= rect.hx; x++)
for (y = rect.ly; y <= rect.hy; y++)
if (selection_getpoint(x, y,sel)) {
if (mf) {
if (mapfrag_match(mf, x, y) && (rn2(100)) < chance)
@@ -6059,7 +6203,7 @@ set_wallprop_in_selection(lua_State *L, int prop)
} else if (argc == 0) {
freesel = TRUE;
sel = selection_new();
selection_not(sel);
selection_clear(sel, 1);
}
if (sel) {