summaryrefslogtreecommitdiff
path: root/appl/spree/other
diff options
context:
space:
mode:
Diffstat (limited to 'appl/spree/other')
-rw-r--r--appl/spree/other/tst.b151
-rw-r--r--appl/spree/other/tstboing.b158
-rwxr-xr-xappl/spree/other/tstlines.sh53
-rw-r--r--appl/spree/other/tstwin.b351
4 files changed, 713 insertions, 0 deletions
diff --git a/appl/spree/other/tst.b b/appl/spree/other/tst.b
new file mode 100644
index 00000000..3b35fefa
--- /dev/null
+++ b/appl/spree/other/tst.b
@@ -0,0 +1,151 @@
+implement Tst;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Point, Rect: import draw;
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+Tst: module
+{
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+File: adt {
+ name: string;
+ fd: ref Sys->FD;
+ pid: int;
+};
+
+files: list of ref File;
+
+stderr: ref Sys->FD;
+outputch: chan of chan of string;
+init(ctxt: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ stderr = sys->fildes(2);
+ bufio = load Bufio Bufio->PATH;
+ sys->print(":cardtst\n");
+ stdin := bufio->fopen(sys->fildes(0), Sys->OREAD);
+ line := "";
+ currfd: ref Sys->FD;
+ outputch = chan of chan of string;
+ spawn outputproc();
+ while ((s := stdin.gets('\n')) != nil) {
+ if (len s > 1 && s[len s - 2] == '\\')
+ line += s[0:len s - 2] + "\n";
+ else {
+ s = line + s;
+ line = nil;
+ if (s[0] == ':') {
+ (nil, toks) := sys->tokenize(s, " \n");
+ case hd toks {
+ ":open" =>
+ if (tl toks == nil) {
+ sys->fprint(stderr, "usage: open file\n");
+ continue;
+ }
+ f := open(hd tl toks);
+ if (f != nil) {
+ currfd = f.fd;
+ sys->print("current file is now %s\n", f.name);
+ }
+ ":close" =>
+ if (tl toks == nil) {
+ sys->fprint(stderr, "usage: close file\n");
+ continue;
+ }
+ fl := files;
+ f: ref File;
+ for (files = nil; fl != nil; fl = tl fl) {
+ if ((hd fl).name == hd tl toks)
+ f = hd fl;
+ else
+ files = hd fl :: files;
+ }
+ if (f == nil) {
+ sys->fprint(stderr, "unknown file '%s'\n", hd tl toks);
+ continue;
+ }
+ sys->fprint(f.fd, "");
+ f = nil;
+ ":files" =>
+ for (fl := files; fl != nil; fl = tl fl) {
+ if ((hd fl).fd == currfd)
+ sys->print(":%s <--- current\n", (hd fl).name);
+ else
+ sys->print(":%s\n", (hd fl).name);
+ }
+ * =>
+ for (fl := files; fl != nil; fl = tl fl)
+ if ((hd fl).name == (hd toks)[1:])
+ break;
+ if (fl == nil) {
+ sys->fprint(stderr, "unknown file '%s'\n", (hd toks)[1:]);
+ continue;
+ }
+ currfd = (hd fl).fd;
+ }
+ } else if (currfd == nil)
+ sys->fprint(stderr, "no current file\n");
+ else if (len s > 1 && sys->fprint(currfd, "%s", s[0:len s - 1]) == -1)
+ sys->fprint(stderr, "command failed: %r\n");
+ }
+ }
+ for (fl := files; fl != nil; fl = tl fl)
+ kill((hd fl).pid);
+ outputch <-= nil;
+}
+
+open(f: string): ref File
+{
+ fd := sys->open("/n/remote/" + f, Sys->ORDWR);
+ if (fd == nil) {
+ sys->fprint(stderr, "cannot open %s: %r\n", f);
+ return nil;
+ }
+ sync := chan of int;
+ spawn updateproc(f, fd, sync);
+ files = ref File(f, fd, <-sync) :: files;
+ sys->print("opened %s\n", f);
+ return hd files;
+}
+
+updateproc(name: string, fd: ref Sys->FD, sync: chan of int)
+{
+ sync <-= sys->pctl(0, nil);
+ c := chan of string;
+ buf := array[Sys->ATOMICIO] of byte;
+ while ((n := sys->read(fd, buf, len buf)) > 0) {
+ (nt, toks) := sys->tokenize(string buf[0:n], "\n");
+ outputch <-= c;
+ c <-= "++ " + name + ":\n";
+ for (; toks != nil; toks = tl toks)
+ c <-= sys->sprint("+%s\n", hd toks);
+ c <-= nil;
+ }
+ if (n < 0)
+ sys->fprint(stderr, "cards: error reading %s: %r\n", name);
+ sys->fprint(stderr, "cards: updateproc (%s) exiting\n", name);
+}
+
+outputproc()
+{
+ for (;;) {
+ c := <-outputch;
+ if (c == nil)
+ exit;
+ while ((s := <-c) != nil)
+ sys->print("%s", s);
+ }
+}
+
+kill(pid: int)
+{
+ if ((fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE)) != nil)
+ sys->write(fd, array of byte "kill", 4);
+}
+
diff --git a/appl/spree/other/tstboing.b b/appl/spree/other/tstboing.b
new file mode 100644
index 00000000..a599a0ab
--- /dev/null
+++ b/appl/spree/other/tstboing.b
@@ -0,0 +1,158 @@
+implement Tst;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+ draw: Draw;
+ Point, Rect: import draw;
+include "sh.m";
+ sh: Sh;
+ Context: import Sh;
+include "math.m";
+ math: Math;
+ZERO: con 1e-6;
+
+stderr: ref Sys->FD;
+
+Tst: module {
+ init: fn(nil: ref Draw->Context, argv: list of string);
+};
+π: con Math->Pi;
+Maxδ: con π / 4.0;
+
+init(nil: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ stderr = sys->fildes(2);
+ math = load Math Math->PATH;
+ if (len argv != 9) {
+ sys->fprint(stderr, "args?\n");
+ exit;
+ }
+ ar := argv2r(tl argv);
+ br := argv2r(tl tl tl tl tl argv);
+
+ a := Line.new(ar.min, ar.max); # ball
+ b := Line.new(br.min, br.max); # bat
+ (hit, hitp, s, t) := b.intersection(a.p, a.v);
+ if (hit) {
+ nv := boing(a.v, b);
+ rl := ref Line(hitp, nv, 50.0);
+ ballθ := a.θ();
+ batθ := b.θ();
+ φ := ballθ - batθ;
+ δ: real;
+ if (math->sin(φ) > 0.0)
+ δ = (t / b.s) * Maxδ * 2.0 - Maxδ;
+ else
+ δ = (t / b.s) * -Maxδ * 2.0 + Maxδ;
+ nl := Line.newpolar(rl.p, rl.θ() + δ, rl.s);
+ sys->print("%s %s %s\n", p2s(rl.point(0.0)), p2s(rl.point(rl.s)), p2s(nl.point(nl.s)));
+ } else
+ sys->fprint(stderr, "no hit\n");
+}
+
+argv2r(v: list of string): Rect
+{
+ r: Rect;
+ (r.min.x, v) = (int hd v, tl v);
+ (r.min.y, v) = (int hd v, tl v);
+ (r.max.x, v) = (int hd v, tl v);
+ (r.max.y, v) = (int hd v, tl v);
+ return r;
+}
+Line: adt {
+ p, v: Realpoint;
+ s: real;
+ new: fn(p1, p2: Point): ref Line;
+ hittest: fn(l: self ref Line, p: Point): (Realpoint, real, real);
+ intersection: fn(b: self ref Line, p, v: Realpoint): (int, Realpoint, real, real);
+ point: fn(b: self ref Line, s: real): Point;
+ θ: fn(b: self ref Line): real;
+ newpolar: fn(p: Realpoint, θ: real, s: real): ref Line;
+};
+
+Realpoint: adt {
+ x, y: real;
+};
+
+Line.new(p1, p2: Point): ref Line
+{
+ ln := ref Line;
+ ln.p = (real p1.x, real p1.y);
+ v := Realpoint(real (p2.x - p1.x), real (p2.y - p1.y));
+ ln.s = math->sqrt(v.x * v.x + v.y * v.y);
+ if (ln.s > ZERO)
+ ln.v = (v.x / ln.s, v.y / ln.s);
+ else
+ ln.v = (1.0, 0.0);
+ return ln;
+}
+
+Line.newpolar(p: Realpoint, θ: real, s: real): ref Line
+{
+ l := ref Line;
+ l.p = p;
+ l.s = s;
+ l.v = (math->cos(θ), math->sin(θ));
+ return l;
+}
+
+Line.θ(l: self ref Line): real
+{
+ return math->atan2(l.v.y, l.v.x);
+}
+
+# return normal from line, perpendicular distance from line and distance down line
+Line.hittest(l: self ref Line, ip: Point): (Realpoint, real, real)
+{
+ p := Realpoint(real ip.x, real ip.y);
+ v := Realpoint(-l.v.y, l.v.x);
+ (nil, nil, perp, ldist) := l.intersection(p, v);
+ return (v, perp, ldist);
+}
+
+Line.point(l: self ref Line, s: real): Point
+{
+ return (int (l.p.x + s * l.v.x), int (l.p.y + s * l.v.y));
+}
+
+# compute the intersection of lines a and b.
+# b is assumed to be fixed, and a is indefinitely long
+# but doesn't extend backwards from its starting point.
+# a is defined by the starting point p and the unit vector v.
+# return whether it hit, the point at which it hit if so,
+# the distance of the intersection point from p,
+# and the distance of the intersection point from b.p.
+Line.intersection(b: self ref Line, p, v: Realpoint): (int, Realpoint, real, real)
+{
+ det := b.v.x * v.y - v.x * b.v.y;
+ if (det > -ZERO && det < ZERO)
+ return (0, (0.0, 0.0), 0.0, 0.0);
+
+ y21 := b.p.y - p.y;
+ x21 := b.p.x - p.x;
+ s := (b.v.x * y21 - b.v.y * x21) / det;
+ t := (v.x * y21 - v.y * x21) / det;
+ if (s < 0.0)
+ return (0, (0.0, 0.0), s, t);
+ hit := t >= 0.0 && t <= b.s;
+ hp: Realpoint;
+ if (hit)
+ hp = (p.x+v.x*s, p.y+v.y*s);
+ return (hit, hp, s, t);
+}
+
+# bounce ball travelling in direction av off line b.
+# return the new unit vector.
+boing(av: Realpoint, b: ref Line): Realpoint
+{
+ d := math->atan2(real b.v.y, real b.v.x) * 2.0 - math->atan2(av.y, av.x);
+ return (math->cos(d), math->sin(d));
+}
+
+p2s(p: Point): string
+{
+ return string p.x + " " + string p.y;
+}
+
diff --git a/appl/spree/other/tstlines.sh b/appl/spree/other/tstlines.sh
new file mode 100755
index 00000000..7e75b3a4
--- /dev/null
+++ b/appl/spree/other/tstlines.sh
@@ -0,0 +1,53 @@
+#!/dis/sh
+load tk std
+pctl newpgrp
+wid=${tk window 'Test lines'}
+fn x {tk $wid $*}
+x canvas .c
+x pack .c
+x 'bind .c <ButtonRelease-1> {send b1 %x %y}'
+x 'bind .c <ButtonRelease-2> {send b2 %x %y}'
+x update
+chan b1 b2
+tk namechan $wid b1
+tk namechan $wid b2
+while {} {tk winctl $wid ${recv $wid}} &
+chan show
+ifs='
+'
+v1 := 0 0 1 1
+v2 := 1 1 2 2
+while {} {
+ args:=${split ${recv show}}
+ (t args) = $args
+ $t = $args
+
+ tk 0 .c delete lines
+ echo $v1 $v2
+ r := `{tstboing $v1 $v2}
+ (ap1x ap1y ap2x ap2y bp1x bp1y bp2x bp2y) := $v1 $v2
+ tk 0 .c create line $ap1x $ap1y $ap2x $ap2y -tags lines -fill black -width 3 -arrow last
+ tk 0 .c create line $bp1x $bp1y $bp2x $bp2y -tags lines -fill red
+ and {~ $#r 6} {
+ (rp1x rp1y rp2x rp2y sp2x sp2y) := $r
+ tk 0 .c create line $ap2x $ap2y $rp1x $rp1y -tags lines -fill black
+ tk 0 .c create line $rp1x $rp1y $rp2x $rp2y -tags lines -fill green -arrow last
+ tk 0 .c create line $rp1x $rp1y $sp2x $sp2y -tags lines -fill blue -arrow last
+ }
+ tk 0 update
+} &
+
+fn show {
+ a:=$*
+ if {~ $#a 8} {echo usage} {
+ send show ${join ' ' $a}
+ }
+}
+
+for i in 1 2 {
+ while {} {
+ p1:=${recv b^$i}
+ p2:=${recv b^$i}
+ send show ${join ' ' v^$i $p1 $p2}
+ } &
+}
diff --git a/appl/spree/other/tstwin.b b/appl/spree/other/tstwin.b
new file mode 100644
index 00000000..de7c7ab4
--- /dev/null
+++ b/appl/spree/other/tstwin.b
@@ -0,0 +1,351 @@
+implement Tstwin;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ draw: Draw;
+ Context, Display, Point, Rect, Image, Screen: import draw;
+
+include "tk.m";
+ tk: Tk;
+ Toplevel: import tk;
+
+include "tkclient.m";
+ tkclient: Tkclient;
+
+include "math.m";
+ math: Math;
+
+Tstwin: module
+{
+ init: fn(ctxt: ref Context, argv: list of string);
+};
+
+screen: ref Screen;
+display: ref Display;
+win: ref Toplevel;
+
+NC: con 6;
+
+task_cfg := array[] of {
+"label .xy -text {0 0}",
+"canvas .c -height 500 -width 500",
+"pack .xy -side top -fill x",
+"pack .c -side bottom -fill both -expand 1",
+"bind .c <ButtonRelease-1> {send cmd 0 1 %x %y}",
+"bind .c <ButtonRelease-2> {send cmd 0 2 %x %y}",
+"bind .c <Button-1> {send cmd 1 1 %x %y}",
+"bind .c <Button-2> {send cmd 1 2 %x %y}",
+};
+
+Obstacle: adt {
+ line: ref Line;
+ s1, s2: real;
+ id: int;
+ config: fn(b: self ref Obstacle);
+ new: fn(id: int): ref Obstacle;
+};
+
+Line: adt {
+ p, v: Realpoint;
+ s: real;
+ new: fn(p1, p2: Point): ref Line;
+ hittest: fn(l: self ref Line, p: Point): (Realpoint, real, real);
+ intersection: fn(b: self ref Line, p, v: Realpoint): (int, Realpoint, real, real);
+ point: fn(b: self ref Line, s: real): Point;
+};
+bats: list of ref Obstacle;
+init(ctxt: ref Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ tk = load Tk Tk->PATH;
+ tkclient = load Tkclient Tkclient->PATH;
+ math = load Math Math->PATH;
+
+ sys->pctl(Sys->NEWPGRP, nil);
+
+ display = ctxt.display;
+ screen = ctxt.screen;
+
+ tkclient->init();
+
+ menubut: chan of string;
+ (win, menubut) = tkclient->toplevel(screen, nil, "Window testing", 0);
+
+ cmd := chan of string;
+ tk->namechan(win, cmd, "cmd");
+
+ tkclient->tkcmds(win, task_cfg);
+
+ mch := chan of (int, Point);
+ spawn mouseproc(mch);
+
+ bat := Obstacle.new(0);
+ bats = bat :: nil;
+ bat.line = Line.new((100, 0), (150, 500));
+ bat.s1 = 10.0;
+ bat.s2 = 110.0;
+ bat.config();
+
+ tk->cmd(win, "update");
+ buts := 0;
+ for(;;) alt {
+ menu := <-menubut =>
+ tkclient->wmctl(win, menu);
+
+ c := <-cmd =>
+ (nil, toks) := sys->tokenize(c, " ");
+ if ((hd toks)[0] == '1')
+ buts |= int hd tl toks;
+ else
+ buts &= ~int hd tl toks;
+ mch <-= (buts, Point(int hd tl tl toks, int hd tl tl tl toks));
+ }
+}
+
+Realpoint: adt {
+ x, y: real;
+};
+
+cmd(top: ref Tk->Toplevel, s: string): string
+{
+ e := tk->cmd(top, s);
+ if (e != nil && e[0] == '!')
+ sys->print("tk error %s on '%s'\n", e, s);
+ return e;
+}
+
+p2s(p: Point): string
+{
+ return string p.x + " " + string p.y;
+}
+
+mouseproc(mch: chan of (int, Point))
+{
+ for (;;) {
+ hitbat: ref Obstacle = nil;
+ minperp, hitdist: real;
+ (buts, p) := <-mch;
+ for (bl := bats; bl != nil; bl = tl bl) {
+ b := hd bl;
+ (normal, perp, dist) := b.line.hittest(p);
+ perp = abs(perp);
+
+ if ((hitbat == nil || perp < minperp) && (dist >= b.s1 && dist <= b.s2))
+ (hitbat, minperp, hitdist) = (b, perp, dist);
+ }
+ if (hitbat == nil || minperp > 30.0) {
+ while ((<-mch).t0)
+ ;
+ continue;
+ }
+ offset := hitdist - hitbat.s1;
+ if (buts & 2)
+ (buts, p) = aim(mch, hitbat, p);
+ if (buts & 1)
+ drag(mch, hitbat, offset);
+ }
+}
+
+
+drag(mch: chan of (int, Point), hitbat: ref Obstacle, offset: real)
+{
+ line := hitbat.line;
+ batlen := hitbat.s2 - hitbat.s1;
+
+ cvsorigin := Point(int cmd(win, ".c cget -actx"), int cmd(win, ".c cget -acty"));
+
+# cmd(win, "grab set .c");
+# cmd(win, "focus .");
+loop: for (;;) alt {
+ (buts, p) := <-mch =>
+ if (buts & 2)
+ (buts, p) = aim(mch, hitbat, p);
+ (v, perp, dist) := line.hittest(p);
+ dist -= offset;
+ # constrain bat and mouse positions
+ if (dist < 0.0 || dist + batlen > line.s) {
+ if (dist < 0.0) {
+ p = line.point(offset);
+ dist = 1.0;
+ } else {
+ p = line.point(line.s - batlen + offset);
+ dist = line.s - batlen;
+ }
+ p.x -= int (v.x * perp);
+ p.y -= int (v.y * perp);
+ win.image.display.cursorset(p.add(cvsorigin));
+ }
+ (hitbat.s1, hitbat.s2) = (dist, dist + batlen);
+ hitbat.config();
+ cmd(win, "update");
+ if (!buts)
+ break loop;
+ }
+# cmd(win, "grab release .c");
+}
+
+CHARGETIME: con 1000.0;
+MAXCHARGE: con 50.0;
+
+α: con 0.999; # decay in one millisecond
+Max: con 60.0;
+D: con 5;
+ZERO: con 1e-6;
+aim(mch: chan of (int, Point), hitbat: ref Obstacle, p: Point): (int, Point)
+{
+ cvsorigin := Point(int cmd(win, ".c cget -actx"), int cmd(win, ".c cget -acty"));
+ startms := ms := sys->millisec();
+ delta := Realpoint(0.0, 0.0);
+ line := hitbat.line;
+ charge := 0.0;
+ pivot := line.point((hitbat.s1 + hitbat.s2) / 2.0);
+ s1 := p2s(line.point(hitbat.s1));
+ s2 := p2s(line.point(hitbat.s2));
+ cmd(win, ".c create line 0 0 0 0 -tags wire");
+ cmd(win, ".c create oval 0 0 1 1 -fill green -tags ball");
+ p2: Point;
+ buts := 2;
+ for (;;) {
+ v := makeunit(delta);
+ bp := pivot.add((int (v.x * charge), int (v.y * charge)));
+ cmd(win, ".c coords wire "+s1+" "+p2s(bp)+" "+s2);
+ cmd(win, ".c coords ball "+string (bp.x - D) + " " + string (bp.y - D) + " " +
+ string (bp.x + D) + " " + string (bp.y + D));
+ cmd(win, "update");
+ if ((buts & 2) == 0)
+ break;
+ (buts, p2) = <-mch;
+ now := sys->millisec();
+ fade := math->pow(α, real (now - ms));
+ charge = real (now - startms) * (MAXCHARGE / CHARGETIME);
+ if (charge > MAXCHARGE)
+ charge = MAXCHARGE;
+ ms = now;
+ delta.x = delta.x * fade + real (p2.x - p.x);
+ delta.y = delta.y * fade + real (p2.y - p.y);
+ mag := delta.x * delta.x + delta.y * delta.y;
+ win.image.display.cursorset(p.add(cvsorigin));
+ }
+ sys->print("pow\n");
+ cmd(win, ".c delete wire ball");
+ cmd(win, "update");
+ return (buts, p2);
+}
+
+makeunit(v: Realpoint): Realpoint
+{
+ mag := math->sqrt(v.x * v.x + v.y * v.y);
+ if (mag < ZERO)
+ return (1.0, 0.0);
+ return (v.x / mag, v.y / mag);
+}
+
+#drag(mch: chan of (int, Point), p: Point)
+#{
+# down := 1;
+# cvsorigin := Point(int cmd(win, ".c cget -actx"), int cmd(win, ".c cget -acty"));
+# ms := sys->millisec();
+# delta := Realpoint(0.0, 0.0);
+# id := cmd(win, ".c create line " + p2s(p) + " " + p2s(p));
+# coords := ".c coords " + id + " " + p2s(p) + " ";
+# do {
+# p2: Point;
+# (down, p2) = <-mch;
+# now := sys->millisec();
+# fade := math->pow(α, real (now - ms));
+# ms = now;
+# delta.x = delta.x * fade + real (p2.x - p.x);
+# delta.y = delta.y * fade + real (p2.y - p.y);
+# mag := delta.x * delta.x + delta.y * delta.y;
+# d: Realpoint;
+# if (mag > Max * Max) {
+# fade = Max / math->sqrt(mag);
+# d = (delta.x * fade, delta.y * fade);
+# } else
+# d = delta;
+#
+# cmd(win, coords + p2s(p.add((int d.x, int d.y))));
+# win.image.display.cursorset(p.add(cvsorigin));
+# cmd(win, "update");
+# } while (down);
+#}
+#
+Line.new(p1, p2: Point): ref Line
+{
+ ln := ref Line;
+ ln.p = (real p1.x, real p1.y);
+ v := Realpoint(real (p2.x - p1.x), real (p2.y - p1.y));
+ ln.s = math->sqrt(v.x * v.x + v.y * v.y);
+ if (ln.s > ZERO)
+ ln.v = (v.x / ln.s, v.y / ln.s);
+ else
+ ln.v = (1.0, 0.0);
+ return ln;
+}
+
+# return normal from line, perpendicular distance from line and distance down line
+Line.hittest(l: self ref Line, ip: Point): (Realpoint, real, real)
+{
+ p := Realpoint(real ip.x, real ip.y);
+ v := Realpoint(-l.v.y, l.v.x);
+ (nil, nil, perp, ldist) := l.intersection(p, v);
+ return (v, perp, ldist);
+}
+
+Line.point(l: self ref Line, s: real): Point
+{
+ return (int (l.p.x + s * l.v.x), int (l.p.y + s * l.v.y));
+}
+
+# compute the intersection of lines a and b.
+# b is assumed to be fixed, and a is indefinitely long
+# but doesn't extend backwards from its starting point.
+# a is defined by the starting point p and the unit vector v.
+# return whether it hit, the point at which it hit if so,
+# the distance of the intersection point from p,
+# and the distance of the intersection point from b.p.
+Line.intersection(b: self ref Line, p, v: Realpoint): (int, Realpoint, real, real)
+{
+ det := b.v.x * v.y - v.x * b.v.y;
+ if (det > -ZERO && det < ZERO)
+ return (0, (0.0, 0.0), 0.0, 0.0);
+
+ y21 := b.p.y - p.y;
+ x21 := b.p.x - p.x;
+ s := (b.v.x * y21 - b.v.y * x21) / det;
+ t := (v.x * y21 - v.y * x21) / det;
+ if (s < 0.0)
+ return (0, (0.0, 0.0), s, t);
+ hit := t >= 0.0 && t <= b.s;
+ hp: Realpoint;
+ if (hit)
+ hp = (p.x+v.x*s, p.y+v.y*s);
+ return (hit, hp, s, t);
+}
+
+blankobstacle: Obstacle;
+Obstacle.new(id: int): ref Obstacle
+{
+ cmd(win, ".c create line 0 0 0 0 -width 3 -fill #aaaaaa" + " -tags l" + string id);
+ o := ref blankobstacle;
+ o.line = Line.new((0, 0), (0, 0));
+ o.id = id;
+ return o;
+}
+
+Obstacle.config(o: self ref Obstacle)
+{
+ cmd(win, ".c coords l" + string o.id + " " +
+ p2s(o.line.point(o.s1)) + " " + p2s(o.line.point(o.s2)));
+ cmd(win, ".c itemconfigure l" + string o.id + " -fill red");
+}
+
+abs(x: real): real
+{
+ if (x < 0.0)
+ return -x;
+ return x;
+}