summaryrefslogtreecommitdiff
path: root/appl/spree/other/tstboing.b
blob: a599a0abade0ea6d56f6c260a9f9d48c9435f499 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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;
}