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;
}
|