diff options
Diffstat (limited to 'appl/math/polyfill.b')
| -rw-r--r-- | appl/math/polyfill.b | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/appl/math/polyfill.b b/appl/math/polyfill.b new file mode 100644 index 00000000..c799d3db --- /dev/null +++ b/appl/math/polyfill.b @@ -0,0 +1,440 @@ +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; +} |
