gcc has recognized various "magic comments" for white-listing
occurrences of implicit fallthrough in switch statements for
a long time:
The range and shape of "falls through" comments accepted are
contingent upon the level of the warning. (The default level is =3.)
-Wimplicit-fallthrough=0 disables the warning altogether.
-Wimplicit-fallthrough=1 treats any kind of comment as a "falls through" comment.
-Wimplicit-fallthrough=2 essentially accepts any comment that contains something
that matches (case insensitively) "falls?[ \t-]*thr(ough|u)" regular expression.
-Wimplicit-fallthrough=3 case sensitively matches a wide range of regular
expressions, listed in the GCC manual. E.g., all of these are accepted:
/* Falls through. */
/* fall-thru */
/* Else falls through. */
/* FALLTHRU */
/* ... falls through ... */
etc.
-Wimplicit-fallthrough=4 also, case sensitively matches a range of regular
expressions but is much more strict than level =3.
-Wimplicit-fallthrough=5 doesn't recognize any comments.
Plenty of other compilers did not recognize the gcc comment convention,
and up until now the compiler warning for detecting unintended
fallthrough had to be suppressed on other compilers. That's because the code
in NetHack has been relying on the gcc approach, and only the gcc approach.
The C23 standard introduces an attribute [[fallthrough]] for the
functionality, when implicit fallthrough warnings have been enabled.
Several popular compilers already support that, or a very similar attribute
style approach, today, even ahead of their C23 support:
C compiler whitelist approach
--------------------------- -------------------------------------
C23 conforming compilers [[fallthrough]]
clang versions supporting
standards prior to
C23 __attribute__((__fallthrough__))
Microsoft Visual Studio
since VS 2022 17.4.
The warning C5262 controls
whether the implict
fallthrough is detected and
warned about with
/std:clatest. [[fallthrough]]
This adds support to NetHack for the attribute approach by inserting a
macro FALLTHROUGH to the existing cases that require white-listing, so
other compilers can analyze things too.
The definition of the FALLTHROUGH macro is controlled in include/tradstdc.h.
The gcc comment approach has also been left in place at this time.
806 lines
21 KiB
C
806 lines
21 KiB
C
/* NetHack 3.7 selvar.c $NHDT-Date: 1709677544 2024/03/05 22:25:44 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.360 $ */
|
|
/* Copyright (c) 2024 by Pasi Kallinen */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
#include "selvar.h"
|
|
#include "sp_lev.h"
|
|
|
|
staticfn boolean sel_flood_havepoint(coordxy, coordxy, coordxy *, coordxy *,
|
|
int);
|
|
staticfn long line_dist_coord(long, long, long, long, long, long);
|
|
|
|
/* selection */
|
|
struct selectionvar *
|
|
selection_new(void)
|
|
{
|
|
struct selectionvar *tmps = (struct selectionvar *) alloc(sizeof *tmps);
|
|
|
|
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';
|
|
|
|
return tmps;
|
|
}
|
|
|
|
void
|
|
selection_free(struct selectionvar *sel, boolean freesel)
|
|
{
|
|
if (sel) {
|
|
if (sel->map)
|
|
free(sel->map);
|
|
sel->map = NULL;
|
|
if (freesel)
|
|
free((genericptr_t) sel);
|
|
else
|
|
(void) memset((genericptr_t) sel, 0, sizeof *sel);
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
struct selectionvar *tmps = (struct selectionvar *) alloc(sizeof *tmps);
|
|
|
|
*tmps = *sel;
|
|
tmps->map = dupstr(sel->map);
|
|
|
|
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 */
|
|
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)
|
|
{
|
|
if (!sel || !sel->map)
|
|
return 0;
|
|
if (x < 0 || y < 0 || x >= sel->wid || y >= sel->hei)
|
|
return 0;
|
|
|
|
return (sel->map[sel->wid * y + x] - 1);
|
|
}
|
|
|
|
void
|
|
selection_setpoint(
|
|
coordxy x, coordxy y,
|
|
struct selectionvar *sel,
|
|
int c)
|
|
{
|
|
if (!sel || !sel->map)
|
|
return;
|
|
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);
|
|
}
|
|
|
|
struct selectionvar *
|
|
selection_not(struct selectionvar *s)
|
|
{
|
|
int x, y;
|
|
NhRect tmprect = cg.zeroNhRect;
|
|
|
|
for (x = 0; x < s->wid; x++)
|
|
for (y = 0; y < s->hei; y++)
|
|
selection_setpoint(x, y, s, selection_getpoint(x, y, s) ? 0 : 1);
|
|
selection_getbounds(s, &tmprect);
|
|
return s;
|
|
}
|
|
|
|
struct selectionvar *
|
|
selection_filter_percent(
|
|
struct selectionvar *ov,
|
|
int percent)
|
|
{
|
|
int x, y;
|
|
struct selectionvar *ret;
|
|
NhRect rect = cg.zeroNhRect;
|
|
|
|
if (!ov)
|
|
return NULL;
|
|
|
|
ret = selection_new();
|
|
|
|
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;
|
|
}
|
|
|
|
struct selectionvar *
|
|
selection_filter_mapchar(struct selectionvar *ov, xint16 typ, int lit)
|
|
{
|
|
int x, y;
|
|
struct selectionvar *ret;
|
|
NhRect rect = cg.zeroNhRect;
|
|
|
|
if (!ov)
|
|
return NULL;
|
|
|
|
ret = selection_new();
|
|
|
|
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) {
|
|
default:
|
|
case -2:
|
|
selection_setpoint(x, y, ret, 1);
|
|
break;
|
|
case -1:
|
|
selection_setpoint(x, y, ret, rn2(2));
|
|
break;
|
|
case 0:
|
|
case 1:
|
|
if (levl[x][y].lit == (unsigned int) lit)
|
|
selection_setpoint(x, y, ret, 1);
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
selection_rndcoord(
|
|
struct selectionvar *ov,
|
|
coordxy *x, coordxy *y,
|
|
boolean removeit)
|
|
{
|
|
int idx = 0;
|
|
int c;
|
|
int dx, dy;
|
|
NhRect rect = cg.zeroNhRect;
|
|
|
|
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 = rect.lx; dx <= rect.hx; dx++)
|
|
for (dy = rect.ly; dy <= rect.hy; dy++)
|
|
if (selection_getpoint(dx, dy, ov)) {
|
|
if (!c) {
|
|
*x = dx;
|
|
*y = dy;
|
|
if (removeit)
|
|
selection_setpoint(dx, dy, ov, 0);
|
|
return 1;
|
|
}
|
|
c--;
|
|
}
|
|
}
|
|
*x = *y = -1;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
selection_do_grow(struct selectionvar *ov, int dir)
|
|
{
|
|
coordxy x, y;
|
|
struct selectionvar *tmp;
|
|
NhRect rect = cg.zeroNhRect;
|
|
|
|
if (!ov)
|
|
return;
|
|
|
|
tmp = selection_new();
|
|
|
|
if (dir == W_RANDOM)
|
|
dir = random_wdir();
|
|
|
|
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-
|
|
way growth [WEST|NORTH => WEST plus WEST|NORTH plus NORTH] */
|
|
if (((dir & W_WEST) && selection_getpoint(x + 1, y, ov))
|
|
|| (((dir & (W_WEST | W_NORTH)) == (W_WEST | W_NORTH))
|
|
&& selection_getpoint(x + 1, y + 1, ov))
|
|
|| ((dir & W_NORTH) && selection_getpoint(x, y + 1, ov))
|
|
|| (((dir & (W_NORTH | W_EAST)) == (W_NORTH | W_EAST))
|
|
&& selection_getpoint(x - 1, y + 1, ov))
|
|
|| ((dir & W_EAST) && selection_getpoint(x - 1, y, ov))
|
|
|| (((dir & (W_EAST | W_SOUTH)) == (W_EAST | W_SOUTH))
|
|
&& selection_getpoint(x - 1, y - 1, ov))
|
|
|| ((dir & W_SOUTH) && selection_getpoint(x, y - 1, ov))
|
|
|| (((dir & (W_SOUTH | W_WEST)) == (W_SOUTH | W_WEST))
|
|
&& selection_getpoint(x + 1, y - 1, ov))) {
|
|
selection_setpoint(x, y, tmp, 1);
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
selection_free(tmp, TRUE);
|
|
}
|
|
|
|
staticfn int (*selection_flood_check_func)(coordxy, coordxy);
|
|
|
|
void
|
|
set_selection_floodfillchk(int (*f)(coordxy, coordxy))
|
|
{
|
|
selection_flood_check_func = f;
|
|
}
|
|
|
|
/* check whethere <x,y> is already in xs[],ys[] */
|
|
staticfn boolean
|
|
sel_flood_havepoint(
|
|
coordxy x, coordxy y,
|
|
coordxy xs[], coordxy ys[],
|
|
int n)
|
|
{
|
|
coordxy xx = x, yy = y;
|
|
|
|
while (n > 0) {
|
|
--n;
|
|
if (xs[n] == xx && ys[n] == yy)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
selection_floodfill(
|
|
struct selectionvar *ov,
|
|
coordxy x, coordxy y,
|
|
boolean diagonals)
|
|
{
|
|
struct selectionvar *tmp = selection_new();
|
|
#define SEL_FLOOD_STACK (COLNO * ROWNO)
|
|
#define SEL_FLOOD(nx, ny) \
|
|
do { \
|
|
if (idx < SEL_FLOOD_STACK) { \
|
|
dx[idx] = (nx); \
|
|
dy[idx] = (ny); \
|
|
idx++; \
|
|
} else \
|
|
panic(floodfill_stack_overrun); \
|
|
} while (0)
|
|
#define SEL_FLOOD_CHKDIR(mx, my, sel) \
|
|
do { \
|
|
if (isok((mx), (my)) \
|
|
&& (*selection_flood_check_func)((mx), (my)) \
|
|
&& !selection_getpoint((mx), (my), (sel)) \
|
|
&& !sel_flood_havepoint((mx), (my), dx, dy, idx)) \
|
|
SEL_FLOOD((mx), (my)); \
|
|
} while (0)
|
|
static const char floodfill_stack_overrun[] = "floodfill stack overrun";
|
|
int idx = 0;
|
|
coordxy dx[SEL_FLOOD_STACK];
|
|
coordxy dy[SEL_FLOOD_STACK];
|
|
|
|
if (selection_flood_check_func == (int (*)(coordxy, coordxy)) 0) {
|
|
selection_free(tmp, TRUE);
|
|
return;
|
|
}
|
|
SEL_FLOOD(x, y);
|
|
do {
|
|
idx--;
|
|
x = dx[idx];
|
|
y = dy[idx];
|
|
if (isok(x, y)) {
|
|
selection_setpoint(x, y, ov, 1);
|
|
selection_setpoint(x, y, tmp, 1);
|
|
}
|
|
SEL_FLOOD_CHKDIR((x + 1), y, tmp);
|
|
SEL_FLOOD_CHKDIR((x - 1), y, tmp);
|
|
SEL_FLOOD_CHKDIR(x, (y + 1), tmp);
|
|
SEL_FLOOD_CHKDIR(x, (y - 1), tmp);
|
|
if (diagonals) {
|
|
SEL_FLOOD_CHKDIR((x + 1), (y + 1), tmp);
|
|
SEL_FLOOD_CHKDIR((x - 1), (y - 1), tmp);
|
|
SEL_FLOOD_CHKDIR((x - 1), (y + 1), tmp);
|
|
SEL_FLOOD_CHKDIR((x + 1), (y - 1), tmp);
|
|
}
|
|
} while (idx > 0);
|
|
#undef SEL_FLOOD
|
|
#undef SEL_FLOOD_STACK
|
|
#undef SEL_FLOOD_CHKDIR
|
|
selection_free(tmp, TRUE);
|
|
}
|
|
|
|
/* McIlroy's Ellipse Algorithm */
|
|
void
|
|
selection_do_ellipse(
|
|
struct selectionvar *ov,
|
|
int xc, int yc,
|
|
int a, int b,
|
|
int filled)
|
|
{ /* e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 */
|
|
int x = 0, y = b;
|
|
long a2 = (long) a * a, b2 = (long) b * b;
|
|
long crit1 = -(a2 / 4 + a % 2 + b2);
|
|
long crit2 = -(b2 / 4 + b % 2 + a2);
|
|
long crit3 = -(b2 / 4 + b % 2);
|
|
long t = -a2 * y; /* e(x+1/2,y-1/2) - (a^2+b^2)/4 */
|
|
long dxt = 2 * b2 * x, dyt = -2 * a2 * y;
|
|
long d2xt = 2 * b2, d2yt = 2 * a2;
|
|
long width = 1;
|
|
long i;
|
|
|
|
if (!ov)
|
|
return;
|
|
|
|
filled = !filled;
|
|
|
|
if (!filled) {
|
|
while (y >= 0 && x <= a) {
|
|
selection_setpoint(xc + x, yc + y, ov, 1);
|
|
if (x != 0 || y != 0)
|
|
selection_setpoint(xc - x, yc - y, ov, 1);
|
|
if (x != 0 && y != 0) {
|
|
selection_setpoint(xc + x, yc - y, ov, 1);
|
|
selection_setpoint(xc - x, yc + y, ov, 1);
|
|
}
|
|
if (t + b2 * x <= crit1 /* e(x+1,y-1/2) <= 0 */
|
|
|| t + a2 * y <= crit3) { /* e(x+1/2,y) <= 0 */
|
|
x++;
|
|
dxt += d2xt;
|
|
t += dxt;
|
|
} else if (t - a2 * y > crit2) { /* e(x+1/2,y-1) > 0 */
|
|
y--;
|
|
dyt += d2yt;
|
|
t += dyt;
|
|
} else {
|
|
x++;
|
|
dxt += d2xt;
|
|
t += dxt;
|
|
y--;
|
|
dyt += d2yt;
|
|
t += dyt;
|
|
}
|
|
}
|
|
} else {
|
|
while (y >= 0 && x <= a) {
|
|
if (t + b2 * x <= crit1 /* e(x+1,y-1/2) <= 0 */
|
|
|| t + a2 * y <= crit3) { /* e(x+1/2,y) <= 0 */
|
|
x++;
|
|
dxt += d2xt;
|
|
t += dxt;
|
|
width += 2;
|
|
} else if (t - a2 * y > crit2) { /* e(x+1/2,y-1) > 0 */
|
|
for (i = 0; i < width; i++)
|
|
selection_setpoint(xc - x + i, yc - y, ov, 1);
|
|
if (y != 0)
|
|
for (i = 0; i < width; i++)
|
|
selection_setpoint(xc - x + i, yc + y, ov, 1);
|
|
y--;
|
|
dyt += d2yt;
|
|
t += dyt;
|
|
} else {
|
|
for (i = 0; i < width; i++)
|
|
selection_setpoint(xc - x + i, yc - y, ov, 1);
|
|
if (y != 0)
|
|
for (i = 0; i < width; i++)
|
|
selection_setpoint(xc - x + i, yc + y, ov, 1);
|
|
x++;
|
|
dxt += d2xt;
|
|
t += dxt;
|
|
y--;
|
|
dyt += d2yt;
|
|
t += dyt;
|
|
width += 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* square of distance from line segment (x1,y1, x2,y2) to point (x3,y3) */
|
|
staticfn long
|
|
line_dist_coord(long x1, long y1, long x2, long y2, long x3, long y3)
|
|
{
|
|
long px = x2 - x1;
|
|
long py = y2 - y1;
|
|
long s = px * px + py * py;
|
|
long x, y, dx, dy, distsq = 0;
|
|
float lu = 0;
|
|
|
|
if (x1 == x2 && y1 == y2)
|
|
return dist2(x1, y1, x3, y3);
|
|
|
|
lu = ((x3 - x1) * px + (y3 - y1) * py) / (float) s;
|
|
if (lu > 1)
|
|
lu = 1;
|
|
else if (lu < 0)
|
|
lu = 0;
|
|
|
|
x = x1 + lu * px;
|
|
y = y1 + lu * py;
|
|
dx = x - x3;
|
|
dy = y - y3;
|
|
distsq = dx * dx + dy * dy;
|
|
|
|
return distsq;
|
|
}
|
|
|
|
/* guts of l_selection_gradient */
|
|
void
|
|
selection_do_gradient(
|
|
struct selectionvar *ov,
|
|
long x, long y,
|
|
long x2,long y2,
|
|
long gtyp,
|
|
long mind, long maxd)
|
|
{
|
|
long dx, dy, dofs;
|
|
|
|
if (mind > maxd) {
|
|
long tmp = mind;
|
|
mind = maxd;
|
|
maxd = tmp;
|
|
}
|
|
|
|
dofs = maxd * maxd - mind * mind;
|
|
if (dofs < 1)
|
|
dofs = 1;
|
|
|
|
switch (gtyp) {
|
|
default:
|
|
impossible("Unrecognized gradient type! Defaulting to radial...");
|
|
FALLTHROUGH;
|
|
/* FALLTHRU */
|
|
case SEL_GRADIENT_RADIAL: {
|
|
for (dx = 0; dx < COLNO; dx++)
|
|
for (dy = 0; dy < ROWNO; dy++) {
|
|
long d0 = line_dist_coord(x, y, x2, y2, dx, dy);
|
|
|
|
if (d0 <= mind * mind
|
|
|| (d0 <= maxd * maxd && d0 - mind * mind < rn2(dofs)))
|
|
selection_setpoint(dx, dy, ov, 1);
|
|
}
|
|
break;
|
|
}
|
|
case SEL_GRADIENT_SQUARE: {
|
|
for (dx = 0; dx < COLNO; dx++)
|
|
for (dy = 0; dy < ROWNO; dy++) {
|
|
long d1 = line_dist_coord(x, y, x2, y2, x, dy);
|
|
long d2 = line_dist_coord(x, y, x2, y2, dx, y);
|
|
long d3 = line_dist_coord(x, y, x2, y2, x2, dy);
|
|
long d4 = line_dist_coord(x, y, x2, y2, dx, y2);
|
|
long d5 = line_dist_coord(x, y, x2, y2, dx, dy);
|
|
long d0 = min(d5, min(max(d1, d2), max(d3, d4)));
|
|
|
|
if (d0 <= mind * mind
|
|
|| (d0 <= maxd * maxd && d0 - mind * mind < rn2(dofs)))
|
|
selection_setpoint(dx, dy, ov, 1);
|
|
}
|
|
break;
|
|
} /*case*/
|
|
} /*switch*/
|
|
}
|
|
|
|
/* bresenham line algo */
|
|
void
|
|
selection_do_line(
|
|
coordxy x1, coordxy y1,
|
|
coordxy x2, coordxy y2,
|
|
struct selectionvar *ov)
|
|
{
|
|
int d0, dx, dy, ai, bi, xi, yi;
|
|
|
|
if (x1 < x2) {
|
|
xi = 1;
|
|
dx = x2 - x1;
|
|
} else {
|
|
xi = -1;
|
|
dx = x1 - x2;
|
|
}
|
|
if (y1 < y2) {
|
|
yi = 1;
|
|
dy = y2 - y1;
|
|
} else {
|
|
yi = -1;
|
|
dy = y1 - y2;
|
|
}
|
|
|
|
selection_setpoint(x1, y1, ov, 1);
|
|
|
|
if (!dx && !dy) {
|
|
/* single point - already all done */
|
|
;
|
|
} else if (dx > dy) {
|
|
ai = (dy - dx) * 2;
|
|
bi = dy * 2;
|
|
d0 = bi - dx;
|
|
do {
|
|
if (d0 >= 0) {
|
|
y1 += yi;
|
|
d0 += ai;
|
|
} else
|
|
d0 += bi;
|
|
x1 += xi;
|
|
selection_setpoint(x1, y1, ov, 1);
|
|
} while (x1 != x2);
|
|
} else {
|
|
ai = (dx - dy) * 2;
|
|
bi = dx * 2;
|
|
d0 = bi - dy;
|
|
do {
|
|
if (d0 >= 0) {
|
|
x1 += xi;
|
|
d0 += ai;
|
|
} else
|
|
d0 += bi;
|
|
y1 += yi;
|
|
selection_setpoint(x1, y1, ov, 1);
|
|
} while (y1 != y2);
|
|
}
|
|
}
|
|
|
|
void
|
|
selection_do_randline(
|
|
coordxy x1, coordxy y1,
|
|
coordxy x2, coordxy y2,
|
|
schar rough,
|
|
schar rec,
|
|
struct selectionvar *ov)
|
|
{
|
|
int mx, my;
|
|
int dx, dy;
|
|
|
|
if (rec < 1 || (x2 == x1 && y2 == y1))
|
|
return;
|
|
|
|
if (rough > max(abs(x2 - x1), abs(y2 - y1)))
|
|
rough = max(abs(x2 - x1), abs(y2 - y1));
|
|
|
|
if (rough < 2) {
|
|
mx = ((x1 + x2) / 2);
|
|
my = ((y1 + y2) / 2);
|
|
} else {
|
|
do {
|
|
dx = rn2(rough) - (rough / 2);
|
|
dy = rn2(rough) - (rough / 2);
|
|
mx = ((x1 + x2) / 2) + dx;
|
|
my = ((y1 + y2) / 2) + dy;
|
|
} while ((mx > COLNO - 1 || mx < 0 || my < 0 || my > ROWNO - 1));
|
|
}
|
|
|
|
if (!selection_getpoint(mx, my, ov)) {
|
|
selection_setpoint(mx, my, ov, 1);
|
|
}
|
|
|
|
rough = (rough * 2) / 3;
|
|
|
|
rec--;
|
|
|
|
selection_do_randline(x1, y1, mx, my, rough, rec, ov);
|
|
selection_do_randline(mx, my, x2, y2, rough, rec, ov);
|
|
|
|
selection_setpoint(x2, y2, ov, 1);
|
|
}
|
|
|
|
void
|
|
selection_iterate(
|
|
struct selectionvar *ov,
|
|
select_iter_func func,
|
|
genericptr_t arg)
|
|
{
|
|
coordxy x, y;
|
|
NhRect rect = cg.zeroNhRect;
|
|
|
|
if (!ov)
|
|
return;
|
|
|
|
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);
|
|
}
|
|
|
|
/* selection is not rectangular, or has holes in it */
|
|
boolean
|
|
selection_is_irregular(struct selectionvar *sel)
|
|
{
|
|
coordxy x, y;
|
|
NhRect rect = cg.zeroNhRect;
|
|
|
|
selection_getbounds(sel, &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, sel))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* return a description of the selection size */
|
|
char *
|
|
selection_size_description(struct selectionvar *sel, char *buf)
|
|
{
|
|
NhRect rect = cg.zeroNhRect;
|
|
coordxy dx, dy;
|
|
|
|
selection_getbounds(sel, &rect);
|
|
dx = rect.hx - rect.lx + 1;
|
|
dy = rect.hy - rect.ly + 1;
|
|
Sprintf(buf, "%s %i by %i",
|
|
selection_is_irregular(sel) ? "irregularly shaped"
|
|
: (dx == dy) ? "square"
|
|
: "rectangular",
|
|
dx, dy);
|
|
return buf;
|
|
}
|
|
|
|
struct selectionvar *
|
|
selection_from_mkroom(struct mkroom *croom)
|
|
{
|
|
struct selectionvar *sel = selection_new();
|
|
coordxy x, y;
|
|
unsigned rmno;
|
|
|
|
if (!croom && gc.coder && gc.coder->croom)
|
|
croom = gc.coder->croom;
|
|
if (!croom)
|
|
return sel;
|
|
|
|
rmno = (unsigned)((croom - svr.rooms) + ROOMOFFSET);
|
|
for (y = croom->ly; y <= croom->hy; y++)
|
|
for (x = croom->lx; x <= croom->hx; x++)
|
|
if (isok(x, y) && !levl[x][y].edge
|
|
&& levl[x][y].roomno == rmno)
|
|
selection_setpoint(x, y, sel, 1);
|
|
return sel;
|
|
}
|
|
|
|
void
|
|
selection_force_newsyms(struct selectionvar *sel)
|
|
{
|
|
coordxy x, y;
|
|
|
|
for (x = 1; x < sel->wid; x++)
|
|
for (y = 0; y < sel->hei; y++)
|
|
if (selection_getpoint(x, y, sel))
|
|
newsym_force(x, y);
|
|
}
|
|
|
|
/*selvar.c*/
|