1 /* NetHack 3.7 selvar.c $NHDT-Date: 1709677544 2024/03/05 22:25:44 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.360 $ */
2 /* Copyright (c) 2024 by Pasi Kallinen */
3 /* NetHack may be freely redistributed. See license for details. */
9 staticfn boolean
sel_flood_havepoint(coordxy
, coordxy
, coordxy
*, coordxy
*,
11 staticfn
long line_dist_coord(long, long, long, long, long, long);
17 struct selectionvar
*tmps
= (struct selectionvar
*) alloc(sizeof *tmps
);
21 tmps
->bounds_dirty
= FALSE
;
22 tmps
->bounds
.lx
= COLNO
;
23 tmps
->bounds
.ly
= ROWNO
;
24 tmps
->bounds
.hx
= tmps
->bounds
.hy
= 0;
25 tmps
->map
= (char *) alloc((COLNO
* ROWNO
) + 1);
26 (void) memset(tmps
->map
, 1, (COLNO
* ROWNO
));
27 tmps
->map
[(COLNO
* ROWNO
)] = '\0';
33 selection_free(struct selectionvar
*sel
, boolean freesel
)
40 free((genericptr_t
) sel
);
42 (void) memset((genericptr_t
) sel
, 0, sizeof *sel
);
46 /* clear selection, setting all locations to value val */
48 selection_clear(struct selectionvar
*sel
, int val
)
50 (void) memset(sel
->map
, 1 + val
, (COLNO
* ROWNO
));
54 sel
->bounds
.hx
= COLNO
- 1;
55 sel
->bounds
.hy
= ROWNO
- 1;
57 sel
->bounds
.lx
= COLNO
;
58 sel
->bounds
.ly
= ROWNO
;
59 sel
->bounds
.hx
= sel
->bounds
.hy
= 0;
61 sel
->bounds_dirty
= FALSE
;
65 selection_clone(struct selectionvar
*sel
)
67 struct selectionvar
*tmps
= (struct selectionvar
*) alloc(sizeof *tmps
);
70 tmps
->map
= dupstr(sel
->map
);
75 /* get boundary rect of selection sel into b */
77 selection_getbounds(struct selectionvar
*sel
, NhRect
*b
)
82 selection_recalc_bounds(sel
);
84 if (sel
->bounds
.lx
>= sel
->wid
) {
90 b
->lx
= sel
->bounds
.lx
;
91 b
->ly
= sel
->bounds
.ly
;
92 b
->hx
= sel
->bounds
.hx
;
93 b
->hy
= sel
->bounds
.hy
;
97 /* recalc the boundary of selection, if necessary */
99 selection_recalc_bounds(struct selectionvar
*sel
)
104 if (!sel
->bounds_dirty
)
107 sel
->bounds
.lx
= COLNO
;
108 sel
->bounds
.ly
= ROWNO
;
109 sel
->bounds
.hx
= sel
->bounds
.hy
= 0;
111 r
.lx
= r
.ly
= r
.hx
= r
.hy
= -1;
114 for (x
= 0; x
< sel
->wid
; x
++) {
115 for (y
= 0; y
< sel
->hei
; y
++) {
116 if (selection_getpoint(x
, y
, sel
)) {
127 for (x
= sel
->wid
-1; x
>= r
.lx
; x
--) {
128 for (y
= 0; y
< sel
->hei
; y
++) {
129 if (selection_getpoint(x
, y
, sel
)) {
139 for (y
= 0; y
< sel
->hei
; y
++) {
140 for (x
= r
.lx
; x
<= r
.hx
; x
++) {
141 if (selection_getpoint(x
, y
, sel
)) {
151 for (y
= sel
->hei
-1; y
>= r
.ly
; y
--) {
152 for (x
= r
.lx
; x
<= r
.hx
; x
++) {
153 if (selection_getpoint(x
, y
, sel
)) {
164 sel
->bounds_dirty
= FALSE
;
169 coordxy x
, coordxy y
,
170 struct selectionvar
*sel
)
172 if (!sel
|| !sel
->map
)
174 if (x
< 0 || y
< 0 || x
>= sel
->wid
|| y
>= sel
->hei
)
177 return (sel
->map
[sel
->wid
* y
+ x
] - 1);
182 coordxy x
, coordxy y
,
183 struct selectionvar
*sel
,
186 if (!sel
|| !sel
->map
)
188 if (x
< 0 || y
< 0 || x
>= sel
->wid
|| y
>= sel
->hei
)
191 if (c
&& !sel
->bounds_dirty
) {
192 if (sel
->bounds
.lx
> x
) sel
->bounds
.lx
= x
;
193 if (sel
->bounds
.ly
> y
) sel
->bounds
.ly
= y
;
194 if (sel
->bounds
.hx
< x
) sel
->bounds
.hx
= x
;
195 if (sel
->bounds
.hy
< y
) sel
->bounds
.hy
= y
;
197 sel
->bounds_dirty
= TRUE
;
200 sel
->map
[sel
->wid
* y
+ x
] = (char) (c
+ 1);
203 struct selectionvar
*
204 selection_not(struct selectionvar
*s
)
207 NhRect tmprect
= cg
.zeroNhRect
;
209 for (x
= 0; x
< s
->wid
; x
++)
210 for (y
= 0; y
< s
->hei
; y
++)
211 selection_setpoint(x
, y
, s
, selection_getpoint(x
, y
, s
) ? 0 : 1);
212 selection_getbounds(s
, &tmprect
);
216 struct selectionvar
*
217 selection_filter_percent(
218 struct selectionvar
*ov
,
222 struct selectionvar
*ret
;
223 NhRect rect
= cg
.zeroNhRect
;
228 ret
= selection_new();
230 selection_getbounds(ov
, &rect
);
232 for (x
= rect
.lx
; x
<= rect
.hx
; x
++)
233 for (y
= rect
.ly
; y
<= rect
.hy
; y
++)
234 if (selection_getpoint(x
, y
, ov
) && (rn2(100) < percent
))
235 selection_setpoint(x
, y
, ret
, 1);
240 struct selectionvar
*
241 selection_filter_mapchar(struct selectionvar
*ov
, xint16 typ
, int lit
)
244 struct selectionvar
*ret
;
245 NhRect rect
= cg
.zeroNhRect
;
250 ret
= selection_new();
252 selection_getbounds(ov
, &rect
);
254 for (x
= rect
.lx
; x
<= rect
.hx
; x
++)
255 for (y
= rect
.ly
; y
<= rect
.hy
; y
++)
256 if (selection_getpoint(x
, y
, ov
)
257 && match_maptyps(typ
, levl
[x
][y
].typ
)) {
261 selection_setpoint(x
, y
, ret
, 1);
264 selection_setpoint(x
, y
, ret
, rn2(2));
268 if (levl
[x
][y
].lit
== (unsigned int) lit
)
269 selection_setpoint(x
, y
, ret
, 1);
278 struct selectionvar
*ov
,
279 coordxy
*x
, coordxy
*y
,
285 NhRect rect
= cg
.zeroNhRect
;
287 selection_getbounds(ov
, &rect
);
289 for (dx
= rect
.lx
; dx
<= rect
.hx
; dx
++)
290 for (dy
= rect
.ly
; dy
<= rect
.hy
; dy
++)
291 if (selection_getpoint(dx
, dy
, ov
))
296 for (dx
= rect
.lx
; dx
<= rect
.hx
; dx
++)
297 for (dy
= rect
.ly
; dy
<= rect
.hy
; dy
++)
298 if (selection_getpoint(dx
, dy
, ov
)) {
303 selection_setpoint(dx
, dy
, ov
, 0);
314 selection_do_grow(struct selectionvar
*ov
, int dir
)
317 struct selectionvar
*tmp
;
318 NhRect rect
= cg
.zeroNhRect
;
323 tmp
= selection_new();
328 selection_getbounds(ov
, &rect
);
330 for (x
= max(0, rect
.lx
-1); x
<= min(COLNO
-1, rect
.hx
+1); x
++)
331 for (y
= max(0, rect
.ly
-1); y
<= min(ROWNO
-1, rect
.hy
+1); y
++) {
332 /* note: dir is a mask of multiple directions, but the only
333 way to specify diagonals is by including the two adjacent
334 orthogonal directions, which effectively specifies three-
335 way growth [WEST|NORTH => WEST plus WEST|NORTH plus NORTH] */
336 if (((dir
& W_WEST
) && selection_getpoint(x
+ 1, y
, ov
))
337 || (((dir
& (W_WEST
| W_NORTH
)) == (W_WEST
| W_NORTH
))
338 && selection_getpoint(x
+ 1, y
+ 1, ov
))
339 || ((dir
& W_NORTH
) && selection_getpoint(x
, y
+ 1, ov
))
340 || (((dir
& (W_NORTH
| W_EAST
)) == (W_NORTH
| W_EAST
))
341 && selection_getpoint(x
- 1, y
+ 1, ov
))
342 || ((dir
& W_EAST
) && selection_getpoint(x
- 1, y
, ov
))
343 || (((dir
& (W_EAST
| W_SOUTH
)) == (W_EAST
| W_SOUTH
))
344 && selection_getpoint(x
- 1, y
- 1, ov
))
345 || ((dir
& W_SOUTH
) && selection_getpoint(x
, y
- 1, ov
))
346 || (((dir
& (W_SOUTH
| W_WEST
)) == (W_SOUTH
| W_WEST
))
347 && selection_getpoint(x
+ 1, y
- 1, ov
))) {
348 selection_setpoint(x
, y
, tmp
, 1);
352 selection_getbounds(tmp
, &rect
);
354 for (x
= rect
.lx
; x
<= rect
.hx
; x
++)
355 for (y
= rect
.ly
; y
<= rect
.hy
; y
++)
356 if (selection_getpoint(x
, y
, tmp
))
357 selection_setpoint(x
, y
, ov
, 1);
359 selection_free(tmp
, TRUE
);
362 staticfn
int (*selection_flood_check_func
)(coordxy
, coordxy
);
365 set_selection_floodfillchk(int (*f
)(coordxy
, coordxy
))
367 selection_flood_check_func
= f
;
370 /* check whethere <x,y> is already in xs[],ys[] */
373 coordxy x
, coordxy y
,
374 coordxy xs
[], coordxy ys
[],
377 coordxy xx
= x
, yy
= y
;
381 if (xs
[n
] == xx
&& ys
[n
] == yy
)
389 struct selectionvar
*ov
,
390 coordxy x
, coordxy y
,
393 struct selectionvar
*tmp
= selection_new();
394 #define SEL_FLOOD_STACK (COLNO * ROWNO)
395 #define SEL_FLOOD(nx, ny) \
397 if (idx < SEL_FLOOD_STACK) { \
402 panic(floodfill_stack_overrun); \
404 #define SEL_FLOOD_CHKDIR(mx, my, sel) \
406 if (isok((mx), (my)) \
407 && (*selection_flood_check_func)((mx), (my)) \
408 && !selection_getpoint((mx), (my), (sel)) \
409 && !sel_flood_havepoint((mx), (my), dx, dy, idx)) \
410 SEL_FLOOD((mx), (my)); \
412 static const char floodfill_stack_overrun
[] = "floodfill stack overrun";
414 coordxy dx
[SEL_FLOOD_STACK
];
415 coordxy dy
[SEL_FLOOD_STACK
];
417 if (selection_flood_check_func
== (int (*)(coordxy
, coordxy
)) 0) {
418 selection_free(tmp
, TRUE
);
427 selection_setpoint(x
, y
, ov
, 1);
428 selection_setpoint(x
, y
, tmp
, 1);
430 SEL_FLOOD_CHKDIR((x
+ 1), y
, tmp
);
431 SEL_FLOOD_CHKDIR((x
- 1), y
, tmp
);
432 SEL_FLOOD_CHKDIR(x
, (y
+ 1), tmp
);
433 SEL_FLOOD_CHKDIR(x
, (y
- 1), tmp
);
435 SEL_FLOOD_CHKDIR((x
+ 1), (y
+ 1), tmp
);
436 SEL_FLOOD_CHKDIR((x
- 1), (y
- 1), tmp
);
437 SEL_FLOOD_CHKDIR((x
- 1), (y
+ 1), tmp
);
438 SEL_FLOOD_CHKDIR((x
+ 1), (y
- 1), tmp
);
442 #undef SEL_FLOOD_STACK
443 #undef SEL_FLOOD_CHKDIR
444 selection_free(tmp
, TRUE
);
447 /* McIlroy's Ellipse Algorithm */
449 selection_do_ellipse(
450 struct selectionvar
*ov
,
454 { /* e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 */
456 long a2
= (long) a
* a
, b2
= (long) b
* b
;
457 long crit1
= -(a2
/ 4 + a
% 2 + b2
);
458 long crit2
= -(b2
/ 4 + b
% 2 + a2
);
459 long crit3
= -(b2
/ 4 + b
% 2);
460 long t
= -a2
* y
; /* e(x+1/2,y-1/2) - (a^2+b^2)/4 */
461 long dxt
= 2 * b2
* x
, dyt
= -2 * a2
* y
;
462 long d2xt
= 2 * b2
, d2yt
= 2 * a2
;
472 while (y
>= 0 && x
<= a
) {
473 selection_setpoint(xc
+ x
, yc
+ y
, ov
, 1);
474 if (x
!= 0 || y
!= 0)
475 selection_setpoint(xc
- x
, yc
- y
, ov
, 1);
476 if (x
!= 0 && y
!= 0) {
477 selection_setpoint(xc
+ x
, yc
- y
, ov
, 1);
478 selection_setpoint(xc
- x
, yc
+ y
, ov
, 1);
480 if (t
+ b2
* x
<= crit1
/* e(x+1,y-1/2) <= 0 */
481 || t
+ a2
* y
<= crit3
) { /* e(x+1/2,y) <= 0 */
485 } else if (t
- a2
* y
> crit2
) { /* e(x+1/2,y-1) > 0 */
499 while (y
>= 0 && x
<= a
) {
500 if (t
+ b2
* x
<= crit1
/* e(x+1,y-1/2) <= 0 */
501 || t
+ a2
* y
<= crit3
) { /* e(x+1/2,y) <= 0 */
506 } else if (t
- a2
* y
> crit2
) { /* e(x+1/2,y-1) > 0 */
507 for (i
= 0; i
< width
; i
++)
508 selection_setpoint(xc
- x
+ i
, yc
- y
, ov
, 1);
510 for (i
= 0; i
< width
; i
++)
511 selection_setpoint(xc
- x
+ i
, yc
+ y
, ov
, 1);
516 for (i
= 0; i
< width
; i
++)
517 selection_setpoint(xc
- x
+ i
, yc
- y
, ov
, 1);
519 for (i
= 0; i
< width
; i
++)
520 selection_setpoint(xc
- x
+ i
, yc
+ y
, ov
, 1);
533 /* square of distance from line segment (x1,y1, x2,y2) to point (x3,y3) */
535 line_dist_coord(long x1
, long y1
, long x2
, long y2
, long x3
, long y3
)
539 long s
= px
* px
+ py
* py
;
540 long x
, y
, dx
, dy
, distsq
= 0;
543 if (x1
== x2
&& y1
== y2
)
544 return dist2(x1
, y1
, x3
, y3
);
546 lu
= ((x3
- x1
) * px
+ (y3
- y1
) * py
) / (float) s
;
556 distsq
= dx
* dx
+ dy
* dy
;
561 /* guts of l_selection_gradient */
563 selection_do_gradient(
564 struct selectionvar
*ov
,
568 long mind
, long maxd
)
578 dofs
= maxd
* maxd
- mind
* mind
;
584 impossible("Unrecognized gradient type! Defaulting to radial...");
587 case SEL_GRADIENT_RADIAL
: {
588 for (dx
= 0; dx
< COLNO
; dx
++)
589 for (dy
= 0; dy
< ROWNO
; dy
++) {
590 long d0
= line_dist_coord(x
, y
, x2
, y2
, dx
, dy
);
592 if (d0
<= mind
* mind
593 || (d0
<= maxd
* maxd
&& d0
- mind
* mind
< rn2(dofs
)))
594 selection_setpoint(dx
, dy
, ov
, 1);
598 case SEL_GRADIENT_SQUARE
: {
599 for (dx
= 0; dx
< COLNO
; dx
++)
600 for (dy
= 0; dy
< ROWNO
; dy
++) {
601 long d1
= line_dist_coord(x
, y
, x2
, y2
, x
, dy
);
602 long d2
= line_dist_coord(x
, y
, x2
, y2
, dx
, y
);
603 long d3
= line_dist_coord(x
, y
, x2
, y2
, x2
, dy
);
604 long d4
= line_dist_coord(x
, y
, x2
, y2
, dx
, y2
);
605 long d5
= line_dist_coord(x
, y
, x2
, y2
, dx
, dy
);
606 long d0
= min(d5
, min(max(d1
, d2
), max(d3
, d4
)));
608 if (d0
<= mind
* mind
609 || (d0
<= maxd
* maxd
&& d0
- mind
* mind
< rn2(dofs
)))
610 selection_setpoint(dx
, dy
, ov
, 1);
617 /* bresenham line algo */
620 coordxy x1
, coordxy y1
,
621 coordxy x2
, coordxy y2
,
622 struct selectionvar
*ov
)
624 int d0
, dx
, dy
, ai
, bi
, xi
, yi
;
641 selection_setpoint(x1
, y1
, ov
, 1);
644 /* single point - already all done */
646 } else if (dx
> dy
) {
657 selection_setpoint(x1
, y1
, ov
, 1);
670 selection_setpoint(x1
, y1
, ov
, 1);
676 selection_do_randline(
677 coordxy x1
, coordxy y1
,
678 coordxy x2
, coordxy y2
,
681 struct selectionvar
*ov
)
686 if (rec
< 1 || (x2
== x1
&& y2
== y1
))
689 if (rough
> max(abs(x2
- x1
), abs(y2
- y1
)))
690 rough
= max(abs(x2
- x1
), abs(y2
- y1
));
693 mx
= ((x1
+ x2
) / 2);
694 my
= ((y1
+ y2
) / 2);
697 dx
= rn2(rough
) - (rough
/ 2);
698 dy
= rn2(rough
) - (rough
/ 2);
699 mx
= ((x1
+ x2
) / 2) + dx
;
700 my
= ((y1
+ y2
) / 2) + dy
;
701 } while ((mx
> COLNO
- 1 || mx
< 0 || my
< 0 || my
> ROWNO
- 1));
704 if (!selection_getpoint(mx
, my
, ov
)) {
705 selection_setpoint(mx
, my
, ov
, 1);
708 rough
= (rough
* 2) / 3;
712 selection_do_randline(x1
, y1
, mx
, my
, rough
, rec
, ov
);
713 selection_do_randline(mx
, my
, x2
, y2
, rough
, rec
, ov
);
715 selection_setpoint(x2
, y2
, ov
, 1);
720 struct selectionvar
*ov
,
721 select_iter_func func
,
725 NhRect rect
= cg
.zeroNhRect
;
730 selection_getbounds(ov
, &rect
);
732 for (x
= rect
.lx
; x
<= rect
.hx
; x
++)
733 for (y
= rect
.ly
; y
<= rect
.hy
; y
++)
734 if (isok(x
,y
) && selection_getpoint(x
, y
, ov
))
738 /* selection is not rectangular, or has holes in it */
740 selection_is_irregular(struct selectionvar
*sel
)
743 NhRect rect
= cg
.zeroNhRect
;
745 selection_getbounds(sel
, &rect
);
747 for (x
= rect
.lx
; x
<= rect
.hx
; x
++)
748 for (y
= rect
.ly
; y
<= rect
.hy
; y
++)
749 if (isok(x
,y
) && !selection_getpoint(x
, y
, sel
))
755 /* return a description of the selection size */
757 selection_size_description(struct selectionvar
*sel
, char *buf
)
759 NhRect rect
= cg
.zeroNhRect
;
762 selection_getbounds(sel
, &rect
);
763 dx
= rect
.hx
- rect
.lx
+ 1;
764 dy
= rect
.hy
- rect
.ly
+ 1;
765 Sprintf(buf
, "%s %i by %i",
766 selection_is_irregular(sel
) ? "irregularly shaped"
767 : (dx
== dy
) ? "square"
773 struct selectionvar
*
774 selection_from_mkroom(struct mkroom
*croom
)
776 struct selectionvar
*sel
= selection_new();
780 if (!croom
&& gc
.coder
&& gc
.coder
->croom
)
781 croom
= gc
.coder
->croom
;
785 rmno
= (unsigned)((croom
- svr
.rooms
) + ROOMOFFSET
);
786 for (y
= croom
->ly
; y
<= croom
->hy
; y
++)
787 for (x
= croom
->lx
; x
<= croom
->hx
; x
++)
788 if (isok(x
, y
) && !levl
[x
][y
].edge
789 && levl
[x
][y
].roomno
== rmno
)
790 selection_setpoint(x
, y
, sel
, 1);
795 selection_force_newsyms(struct selectionvar
*sel
)
799 for (x
= 1; x
< sel
->wid
; x
++)
800 for (y
= 0; y
< sel
->hei
; y
++)
801 if (selection_getpoint(x
, y
, sel
))