ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/wm/collide.b/
#
# initially generated by c2l
#
implement Collide;
include "draw.m";
draw: Draw;
Display, Image: import draw;
Collide: module
{
init: fn(nil: ref Draw->Context, argl: list of string);
};
include "sys.m";
sys: Sys;
include "tk.m";
tk: Tk;
Toplevel: import tk;
include "tkclient.m";
tkclient: Tkclient;
include "math.m";
maths: Math;
include "rand.m";
rand: Rand;
include "daytime.m";
daytime: Daytime;
include "bufio.m";
include "arg.m";
arg: Arg;
include "math/polyhedra.m";
polyhedra: Polyhedra;
init(ctxt: ref Draw->Context, argl: list of string)
{
sys = load Sys Sys->PATH;
draw = load Draw Draw->PATH;
tk = load Tk Tk->PATH;
tkclient = load Tkclient Tkclient->PATH;
maths = load Math Math->PATH;
rand = load Rand Rand->PATH;
arg = load Arg Arg->PATH;
daytime = load Daytime Daytime->PATH;
main(ctxt, argl);
}
π: con Math->Pi;
∞: con real (1<<30);
ε: con 0.001;
√2: con 1.4142135623730950488016887242096980785696718753769486732;
M1: con 1.0;
M2: con 1.0;
E: con 1.0; # coefficient of restitution/elasticity
COLLIDE, REFLECT: con 1<<iota;
MAXX, MAXY: con 512;
RDisp: ref Draw->Image;
black, white, red: ref Draw->Image;
display: ref Draw->Display;
toplev: ref Toplevel;
Vector: adt{
x: real;
y: real;
z: real;
};
Line: adt{
a: Vector;
d: Vector; # normalized
};
Plane: adt{
id: int;
n: Vector; # normalized
d: real;
min: Vector;
max: Vector;
far: Vector;
v: array of Vector;
};
Object: adt{
id: int;
poly: ref Polyhedra->Polyhedron; # if any
c: ref Draw->Image; # colour
cb: ref Draw->Image; # border colour
l: ref Line; # initial point and direction
p: Vector; # current position
rp: Vector; # position after reflection
cp: Vector; # any collision point
rt: real; # time to reflection
ct: real; # time to collision
plane: ref Plane; # reflecting off
pmask: int; # plane mask
obj: cyclic ref Object; # colliding with
v: real; # speed
ω: real; # speed of rotation
roll: real; # roll
pitch: real; # pitch
yaw: real; # yaw
todo: int; # work to do
};
planes: list of ref Plane;
V0: con Vector(real 0, real 0, real 0);
VZ: con Vector(0.0, 0.0, 1.0);
far: Vector;
DOCIRCLE: con 1;
POLY, FILLPOLY, CIRCLE, FILLCIRCLE, ELLIPSE, FILLELLIPSE: con iota;
#
# final object is centred on (0, 0, -objd)
# viewer is at origin looking along (0 0 -1)
#
maxx, maxy: int;
SCRW: con 320; # screen width
SCRH: con 240; # screen height
frac := 0.5; # % of screen for cube
front := 0.5; # % of cube in front of screen
hpar := 0.0; # horizontal parallax
fov := -1.0; # field of view : 0 for parallel projection, -1 for unspecified
objd := 500.0; # eye to middle of cube
cubd := 100.0; # half side of cube
icubd: real; # half side of inner cube
icubd2: real; # square of above
eyed := 32.0; # half eye to eye
trkd := 5.0; # side/diameter of object
trkd2: real; # square of above
rpy := 0;
roll := 0.0; # z
pitch := 0.0; # y
yaw := 0.0; # x
scrd, objD, scrD: real; # screen distance
left := 0; # left or right eye
sx, sy, sz: real; # screen scale factors
sf: real; # perspective scale factor
fbpar: real; # -1 for front of cube, 1 for back
vf := 1.0; # current velocity factor
cmin, cmax: Vector; # cube extents
# special transformation matrix without roll, pitch, yaw
# this is needed so that spheres can be drawn as circles
mod0 := array[4] of array of real;
stereo := 0; # stereopsis
SPHERE, ELLIPSOID, CUBE, POLYHEDRON: con iota;
surr := CUBE; # surround
poly := 0; # show polyhedra
flat: int; # objects in one plane
projx: int; # strange projection
# ellipse parameters
ef: Vector = (1.0, 0.8, 1.0);
e2: Vector;
# objects
nobjs: int;
objs: array of ref Object;
me: ref Object;
# circle drawing
NC: con 72;
cost, sint: array of real;
# polyhedra
polys: ref Polyhedra->Polyhedron;
npolys: int;
polyh: ref Polyhedra->Polyhedron;
rgba(r: int, g: int, b: int, α: int): ref Image
{
c := draw->setalpha((r<<24)|(g<<16)|(b<<8), α);
return display.newimage(((0, 0), (1, 1)), display.image.chans, 1, c);
}
random(a: int, b: int): int
{
return a+rand->rand(b-a+1);
}
urand(): real
{
M: con 1000;
return real random(0, M)/real M;
}
randomr(a: real, b: real): real
{
return a+urand()*(b-a);
}
randomc(): ref Image
{
r, g, b: int;
do{
r = random(0, 255);
g = random(0, 255);
b = random(0, 255);
}while(r+g+b < 384);
return rgba(r, g, b, 255);
}
randomv(a: real, b: real): Vector
{
x := randomr(a, b);
y := randomr(a, b);
if(flat)
return (x, y, (a+b)/2.0);
return (x, y, randomr(a, b));
}
randomd(): Vector
{
M: con 1000.0;
v := randomv(-M, M);
while(vlen(v) == 0.0)
v = randomv(-M, M);
return vnorm(v);
}
randomp(min: real, max: real): Vector
{
case(surr){
SPHERE =>
return vmul(randomd(), max*maths->sqrt(urand()));
ELLIPSOID =>
return vmul(randomd(), max*vmin(ef)*maths->sqrt(urand()));
CUBE =>
return randomv(min, max);
* =>
v := randomv(min, max);
while(outside3(v, cmin, cmax))
v = randomv(min, max);
return v;
}
}
det(a: real, b: real, c: real, d: real): real
{
return a*d-b*c;
}
simeq(a: real, b: real, c: real, d: real, e: real, f: real): (real, real)
{
de := det(a, b, c, d);
return (det(e, b, f, d)/de, det(a, e, c, f)/de);
}
cksimeq(a: real, b: real, c: real, d: real, e: real, f: real): (int, real, real)
{
ade := de := det(a, b, c, d);
if(ade < 0.0)
ade = -ade;
if(ade < ε)
return (0, 0.0, 0.0);
return (1, det(e, b, f, d)/de, det(a, e, c, f)/de);
}
ostring(o: ref Object): string
{
return lstring(o.l) + "+" + vstring(o.p) + "+" + string o.v;
}
pstring(p: ref Plane): string
{
return vstring(p.n) + "=" + string p.d;
}
lstring(l: ref Line): string
{
return vstring(l.a) + "->" + vstring(l.d);
}
vstring(v: Vector): string
{
return "(" + string v.x + " " + string v.y + " " + string v.z + ")";
}
vpt(x: real, y: real, z: real): Vector
{
p: Vector;
p.x = x;
p.y = y;
p.z = z;
return p;
}
vdot(v1: Vector, v2: Vector): real
{
return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z;
}
vcross(v1: Vector, v2: Vector): Vector
{
v: Vector;
v.x = v1.y*v2.z-v1.z*v2.y;
v.y = v1.z*v2.x-v1.x*v2.z;
v.z = v1.x*v2.y-v1.y*v2.x;
return v;
}
vadd(v1: Vector, v2: Vector): Vector
{
v: Vector;
v.x = v1.x+v2.x;
v.y = v1.y+v2.y;
v.z = v1.z+v2.z;
return v;
}
vsub(v1: Vector, v2: Vector): Vector
{
v: Vector;
v.x = v1.x-v2.x;
v.y = v1.y-v2.y;
v.z = v1.z-v2.z;
return v;
}
vmul(v1: Vector, s: real): Vector
{
v: Vector;
v.x = s*v1.x;
v.y = s*v1.y;
v.z = s*v1.z;
return v;
}
vdiv(v1: Vector, s: real): Vector
{
v: Vector;
v.x = v1.x/s;
v.y = v1.y/s;
v.z = v1.z/s;
return v;
}
vlen(v: Vector): real
{
return maths->sqrt(vdot(v, v));
}
vlen2(v: Vector): (real, real)
{
d2 := vdot(v, v);
d := maths->sqrt(d2);
return (d, d2);
}
vnorm(v: Vector): Vector
{
d := maths->sqrt(vdot(v, v));
if(d == 0.0)
return v;
return vmul(v, real 1/d);
}
vnorm2(v: Vector): (real, Vector)
{
d := maths->sqrt(vdot(v, v));
if(d == 0.0)
return (0.0, VZ);
return (d, vmul(v, real 1/d));
}
clip(x: real, d: real): real
{
if(x < -d)
x = -d;
if(x > d)
x = d;
return x;
}
vclip(v: Vector, d: real): Vector
{
c: Vector;
c.x = clip(v.x, d);
c.y = clip(v.y, d);
c.z = clip(v.z, d);
return c;
}
vout(v1: Vector, v2: Vector): int
{
v := vsub(v2, v1);
return v.x < 0.0 || v.y < 0.0 || v.z < 0.0;
}
vmin(v: Vector): real
{
m := v.x;
if(v.y < m)
m = v.y;
if(v.z < m)
m = v.z;
return m;
}
vvmul(v1: Vector, v2: Vector): Vector
{
v: Vector;
v.x = v1.x*v2.x;
v.y = v1.y*v2.y;
v.z = v1.z*v2.z;
return v;
}
vvdiv(v1: Vector, v2: Vector): Vector
{
v: Vector;
v.x = v1.x/v2.x;
v.y = v1.y/v2.y;
v.z = v1.z/v2.z;
return v;
}
vmuldiv(v1: Vector, v2: Vector, v3: Vector): real
{
return vdot(vvdiv(v1, v3), v2);
}
newp(id: int, a: real, b: real, c: real, d: real, m: real, v: array of Vector)
{
p := ref Plane;
p.id = id;
p.n = (a, b, c);
p.d = d;
m += ε;
p.min = (-m, -m, -m);
p.max = (+m, +m, +m);
p.v = v;
if(v != nil){
p.min = (∞, ∞, ∞);
p.max = (-∞, -∞, -∞);
for(i := 0; i < len v; i++){
vtx := v[i];
if(vtx.x < p.min.x)
p.min.x = vtx.x;
if(vtx.y < p.min.y)
p.min.y = vtx.y;
if(vtx.z < p.min.z)
p.min.z = vtx.z;
if(vtx.x > p.max.x)
p.max.x = vtx.x;
if(vtx.y > p.max.y)
p.max.y = vtx.y;
if(vtx.z > p.max.z)
p.max.z = vtx.z;
}
(x, y, z) := p.far = vmul(p.max, 2.0);
if(a != 0.0)
p.far.x = (d-b*y-c*z)/a;
else if(b != 0.0)
p.far.y = (d-c*z-a*x)/b;
else if(c != 0.0)
p.far.z = (d-a*x-b*y)/c;
else
fatal("null plane");
}
planes = p :: planes;
}
pinit()
{
case(surr){
SPHERE or
ELLIPSOID =>
newp(0, 0.0, 0.0, 1.0, ∞, ∞, nil);
CUBE =>
newp(0, 1.0, 0.0, 0.0, -icubd, icubd, nil);
newp(1, 1.0, 0.0, 0.0, +icubd, icubd, nil);
newp(2, 0.0, 1.0, 0.0, -icubd, icubd, nil);
newp(3, 0.0, 1.0, 0.0, +icubd, icubd, nil);
newp(4, 0.0, 0.0, 1.0, -icubd, icubd, nil);
newp(5, 0.0, 0.0, 1.0, +icubd, icubd, nil);
* =>
p := polyh;
F := p.F;
v := p.v;
f := p.f;
fv := p.fv;
d := 0.0;
for(i := 0; i < F; i++){
n := vnorm(f[i]);
dn := vmul(n, cubd-icubd);
fvi := fv[i];
m := fvi[0];
av := array[m] of Vector;
for(j := 0; j < m; j++){
av[j] = vtx := vsub(vmul(v[fvi[j+1]], 2.0*cubd), dn);
d += vdot(n, vtx);
}
d /= real m;
newp(i, n.x, n.y, n.z, d, 0.0, av);
}
}
}
inside(v: Vector, vmin: Vector, vmax: Vector): int
{
return !vout(vmin, v) && !vout(v, vmax);
}
inside2(v: Vector, p: ref Plane): int
{
h := 0;
pt := p.far;
vs := p.v;
n := len p.v;
j := n-1;
for(i := 0; i < n; i++){
(ok, λ, μ) := cksimeq(vs[j].x-vs[i].x, v.x-pt.x, vs[j].y-vs[i].y, v.y-pt.y, v.x-vs[i].x, v.y-vs[i].y);
if(!ok)
(ok, λ, μ) = cksimeq(vs[j].y-vs[i].y, v.y-pt.y, vs[j].z-vs[i].z, v.z-pt.z, v.y-vs[i].y, v.z-vs[i].z);
if(!ok)
(ok, λ, μ) = cksimeq(vs[j].z-vs[i].z, v.z-pt.z, vs[j].x-vs[i].x, v.x-pt.x, v.z-vs[i].z, v.x-vs[i].x);
if(ok && μ >= 0.0 && λ >= 0.0 && λ < 1.0)
h++;
j = i;
}
return h&1;
}
inside3(v: Vector, lp: list of ref Plane): int
{
h := 0;
l := ref Line;
l.a = v;
l.d = vnorm(vsub(far, v));
for( ; lp != nil; lp = tl lp){
(ok, nil, nil) := intersect(l, hd lp);
if(ok)
h++;
}
return h&1;
}
# outside of a face
outside2(v: Vector, p: ref Plane): int
{
if(surr == CUBE)
return vout(p.min, v) || vout(v, p.max);
else
return !inside2(v, p);
}
# outside of a polyhedron
outside3(v: Vector, vmin: Vector, vmax: Vector): int
{
case(surr){
SPHERE =>
return vout(vmin, v) || vout(v, vmax) || vdot(v, v) > icubd2 ;
ELLIPSOID =>
return vout(vmin, v) || vout(v, vmax) || vmuldiv(v, v, e2) > 1.0;
CUBE =>
return vout(vmin, v) || vout(v, vmax);
* =>
return !inside3(v, planes);
}
}
intersect(l: ref Line, p: ref Plane): (int, real, Vector)
{
m := vdot(p.n, l.d);
if(m == real 0)
return (0, real 0, V0);
c := vdot(p.n, l.a);
λ := (p.d-c)/m;
if(λ < real 0)
return (0, λ, V0);
pt := vadd(l.a, vmul(l.d, λ));
if(outside2(pt, p))
return (0, λ, pt);
return (1, λ, pt);
}
reflection(tr: ref Object, lp: list of ref Plane)
{
ok: int;
λ: real;
l := tr.l;
if(surr == SPHERE){
(ok, λ) = quadratic(1.0, 2.0*vdot(l.a, l.d), vdot(l.a, l.a)-icubd2);
if(!ok || λ < 0.0)
fatal("no sphere intersections");
tr.rp = vadd(l.a, vmul(l.d, λ));
tr.plane = hd lp; # anything
}
else if(surr == ELLIPSOID){
(ok, λ) = quadratic(vmuldiv(l.d, l.d, e2), 2.0*vmuldiv(l.a, l.d, e2), vmuldiv(l.a, l.a, e2)-1.0);
if(!ok || λ < 0.0)
fatal("no ellipsoid intersections");
tr.rp = vadd(l.a, vmul(l.d, λ));
tr.plane = hd lp; # anything
}
else{
p: ref Plane;
pt := V0;
λ = ∞;
for( ; lp != nil; lp = tl lp){
p0 := hd lp;
if((1<<p0.id)&tr.pmask)
continue;
(ok0, λ0, pt0) := intersect(l, p0);
if(ok0 && λ0 < λ){
λ = λ0;
p = p0;
pt = pt0;
}
}
if(λ == ∞)
fatal("no intersections");
tr.rp = pt;
tr.plane = p;
}
if(tr.v == 0.0)
tr.rt = ∞;
else
tr.rt = λ/tr.v;
}
reflect(tr: ref Object)
{
l := tr.l;
if(surr == SPHERE)
n := vdiv(tr.rp, -icubd);
else if(surr == ELLIPSOID)
n = vnorm(vdiv(vvdiv(tr.rp, e2), -1.0));
else
n = tr.plane.n;
tr.l.a = tr.rp;
tr.l.d = vnorm(vsub(l.d, vmul(n, 2.0*vdot(n, l.d))));
}
impact(u2: real): (real, real)
{
# u1 == 0
return simeq(M1, M2, -1.0, 1.0, M2*u2, -E*u2);
}
collision(t1: ref Object, t2: ref Object): (int, real, Vector, Vector)
{
# stop t2
(v3, f) := vnorm2(vsub(vmul(t1.l.d, t1.v), vmul(t2.l.d, t2.v)));
if(v3 == 0.0)
return (0, 0.0, V0, V0);
ab := vsub(t2.p, t1.p);
(d, d2) := vlen2(ab);
cos := vdot(f, ab)/d;
cos2 := cos*cos;
if(cos < 0.0 || (disc := trkd2 - d2*(1.0-cos2)) < 0.0)
return (0, 0.0, V0, V0);
s := d*cos - maths->sqrt(disc);
t := s/v3;
s1 := t1.v*t;
s2 := t2.v*t;
cp1 := vadd(t1.p, vmul(t1.l.d, s1));
if(outside3(cp1, cmin, cmax))
return (0, 0.0, V0, V0);
cp2 := vadd(t2.p, vmul(t2.l.d, s2));
if(outside3(cp2, cmin, cmax))
return (0, 0.0, V0, V0);
return (1, t, cp1, cp2);
}
collisions(tr: ref Object)
{
mincp1, mincp2: Vector;
n := nobjs;
t := objs;
tr0 := tr.obj;
mint := tr.ct;
tr1: ref Object;
for(i := 0; i < n; i++){
if((tr3 := t[i]) == tr || tr3 == tr0)
continue;
(c, tm, cp1, cp2) := collision(tr, tr3);
if(c && tm < mint && tm < tr3.ct){
mint = tm;
tr1 = tr3;
mincp1 = cp1;
mincp2 = cp2;
}
}
if(tr1 != nil){
tr.ct = mint;
tr1.ct = mint;
tr.obj = tr1;
tr2 := tr1.obj;
tr1.obj = tr;
tr.cp = mincp1;
tr1.cp = mincp2;
zerot(tr0, COLLIDE, 0);
zerot(tr2, COLLIDE, 0);
if(tr0 != nil && tr0 != tr2)
collisions(tr0);
if(tr2 != nil)
collisions(tr2);
}
}
collide(t1: ref Object, t2: ref Object)
{
# stop t2
ov := vmul(t2.l.d, t2.v);
(v3, f) := vnorm2(vsub(vmul(t1.l.d, t1.v), ov));
ab := vsub(t2.cp, t1.cp);
α := vdot(f, ab)/vdot(ab, ab);
abr := vsub(f, vmul(ab, α));
(v2, v1) := impact(α*v3);
t1.l.a = t1.cp;
t2.l.a = t2.cp;
dir1 := vadd(vmul(ab, v1), vmul(abr, v3));
dir2 := vmul(ab, v2);
# start t2
(t1.v, t1.l.d) = vnorm2(vadd(dir1, ov));
(t2.v, t2.l.d) = vnorm2(vadd(dir2, ov));
}
deg2rad(d: real): real
{
return π*d/180.0;
}
rad2deg(r: real): real
{
return 180.0*r/π;
}
rp2d(r: real, p: real): Vector
{
r = deg2rad(r);
cr := maths->cos(r);
sr := maths->sin(r);
p = deg2rad(p);
cp := maths->cos(p);
sp := maths->sin(p);
return (cr*cp, sr*cp, sp);
}
d2rp(v: Vector): (real, real)
{
r := maths->atan2(v.y, v.x);
p := maths->asin(v.z);
return (rad2deg(r), rad2deg(p));
}
collideω(t1: ref Object, t2: ref Object)
{
d1 := rp2d(t1.roll, t1.pitch);
d2 := rp2d(t2.roll, t2.pitch);
oω := vmul(d2, t2.ω);
(ω3, f) := vnorm2(vsub(vmul(d1, t1.ω), oω));
ab := vsub(t2.cp, t1.cp);
α := vdot(f, ab)/vdot(ab, ab);
abr := vsub(f, vmul(ab, α));
(ω2, ω1) := impact(α*ω3);
dir1 := vadd(vmul(ab, ω1), vmul(abr, ω3));
dir2 := vmul(ab, ω2);
(t1.ω, d1) = vnorm2(vadd(dir1, oω));
(t2.ω, d2) = vnorm2(vadd(dir2, oω));
(t1.roll, t1.pitch) = d2rp(d1);
(t2.roll, t2.pitch) = d2rp(d2);
}
plane(p1: Vector, p2: Vector, p3: Vector): (Vector, real)
{
n := vnorm(vcross(vsub(p2, p1), vsub(p3, p1)));
d := vdot(n, p1);
return (n, d);
}
# angle subtended by the eyes at p in minutes
angle(p: Vector): real
{
l, r: Vector;
# left eye at (-eyed, 0, 0)
# right eye at (+eyed, 0, 0)
#
l = p;
l.x += eyed;
r = p;
r.x -= eyed;
return real 60*(real 180*maths->acos(vdot(l, r)/(maths->sqrt(vdot(l, l))*maths->sqrt(vdot(r, r))))/π);
}
# given coordinates relative to centre of cube
disparity(p: Vector, b: Vector): real
{
p.z -= objd;
b.z -= objd;
return angle(p)-angle(b);
}
# rotation about any of the axes
# rotate(theta, &x, &y, &z) for x-axis
# rotate(theta, &y, &z, &x) for y-axis
# rotate(theta, &z, &x, &y) for z-axis
#
rotate(theta: int, x: real, y: real, z: real): (real, real, real)
{
a := π*real theta/real 180;
c := maths->cos(a);
s := maths->sin(a);
oy := y;
oz := z;
y = c*oy-s*oz;
z = c*oz+s*oy;
return (x, y, z);
}
# solve the quadratic ax^2 + bx + c = 0, returning the larger root
# * (a > 0)
#
quadratic(a: real, b: real, c: real): (int, real)
{
d := b*b-real 4*a*c;
if(d < real 0)
return (0, 0.0); # no real roots
x := (maths->sqrt(d)-b)/(real 2*a);
return (1, x);
}
# calculate the values of objD, scrD given the required parallax
dopar()
{
a := real 1;
b, c: real;
f := real 2*front-real 1;
x: real;
s: int;
w := sx*real SCRW;
ok: int;
if(hpar == 0.0){ # natural parallax
objD = objd;
scrD = scrd;
return;
}
if(fbpar < f)
s = -1;
else
s = 1;
if(fbpar == f)
fatal("parallax value is zero at screen distance");
b = (fbpar+f)*cubd-(fbpar-f)*w*eyed*real s*frac/hpar;
c = fbpar*f*cubd*cubd;
(ok, x) = quadratic(a, b, c);
if(ok){
objD = x;
scrD = x+f*cubd;
if(objD > real 0 && scrD > real 0)
return;
}
fatal("unachievable parallax value");
}
# update graphics parameters
update(init: int)
{
if(fov != real 0){
if(objd == real 0)
fov = 180.0;
else
fov = real 2*(real 180*maths->atan(cubd/(frac*objd))/π);
}
scrd = objd+(real 2*front-real 1)*cubd;
if(init){
if(objd < ε)
objd = ε;
if(fov != real 0)
sf = real (1<<2)*cubd/(objd*frac);
else
sf = cubd/frac;
}
# dopar();
domats();
}
fovtodist()
{
if(fov != real 0)
objd = cubd/(frac*maths->tan(π*(fov/real 2)/real 180));
}
getpolys()
{
(n, p, b) := polyhedra->scanpolyhedra("/lib/polyhedra");
polyhedra->getpolyhedra(p, b);
polys = p;
npolys = n;
do{
for(i := 0; i < p.V; i++)
p.v[i] = vmul(p.v[i], 0.5);
for(i = 0; i < p.F; i++)
p.f[i] = vmul(p.f[i], 0.5);
p = p.nxt;
}while(p != polys);
}
randpoly(p: ref Polyhedra->Polyhedron, n: int): ref Polyhedra->Polyhedron
{
r := random(0, n-1);
for( ; --r >= 0; p = p.nxt)
;
return p;
}
drawpoly(p: ref Polyhedra->Polyhedron, typex: int)
{
# V := p.V;
F := p.F;
v := p.v;
# f := p.f;
fv := p.fv;
for(i := 0; i < F; i++){
fvi := fv[i];
n := fvi[0];
m_begin(typex, n);
for(j := 0; j < n; j++){
vtx := v[fvi[j+1]];
m_vertex(vtx.x, vtx.y, vtx.z);
}
m_end();
}
}
# objects with unit sides/diameter
H: con 0.5;
square(typex: int)
{
m_begin(typex, 4);
m_vertex(-H, -H, 0.0);
m_vertex(-H, +H, 0.0);
m_vertex(+H, +H, 0.0);
m_vertex(+H, -H, 0.0);
m_end();
}
diamond(typex: int)
{
m_pushmatrix();
m_rotatez(45.0);
square(typex);
m_popmatrix();
}
circleinit()
{
i: int;
a := 0.0;
cost = array[NC] of real;
sint = array[NC] of real;
for(i = 0; i < NC; i++){
cost[i] = H*maths->cos(a);
sint[i] = H*maths->sin(a);
a += (2.0*π)/real NC;
}
}
circle(typex: int)
{
i: int;
if(DOCIRCLE){
m_begin(typex, 2);
m_circle(0.0, 0.0, 0.0, 0.5);
m_end();
return;
}
else{
m_begin(typex, NC);
for(i = 0; i < NC; i++)
m_vertex(cost[i], sint[i], 0.0);
m_end();
}
}
ellipse(typex: int)
{
m_begin(typex, 4);
m_ellipse(0.0, 0.0, 0.0, vmul(ef, 0.5));
m_end();
}
hexahedron(typex: int)
{
i, j, k: int;
V := array[8] of {
array[3] of {
-H, -H, -H,
},
array[3] of {
-H, -H, +H,
},
array[3] of {
-H, +H, -H,
},
array[3] of {
-H, +H, +H,
},
array[3] of {
+H, -H, -H,
},
array[3] of {
+H, -H, +H,
},
array[3] of {
+H, +H, -H,
},
array[3] of {
+H, +H, +H,
},
};
F := array[6] of {
array[4] of {
0, 4, 6, 2,
},
array[4] of {
0, 4, 5, 1,
},
array[4] of {
0, 1, 3, 2,
},
array[4] of {
1, 5, 7, 3,
},
array[4] of {
2, 6, 7, 3,
},
array[4] of {
4, 5, 7, 6,
},
};
for(i = 0; i < 6; i++){
m_begin(typex, 4);
for(j = 0; j < 4; j++){
k = F[i][j];
m_vertex(V[k][0], V[k][1], V[k][2]);
}
m_end();
}
}
zerot(tr: ref Object, zero: int, note: int)
{
if(tr != nil){
if(zero&REFLECT){
tr.rt = ∞;
tr.plane = nil;
}
if(zero&COLLIDE){
tr.ct = ∞;
tr.obj = nil;
}
if(note)
tr.todo = zero;
}
}
newobj(t: array of ref Object, n: int, vel: int, velf: real): ref Object
{
tr: ref Object;
p1: Vector;
again: int;
d := icubd-1.0;
cnt := 1024;
do{
p1 = randomp(-d, d);
again = 0;
for(i := 0; i < n; i++){
(nil, d2) := vlen2(vsub(t[i].p, p1));
if(d2 <= trkd2){
again = 1;
break;
}
}
cnt--;
}while(again && cnt > 0);
if(again)
return nil;
# p2 := randomp(-d, d);
p21 := randomd();
tr = ref Object;
tr.id = n;
tr.poly = nil;
if(poly){
if(n == 0)
tr.poly = randpoly(polys, npolys);
else
tr.poly = t[0].poly;
}
tr.c = randomc();
tr.cb = tr.c; # randomc();
if(vel)
tr.v = vf*velf*randomr(0.5, 2.0);
else
tr.v = 0.0;
tr.ω = vf*randomr(1.0, 10.0);
tr.roll = randomr(0.0, 360.0);
tr.pitch = randomr(0.0, 360.0);
tr.yaw = randomr(0.0, 360.0);
tr.l = ref Line(p1, vnorm(p21));
tr.p = p1;
tr.todo = 0;
zerot(tr, REFLECT|COLLIDE, 0);
tr.pmask = 0;
reflection(tr, planes);
return tr;
}
objinit(m: int, v: int)
{
velf := real m/real v;
p := nobjs;
n := p+m;
v += p;
t := array[n] of ref Object;
for(i := 0; i < p; i++)
t[i] = objs[i];
for(i = p; i < n; i++){
t[i] = newobj(t, i, i < v, velf);
if(t[i] == nil)
return;
}
sort(t, n);
nobjs = n;
objs = t;
for(i = p; i < n; i++)
collisions(t[i]);
}
zobj: Object;
newo(n: int, p: Vector, c: ref Draw->Image): ref Object
{
o := ref Object;
*o = zobj;
o.id = n;
o.c = o.cb = c;
o.l = ref Line(p, VZ);
o.p = p;
zerot(o, REFLECT|COLLIDE, 0);
reflection(o, planes);
return o;
}
objinits(nil: int, nil: int)
{
n := 16;
t := array[n] of ref Object;
r := trkd/2.0;
i := 0;
yc := 0.0;
for(y := 0; y < 5; y++){
xc := -real y*r;
for(x := 0; x <= y; x++){
t[i] = newo(i, (xc, yc, 0.0), red);
xc += trkd;
i++;
}
yc += trkd;
}
t[i] = newo(i, (0.0, -50.0, 0.0), white);
t[i].l.d = (0.0, 1.0, 0.0);
t[i].v = 1.0;
sort(t, n);
nobjs = n;
objs = t;
for(i = 0; i < n; i++)
collisions(t[i]);
}
initme(): ref Object
{
t := newobj(nil, 0, 1, 1.0);
t.roll = t.pitch = t.yaw = 0.0;
t.v = t.ω = 0.0;
t.l.a = (0.0, 0.0, objd); # origin when translated
t.l.d = (0.0, 0.0, -1.0);
t.p = t.l.a;
zerot(t, REFLECT|COLLIDE, 0);
return t;
}
retime(s: real)
{
r := 1.0/s;
n := nobjs;
t := objs;
for(i := 0; i < n; i++){
tr := t[i];
tr.v *= s;
tr.ω *= s;
tr.rt *= r;
tr.ct *= r;
}
me.v *= s;
me.ω *= s;
me.rt *= r;
me.ct *= r;
}
drawobjs()
{
tr: ref Object;
p: Vector;
n := nobjs;
t := objs;
for(i := 0; i < n; i++){
tr = t[i];
tr.pmask = 0;
p = tr.p;
m_pushmatrix();
if(rpy && tr.poly == nil){
m_loadmatrix(mod0);
(p.x, p.y, p.z) = rotate(int yaw, p.x, p.y, p.z);
(p.y, p.z, p.x) = rotate(int pitch, p.y, p.z, p.x);
(p.z, p.x, p.y) = rotate(int roll, p.z, p.x, p.y);
}
m_translate(p.x, p.y, p.z);
m_scale(trkd, trkd, trkd);
if(tr.poly != nil){
m_rotatez(tr.roll);
m_rotatey(tr.pitch);
m_rotatex(tr.yaw);
tr.yaw += tr.ω;
}
m_matmul();
if(tr.cb != tr.c){
m_colour(tr.cb);
if(tr.poly != nil)
drawpoly(tr.poly, POLY);
else if(DOCIRCLE)
circle(CIRCLE);
else
circle(POLY);
}
m_colour(tr.c);
if(tr.poly != nil)
drawpoly(tr.poly, FILLPOLY);
else if(DOCIRCLE)
circle(FILLCIRCLE);
else
circle(FILLPOLY);
m_popmatrix();
}
tm := 1.0;
do{
δt := ∞;
for(i = 0; i < n; i++){
tr = t[i];
if(tr.rt < δt)
δt = tr.rt;
if(tr.ct < δt)
δt = tr.ct;
}
if(δt > tm)
δt = tm;
for(i = 0; i < n; i++){
tr = t[i];
if(tr.rt == δt){
tr1 := tr.obj;
reflect(tr);
tr.p = tr.rp;
if(δt > 0.0)
tr.pmask = (1<<tr.plane.id);
else
tr.pmask |= (1<<tr.plane.id);
zerot(tr, REFLECT|COLLIDE, 1);
zerot(tr1, COLLIDE, 1);
}
else if(tr.ct == δt){
tr1 := tr.obj ;
collide(tr, tr1);
if(0 && poly)
collideω(tr, tr1);
tr.p = tr.cp;
tr1.p = tr1.cp;
tr.pmask = tr1.pmask = 0;
zerot(tr, REFLECT|COLLIDE, 1);
zerot(tr1, REFLECT|COLLIDE, 1);
}
else if(tr.todo != (REFLECT|COLLIDE)){
tr.p = vclip(vadd(tr.p, vmul(tr.l.d, tr.v*δt)), icubd);
tr.rt -= δt;
tr.ct -= δt;
}
}
for(i = 0; i < n; i++){
tr = t[i];
if(tr.todo){
if(tr.todo&REFLECT)
reflection(tr, planes);
if(tr.todo&COLLIDE)
collisions(tr);
tr.todo = 0;
}
}
tm -= δt;
}while(tm > 0.0);
sort(t, n);
}
drawscene()
{
m_pushmatrix();
m_scale(real 2*cubd, real 2*cubd, real 2*cubd);
m_colour(white);
m_matmul();
case(surr){
SPHERE =>
if(DOCIRCLE)
circle(CIRCLE);
else
circle(POLY);
ELLIPSOID =>
ellipse(ELLIPSE);
CUBE =>
if(flat)
square(POLY);
else
hexahedron(POLY);
* =>
drawpoly(polyh, POLY);
}
m_popmatrix();
drawobjs();
}
# ensure parallax doesn't alter between images
adjpar(x: array of real, y: array of real, z: array of real)
{
zed, incr: real;
y = nil;
if(z[0] < real 0)
zed = -z[0];
else
zed = z[0];
incr = eyed*zed*(real 1-scrD/(zed+objD-objd))/scrd;
if(!stereo || fov == real 0)
return;
if(left)
x[0] -= incr;
else
x[0] += incr;
}
view()
{
m_mode(PROJ);
m_loadidentity();
m_scale(sx, sy, sz);
if(fov != real 0)
m_frustum(sf, real (1<<2), real (1<<20));
else
m_ortho(sf, real (1<<2), real (1<<20));
# m_print();
m_mode(MODEL);
}
model(rot: int)
{
m_loadidentity();
m_translate(0.0, 0.0, -objd);
if(rpy && rot){
m_rotatez(roll);
m_rotatey(pitch);
m_rotatex(yaw);
}
}
# store projection and modelview matrices
domats()
{
model(0);
m_storematrix(mod0);
model(1);
view();
}
scale()
{
if(maxx > maxy)
sx = real maxy/real maxx;
else
sx = 1.0;
if(maxy > maxx)
sy = real maxx/real maxy;
else
sy = 1.0;
sz = 1.0;
}
rescale(w: int, h: int)
{
maxx = w;
maxy = h;
scale();
m_viewport(0, 0, maxx, maxy);
}
grinit()
{
for(i := 0; i < 4; i++)
mod0[i] = array[4] of real;
far = (2.0*cubd, 2.0*cubd, 2.0*cubd);
icubd = cubd-trkd/2.0;
icubd2 = icubd*icubd;
trkd2 = trkd*trkd;
cmin = vpt(-icubd, -icubd, -icubd);
cmax = vpt(+icubd, +icubd, +icubd);
maxx = MAXX;
maxy = MAXY;
e2 = vmul(vvmul(ef, ef), icubd2);
m_init();
pinit();
circleinit();
m_viewport(0, 0, maxx, maxy);
scale();
if(fov > real 0)
fovtodist();
update(1);
}
newimage(win: ref Toplevel, init: int)
{
maxx = int cmd(win, ".p cget -actwidth");
maxy = int cmd(win, ".p cget -actheight");
RDisp = display.newimage(((0, 0), (maxx, maxy)), win.image.chans, 0, Draw->Black);
tk->putimage(win, ".p", RDisp, nil);
RDisp.draw(RDisp.r, black, nil, (0, 0));
reveal();
rescale(maxx, maxy);
update(init);
}
reveal()
{
cmd(toplev, ".p dirty; update");
}
usage()
{
sys->fprint(sys->fildes(2), "usage: collide [-cse] [-f] [-op] [-b num] [-v num]\n");
exit;
}
main(ctxt: ref Draw->Context, args: list of string)
{
rand->init(daytime->now());
daytime = nil;
b := v := random(16, 32);
arg->init(args);
while((o := arg->opt()) != 0){
case(o){
* =>
usage();
's' =>
surr = SPHERE;
'e' =>
surr = ELLIPSOID;
'c' =>
surr = CUBE;
'o' =>
fov = 0.0;
'p' =>
fov = -1.0;
'f' =>
flat = 1;
'b' =>
b = v = int arg->arg();
'v' =>
v = int arg->arg();
}
}
if(arg->argv() != nil)
usage();
if(b <= 0)
b = 1;
if(b > 100)
b = 100;
if(v <= 0)
v = 1;
if(v > b)
v = b;
if(poly || surr == POLYHEDRON){
polyhedra = load Polyhedra Polyhedra->PATH;
getpolys();
}
if(surr == POLYHEDRON)
polyh = randpoly(polys, npolys);
grinit();
tkclient->init();
(win, wmch) := tkclient->toplevel(ctxt, "", "Collide", Tkclient->Resize | Tkclient->Hide);
toplev = win;
sys->pctl(Sys->NEWPGRP, nil);
cmdch := chan of string;
tk->namechan(win, cmdch, "cmd");
for(i := 0; i < len winconfig; i++)
cmd(win, winconfig[i]);
cmd(win, "update");
tkclient->onscreen(win, nil);
tkclient->startinput(win, "kbd"::"ptr"::nil);
display = win.image.display;
newimage(win, 1);
black = display.color(Draw->Black);
white = display.color(Draw->White);
red = display.color(Draw->Red);
objinit(b, v);
me = initme();
pid := -1;
sync := chan of int;
cmdc := chan of int;
spawn animate(sync, cmdc);
pid = <- sync;
for(;;){
alt{
c := <-win.ctxt.kbd =>
tk->keyboard(win, c);
c := <-win.ctxt.ptr =>
tk->pointer(win, *c);
c := <-win.ctxt.ctl or
c = <-win.wreq =>
tkclient->wmctl(win, c);
c := <- wmch =>
case c{
"exit" =>
if(pid != -1)
kill(pid);
exit;
* =>
sync <-= 0;
tkclient->wmctl(win, c);
if(c[0] == '!')
newimage(win, 0);
sync <-= 1;
}
c := <- cmdch =>
case c{
"stop" =>
cmdc <-= 's';
"zoomin" =>
cmdc <-= 'z';
"zoomout" =>
cmdc <-= 'o';
"slow" =>
cmdc <-= '-';
"fast" =>
cmdc <-= '+';
"objs" =>
sync <-= 0;
b >>= 1;
if(b == 0)
b = 1;
objinit(b, b);
sync <-= 1;
}
}
}
}
sign(r: real): real
{
if(r < 0.0)
return -1.0;
return 1.0;
}
abs(r: real): real
{
if(r < 0.0)
return -r;
return r;
}
animate(sync: chan of int, cmd: chan of int)
{
zd := objd/250.0;
δ := θ := 0.0;
f := 8;
sync <- = sys->pctl(0, nil);
for(;;){
σ := 1.0;
alt{
<- sync =>
<- sync;
c := <- cmd =>
case(c){
's' =>
δ = θ = 0.0;
'z' =>
δ = zd;
'o' =>
δ = -zd;
'r' =>
θ = 1.0;
'+' =>
σ = 1.25;
f++;
if(f > 16){
f--;
σ = 1.0;
}
else
vf *= σ;
'-' =>
σ = 0.8;
f--;
if(f < 0){
f++;
σ = 1.0;
}
else
vf *= σ;
}
* =>
sys->sleep(0);
}
RDisp.draw(RDisp.r, black, nil, (0, 0));
drawscene();
reveal();
if(δ != 0.0 || θ != 0.0){
objd -= δ;
me.l.a.z -= δ;
if(θ != 0.0){
roll += θ;
pitch += θ;
yaw += θ;
rpy = 1;
}
update(projx);
}
if(σ != 1.0)
retime(σ);
}
}
# usually almost sorted
sort(ts: array of ref Object, n: int)
{
done: int;
t: ref Object;
q, p: int;
q = n;
do{
done = 1;
q--;
for(p = 0; p < q; p++){
if(ts[p].p.z > ts[p+1].p.z){
t = ts[p];
ts[p] = ts[p+1];
ts[p+1] = t;
done = 0;
}
}
}while(!done);
}
kill(pid: int): int
{
fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
if(fd == nil)
return -1;
if(sys->write(fd, array of byte "kill", 4) != 4)
return -1;
return 0;
}
fatal(e: string)
{
sys->fprint(sys->fildes(2), "%s\n", e);
raise "fatal";
}
cmd(top: ref Toplevel, s: string): string
{
e := tk->cmd(top, s);
if (e != nil && e[0] == '!')
fatal(sys->sprint("tk error on '%s': %s", s, e));
return e;
}
winconfig := array[] of {
"frame .f",
"button .f.zoomin -text {zoomin} -command {send cmd zoomin}",
"button .f.zoomout -text {zoomout} -command {send cmd zoomout}",
"button .f.stop -text {stop} -command {send cmd stop}",
"pack .f.zoomin -side left",
"pack .f.zoomout -side right",
"pack .f.stop -side top",
"frame .f2",
"button .f2.slow -text {slow} -command {send cmd slow}",
"button .f2.fast -text {fast} -command {send cmd fast}",
"button .f2.objs -text {objects} -command {send cmd objs}",
"pack .f2.slow -side left",
"pack .f2.fast -side right",
"pack .f2.objs -side top",
"panel .p -width " + string MAXX + " -height " + string MAXY,
"pack .f -side top -fill x",
"pack .f2 -side top -fill x",
"pack .p -side bottom -fill both -expand 1",
"pack propagate . 0",
"update"
};
############################################################
# gl.b
#
# initially generated by c2l
#
MODEL, PROJ: con iota;
Matrix: type array of array of real;
Mstate: adt{
matl: list of Matrix;
modl: list of Matrix;
prjl: list of Matrix;
mull: Matrix;
freel: list of Matrix;
vk: int;
vr: int;
vrr: int;
vc: ref Draw->Image;
ap: array of Draw->Point;
apn: int;
mx, cx, my, cy: real;
ignore: int;
};
ms: Mstate;
m_new(): Matrix
{
if(ms.freel != nil){
m := hd ms.freel;
ms.freel = tl ms.freel;
return m;
}
m := array[4] of array of real;
for(i := 0; i < 4; i++)
m[i] = array[4] of real;
return m;
}
m_free(m: Matrix)
{
ms.freel = m :: ms.freel;
}
m_init()
{
ms.modl = m_new() :: nil;
ms.prjl = m_new() :: nil;
ms.matl = ms.modl;
ms.mull = m_new();
ms.vk = 0;
ms.apn = 0;
ms.mx = ms.cx = ms.my = ms.cy = 0.0;
ms.ignore = 0;
}
m_print()
{
m := hd ms.matl;
for(i := 0; i < 4; i++){
for(j := 0; j < 4; j++)
sys->print("%f ", m[i][j]);
sys->print("\n");
}
sys->print("\n");
}
m_mode(m: int)
{
if(m == PROJ)
ms.matl = ms.prjl;
else
ms.matl = ms.modl;
}
m_pushmatrix()
{
if(ms.matl == ms.modl){
ms.modl = m_new() :: ms.modl;
ms.matl = ms.modl;
}
else{
ms.prjl = m_new() :: ms.prjl;
ms.matl = ms.prjl;
}
s := hd tl ms.matl;
d := hd ms.matl;
for(i := 0; i < 4; i++)
for(j := 0; j < 4; j++)
d[i][j] = s[i][j];
}
m_popmatrix()
{
if(ms.matl == ms.modl){
m_free(hd ms.modl);
ms.modl = tl ms.modl;
ms.matl = ms.modl;
}
else{
m_free(hd ms.prjl);
ms.prjl = tl ms.prjl;
ms.matl = ms.prjl;
}
}
m_loadidentity()
{
i, j: int;
m := hd ms.matl;
for(i = 0; i < 4; i++){
for(j = 0; j < 4; j++)
m[i][j] = real 0;
m[i][i] = real 1;
}
}
m_translate(x: real, y: real, z: real)
{
i: int;
m := hd ms.matl;
for(i = 0; i < 4; i++)
m[i][3] = x*m[i][0]+y*m[i][1]+z*m[i][2]+m[i][3];
}
m_scale(x: real, y: real, z: real)
{
i: int;
m := hd ms.matl;
for(i = 0; i < 4; i++){
m[i][0] *= x;
m[i][1] *= y;
m[i][2] *= z;
}
}
# rotate about x, y or z axes
rot(deg: real, j: int, k: int)
{
i: int;
m := hd ms.matl;
rad := Math->Pi*deg/real 180;
s := maths->sin(rad);
c := maths->cos(rad);
a, b: real;
for(i = 0; i < 4; i++){
a = m[i][j];
b = m[i][k];
m[i][j] = c*a+s*b;
m[i][k] = c*b-s*a;
}
}
m_rotatex(a: real)
{
rot(a, 1, 2);
}
m_rotatey(a: real)
{
rot(a, 2, 0);
}
m_rotatez(a: real)
{
rot(a, 0, 1);
}
# (l m n) normalized
m_rotate(deg: real, l: real, m: real, n:real)
{
i: int;
mx := hd ms.matl;
rad := Math->Pi*deg/real 180;
s := maths->sin(rad);
c := maths->cos(rad);
f := 1.0-c;
m0, m1, m2: real;
for(i = 0; i < 4; i++){
m0 = mx[i][0];
m1 = mx[i][1];
m2 = mx[i][2];
mx[i][0] = m0*(l*l*f+c)+m1*(l*m*f+n*s)+m2*(l*n*f-m*s);
mx[i][1] = m0*(l*m*f-n*s)+m1*(m*m*f+c)+m2*(m*n*f+l*s);
mx[i][2] = m0*(l*n*f+m*s)+m1*(m*n*f-l*s)+m2*(n*n*f+c);
}
}
# Frustum(-l, l, -l, l, n, f)
m_frustum(l: real, n: real, f: real)
{
i: int;
m := hd ms.matl;
r := n/l;
a, b: real;
f = ∞;
for(i = 0; i < 4; i++){
a = m[i][2];
b = m[i][3];
m[i][0] *= r;
m[i][1] *= r;
m[i][2] = a+b;
m[i][3] = 0.0;
# m[i][2] = -(a*(f+n)/(f-n)+b);
# m[i][3] = real -2*f*n*a/(f-n);
}
}
# Ortho(-l, l, -l, l, n, f)
m_ortho(l: real, n: real, f: real)
{
i: int;
m := hd ms.matl;
r := real 1/l;
# a: real;
n = 0.0;
f = ∞;
for(i = 0; i < 4; i++){
# a = m[i][2];
m[i][0] *= r;
m[i][1] *= r;
# m[i][2] *= real -2/(f-n);
# m[i][3] -= a*(f+n)/(f-n);
}
}
m_loadmatrix(u: array of array of real)
{
m := hd ms.matl;
for(i := 0; i < 4; i++)
for(j := 0; j < 4; j++)
m[i][j] = u[i][j];
}
m_storematrix(u: array of array of real)
{
m := hd ms.matl;
for(i := 0; i < 4; i++)
for(j := 0; j < 4; j++)
u[i][j] = m[i][j];
}
m_matmul()
{
m, p, r: Matrix;
m = hd ms.modl;
p = hd ms.prjl;
r = ms.mull;
for(i := 0; i < 4; i++){
pr := p[i];
rr := r[i];
for(j := 0; j < 4; j++)
rr[j] = pr[0]*m[0][j]+pr[1]*m[1][j]+pr[2]*m[2][j]+pr[3]*m[3][j];
}
}
m_vertexo(x: real, y: real, z: real)
{
m: Matrix;
mr: array of real;
w, x1, y1, z1: real;
m = hd ms.modl;
mr = m[0]; x1 = x*mr[0]+y*mr[1]+z*mr[2]+mr[3];
mr = m[1]; y1 = x*mr[0]+y*mr[1]+z*mr[2]+mr[3];
mr = m[2]; z1 = x*mr[0]+y*mr[1]+z*mr[2]+mr[3];
mr = m[3]; w = x*mr[0]+y*mr[1]+z*mr[2]+mr[3];
if(w != real 1){
x1 /= w;
y1 /= w;
z1 /= w;
}
if(z1 >= 0.0){
ms.ignore = 1;
return;
}
m = hd ms.prjl;
mr = m[0]; x = x1*mr[0]+y1*mr[1]+z1*mr[2]+mr[3];
mr = m[1]; y = x1*mr[0]+y1*mr[1]+z1*mr[2]+mr[3];
mr = m[2]; z = x1*mr[0]+y1*mr[1]+z1*mr[2]+mr[3];
mr = m[3]; w = x1*mr[0]+y1*mr[1]+z1*mr[2]+mr[3];
if(w == real 0){
ms.ignore = 1;
return;
}
if(w != real 1){
x /= w;
y /= w;
# z /= w;
}
ms.ap[ms.apn++] = (int (ms.mx*x+ms.cx), int (ms.my*y+ms.cy));
}
m_vertex(x: real, y: real, z: real): (real, real)
{
m: Matrix;
mr: array of real;
w, x1, y1, z1: real;
m = ms.mull;
mr = m[0]; x1 = x*mr[0]+y*mr[1]+z*mr[2]+mr[3];
mr = m[1]; y1 = x*mr[0]+y*mr[1]+z*mr[2]+mr[3];
mr = m[2]; z1 = x*mr[0]+y*mr[1]+z*mr[2]+mr[3];
mr = m[3]; w = x*mr[0]+y*mr[1]+z*mr[2]+mr[3];
if(w == real 0){
ms.ignore = 1;
return (x1, y1);
}
if(w != real 1){
x1 /= w;
y1 /= w;
# z1 /= w;
}
if(z1 >= 0.0){
ms.ignore = 1;
return (x1, y1);
}
ms.ap[ms.apn++] = (int (ms.mx*x1+ms.cx), int (ms.my*y1+ms.cy));
return (x1, y1);
}
m_circle(x: real, y: real, z: real, r: real)
{
(d, nil) := m_vertex(x, y, z);
(e, nil) := m_vertex(x+r, y, z);
d -= e;
if(d < 0.0)
d = -d;
ms.vr = int (ms.mx*d);
}
m_ellipse(x: real, y: real, z: real, v: Vector)
{
m_circle(x, y, z, v.x);
(nil, d) := m_vertex(x, y, z);
(nil, e) := m_vertex(x, y+v.y, z);
d -= e;
if(d < 0.0)
d = -d;
ms.vrr = int (ms.my*d);
}
m_begin(k: int, n: int)
{
ms.ignore = 0;
ms.vk = k;
ms.ap = array[n+1] of Draw->Point;
ms.apn = 0;
}
m_end()
{
if(ms.ignore)
return;
case(ms.vk){
CIRCLE =>
RDisp.ellipse(ms.ap[0], ms.vr, ms.vr, 0, ms.vc, (0, 0));
FILLCIRCLE =>
RDisp.fillellipse(ms.ap[0], ms.vr, ms.vr, ms.vc, (0, 0));
ELLIPSE =>
RDisp.ellipse(ms.ap[0], ms.vr, ms.vrr, 0, ms.vc, (0, 0));
FILLELLIPSE =>
RDisp.fillellipse(ms.ap[0], ms.vr, ms.vrr, ms.vc, (0, 0));
POLY =>
ms.ap[len ms.ap-1] = ms.ap[0];
RDisp.poly(ms.ap, Draw->Endsquare, Draw->Endsquare, 0, ms.vc, (0, 0));
FILLPOLY =>
ms.ap[len ms.ap-1] = ms.ap[0];
RDisp.fillpoly(ms.ap, ~0, ms.vc, (0, 0));
}
}
m_colour(i: ref Draw->Image)
{
ms.vc = i;
}
m_viewport(x1: int, y1: int, x2: int, y2: int)
{
ms.mx = real (x2-x1)/2.0;
ms.cx = real (x2+x1)/2.0;
ms.my = real (y2-y1)/2.0;
ms.cy = real (y2+y1)/2.0;
}
# sys->print("%d %f (%f %f %f) %s\n", ok, λ, 1.0, 2.0*vdot(l.a, l.d), vdot(l.a, l.a)-icubd2, lstring(l));
# sys->print("%d %f (%f %f %f) %s\n", ok, λ, vmuldiv(l.d, l.d, e2), 2.0*vmuldiv(l.a, l.d, e2), vmuldiv(l.a, l.a, e2)-1.0, lstring(l));
# for(lp = lp0 ; lp != nil; lp = tl lp){
# p := hd lp;
# (ok, λ, pt) := intersect(l, p);
# sys->print("%d %x %d %f %s %s %s\n", p.id, tr.pmask, ok, λ, vstring(pt), pstring(p), lstring(l));
# }