ref: 2183f9eaeca9fb5d57b637c15707e663caf33575
dir: /appl/math/polyfill.b/
implement Polyfill;
include "sys.m";
sys: Sys;
include "draw.m";
draw: Draw;
Point, Rect, Image, Endsquare: import draw;
include "math/polyfill.m";
∞: con 16r7fffffff;
init()
{
sys = load Sys Sys->PATH;
draw = load Draw Draw->PATH;
}
initzbuf(r: Rect): ref Zstate
{
if(sys == nil)
init();
s := ref Zstate;
s.r = r;
s.xlen = r.dx();
s.ylen = r.dy();
s.xylen = s.xlen*s.ylen;
s.zbuf0 = array[s.xylen] of int;
s.zbuf1 = array[s.xylen] of int;
return s;
}
clearzbuf(s: ref Zstate)
{
b0 := s.zbuf0;
b1 := s.zbuf1;
n := s.xylen;
for(i := 0; i < n; i++)
b0[i] = b1[i] = ∞;
}
setzbuf(s: ref Zstate, zd: int)
{
b0 := s.zbuf0;
b1 := s.zbuf1;
n := s.xylen;
for(i := 0; i < n; i++)
b0[i] = b1[i] = zd;
}
Seg: adt
{
p0: Point;
p1: Point;
num: int;
den: int;
dz: int;
dzrem: int;
z: int;
zerr: int;
d: int;
};
fillline(dst: ref Image, left: int, right: int, y: int, src: ref Image, p: Point)
{
p.x += left;
p.y += y;
dst.line((left, y), (right, y), Endsquare, Endsquare, 0, src, p);
}
filllinez(dst: ref Image, left: int, right: int, y: int, z: int, e: int, dx: int, k: int, zbuf0: array of int, zbuf1: array of int, src: ref Image, p: Point)
{
prevx := ∞;
for(x := left; x <= right; x++){
if(z+e < zbuf0[k] || (z-e <= zbuf1[k] && x != right && prevx != ∞)){
zbuf0[k] = z-e;
zbuf1[k] = z+e;
if(prevx == ∞)
prevx = x;
}
else if(prevx != ∞){
fillline(dst, prevx, x-1, y, src, p);
prevx = ∞;
}
z += dx;
k++;
}
if(prevx != ∞)
fillline(dst, prevx, right, y, src, p);
}
fillpoly(dst: ref Image, vert: array of Point, w: int, src: ref Image, sp: Point, zstate: ref Zstate, dc: int, dx: int, dy: int)
{
p0: Point;
i: int;
nvert := len vert;
if(nvert == 0)
return;
fixshift := 0;
seg := array[nvert+2] of ref Seg;
if(seg == nil)
return;
segtab := array[nvert+1] of ref Seg;
if(segtab == nil)
return;
sp.x = (sp.x - vert[0].x) >> fixshift;
sp.y = (sp.y - vert[0].y) >> fixshift;
p0 = vert[nvert-1];
if(!fixshift) {
p0.x <<= 1;
p0.y <<= 1;
}
for(i = 0; i < nvert; i++) {
segtab[i] = ref Seg;
segtab[i].p0 = p0;
p0 = vert[i];
if(!fixshift) {
p0.x <<= 1;
p0.y <<= 1;
}
segtab[i].p1 = p0;
segtab[i].d = 1;
}
if(!fixshift)
fixshift = 1;
xscan(dst, seg, segtab, nvert, w, src, sp, zstate, dc, dx, dy, fixshift);
}
mod(x: int, y: int): int
{
z: int;
z = x%y;
if((z^y) > 0 || z == 0)
return z;
return z + y;
}
sdiv(x: int, y: int): int
{
if((x^y) >= 0 || x == 0)
return x/y;
return (x+((y>>30)|1))/y-1;
}
smuldivmod(x: int, y: int, z: int): (int, int)
{
mod: int;
vx: int;
if(x == 0 || y == 0)
return (0, 0);
vx = x;
vx *= y;
mod = vx % z;
if(mod < 0)
mod += z;
if((vx < 0) == (z < 0))
return (vx/z, mod);
return (-((-vx)/z), mod);
}
xscan(dst: ref Image, seg: array of ref Seg, segtab: array of ref Seg, nseg: int, wind: int, src: ref Image, spt: Point, zstate: ref Zstate, dc: int, dx: int, dy: int, fixshift: int)
{
y, maxy, x, x2, onehalf: int;
ep, next, p, q, s: int;
n, i, iy, cnt, ix, ix2, minx, maxx, zinc, k, zv: int;
pt: Point;
sp: ref Seg;
er := (abs(dx)+abs(dy)+1)/2;
zr := zstate.r;
xlen := zstate.xlen;
zbuf0 := zstate.zbuf0;
zbuf1 := zstate.zbuf1;
s = 0;
p = 0;
for(i=0; i<nseg; i++) {
sp = seg[p] = segtab[s];
if(sp.p0.y == sp.p1.y){
s++;
continue;
}
if(sp.p0.y > sp.p1.y) {
pt = sp.p0;
sp.p0 = sp.p1;
sp.p1 = pt;
sp.d = -sp.d;
}
sp.num = sp.p1.x - sp.p0.x;
sp.den = sp.p1.y - sp.p0.y;
sp.dz = sdiv(sp.num, sp.den) << fixshift;
sp.dzrem = mod(sp.num, sp.den) << fixshift;
sp.dz += sdiv(sp.dzrem, sp.den);
sp.dzrem = mod(sp.dzrem, sp.den);
p++;
s++;
}
n = p;
if(n == 0)
return;
seg[p] = nil;
qsortycompare(seg, p);
onehalf = 0;
if(fixshift)
onehalf = 1 << (fixshift-1);
minx = dst.clipr.min.x;
maxx = dst.clipr.max.x;
y = seg[0].p0.y;
if(y < (dst.clipr.min.y << fixshift))
y = dst.clipr.min.y << fixshift;
iy = (y + onehalf) >> fixshift;
y = (iy << fixshift) + onehalf;
maxy = dst.clipr.max.y << fixshift;
k = (iy-zr.min.y)*xlen;
zv = dc+iy*dy;
ep = next = 0;
while(y<maxy) {
for(q = p = 0; p < ep; p++) {
sp = seg[p];
if(sp.p1.y < y)
continue;
sp.z += sp.dz;
sp.zerr += sp.dzrem;
if(sp.zerr >= sp.den) {
sp.z++;
sp.zerr -= sp.den;
if(sp.zerr < 0 || sp.zerr >= sp.den)
sys->print("bad ratzerr1: %d den %d dzrem %d\n", sp.zerr, sp.den, sp.dzrem);
}
seg[q] = sp;
q++;
}
for(p = next; seg[p] != nil; p++) {
sp = seg[p];
if(sp.p0.y >= y)
break;
if(sp.p1.y < y)
continue;
sp.z = sp.p0.x;
(zinc, sp.zerr) = smuldivmod(y - sp.p0.y, sp.num, sp.den);
sp.z += zinc;
if(sp.zerr < 0 || sp.zerr >= sp.den)
sys->print("bad ratzerr2: %d den %d ratdzrem %d\n", sp.zerr, sp.den, sp.dzrem);
seg[q] = sp;
q++;
}
ep = q;
next = p;
if(ep == 0) {
if(seg[next] == nil)
break;
iy = (seg[next].p0.y + onehalf) >> fixshift;
y = (iy << fixshift) + onehalf;
k = (iy-zr.min.y)*xlen;
zv = dc+iy*dy;
continue;
}
zsort(seg, ep);
for(p = 0; p < ep; p++) {
sp = seg[p];
cnt = 0;
x = sp.z;
ix = (x + onehalf) >> fixshift;
if(ix >= maxx)
break;
if(ix < minx)
ix = minx;
cnt += sp.d;
p++;
sp = seg[p];
for(;;) {
if(p == ep) {
sys->print("xscan: fill to infinity");
return;
}
cnt += sp.d;
if((cnt&wind) == 0)
break;
p++;
sp = seg[p];
}
x2 = sp.z;
ix2 = (x2 + onehalf) >> fixshift;
if(ix2 <= minx)
continue;
if(ix2 > maxx)
ix2 = maxx;
filllinez(dst, ix, ix2, iy, zv+ix*dx, er, dx, k+ix-zr.min.x, zbuf0, zbuf1, src, spt);
}
y += (1<<fixshift);
iy++;
k += xlen;
zv += dy;
}
}
zsort(seg: array of ref Seg, ep: int)
{
done: int;
s: ref Seg;
q, p: int;
if(ep < 20) {
# bubble sort by z - they should be almost sorted already
q = ep;
do {
done = 1;
q--;
for(p = 0; p < q; p++) {
if(seg[p].z > seg[p+1].z) {
s = seg[p];
seg[p] = seg[p+1];
seg[p+1] = s;
done = 0;
}
}
} while(!done);
} else {
q = ep-1;
for(p = 0; p < q; p++) {
if(seg[p].z > seg[p+1].z) {
qsortzcompare(seg, ep);
break;
}
}
}
}
ycompare(s0: ref Seg, s1: ref Seg): int
{
y0, y1: int;
y0 = s0.p0.y;
y1 = s1.p0.y;
if(y0 < y1)
return -1;
if(y0 == y1)
return 0;
return 1;
}
zcompare(s0: ref Seg, s1: ref Seg): int
{
z0, z1: int;
z0 = s0.z;
z1 = s1.z;
if(z0 < z1)
return -1;
if(z0 == z1)
return 0;
return 1;
}
qsortycompare(a : array of ref Seg, n : int)
{
i, j : int;
t : ref Seg;
while(n > 1) {
i = n>>1;
t = a[0]; a[0] = a[i]; a[i] = t;
i = 0;
j = n;
for(;;) {
do
i++;
while(i < n && ycompare(a[i], a[0]) < 0);
do
j--;
while(j > 0 && ycompare(a[j], a[0]) > 0);
if(j < i)
break;
t = a[i]; a[i] = a[j]; a[j] = t;
}
t = a[0]; a[0] = a[j]; a[j] = t;
n = n-j-1;
if(j >= n) {
qsortycompare(a, j);
a = a[j+1:];
} else {
qsortycompare(a[j+1:], n);
n = j;
}
}
}
qsortzcompare(a : array of ref Seg, n : int)
{
i, j : int;
t : ref Seg;
while(n > 1) {
i = n>>1;
t = a[0]; a[0] = a[i]; a[i] = t;
i = 0;
j = n;
for(;;) {
do
i++;
while(i < n && zcompare(a[i], a[0]) < 0);
do
j--;
while(j > 0 && zcompare(a[j], a[0]) > 0);
if(j < i)
break;
t = a[i]; a[i] = a[j]; a[j] = t;
}
t = a[0]; a[0] = a[j]; a[j] = t;
n = n-j-1;
if(j >= n) {
qsortzcompare(a, j);
a = a[j+1:];
} else {
qsortzcompare(a[j+1:], n);
n = j;
}
}
}
abs(n: int): int
{
if(n < 0)
return -n;
return n;
}