shapes.gno
1.97 Kb · 84 lines
1package physics
2
3import "math"
4
5// Segment is a straight barrier from A to B.
6type Segment struct{ A, B Vec2 }
7
8// Normal is the unit left-normal of the segment.
9func (s Segment) Normal() Vec2 {
10 d := s.B.Sub(s.A)
11 l := d.Len()
12 if l == 0 {
13 return Vec2{}
14 }
15 return Vec2{-d.Y / l, d.X / l}
16}
17
18// Crosses reports whether the motion p0->p1 crosses the segment, without
19// working out the normal (and its square root).
20func (s Segment) Crosses(p0, p1 Vec2) bool {
21 _, _, ok := s.hitN(p0, p1, Vec2{})
22 return ok
23}
24
25// hitN reports where the motion p0->p1 crosses the segment: the travel
26// fraction in [0,1] and the given normal (precomputed, to save a square
27// root per test), turned to face the motion.
28func (s Segment) hitN(p0, p1, normal Vec2) (float64, Vec2, bool) {
29 d := p1.Sub(p0)
30 e := s.B.Sub(s.A)
31 den := d.X*e.Y - d.Y*e.X
32 if math.Abs(den) < 1e-12 {
33 return 0, Vec2{}, false // parallel
34 }
35 f := s.A.Sub(p0)
36 t := (f.X*e.Y - f.Y*e.X) / den // along the motion
37 u := (f.X*d.Y - f.Y*d.X) / den // along the barrier
38 if t < 0 || t > 1 || u < 0 || u > 1 {
39 return 0, Vec2{}, false
40 }
41 n := normal
42 if d.Dot(n) > 0 {
43 n = n.Scale(-1)
44 }
45 return t, n, true
46}
47
48// Circle is a round barrier: a post, a bumper, the rim of a pond.
49type Circle struct {
50 C Vec2
51 R float64
52}
53
54// hit is the swept point-vs-circle test, adding rootWork to *w per square
55// root. A ball that starts inside reports no hit: a post never traps a ball.
56func (c Circle) hit(p0, p1 Vec2, w *int) (float64, Vec2, bool) {
57 d := p1.Sub(p0)
58 a := d.Dot(d)
59 if a < 1e-12 {
60 return 0, Vec2{}, false
61 }
62 m := p0.Sub(c.C)
63 if m.Dot(m) < c.R*c.R {
64 return 0, Vec2{}, false // started inside
65 }
66 b := 2 * m.Dot(d)
67 k := m.Dot(m) - c.R*c.R
68 disc := b*b - 4*a*k
69 if disc < 0 {
70 return 0, Vec2{}, false
71 }
72 *w += rootWork
73 t := (-b - math.Sqrt(disc)) / (2 * a)
74 if t < 0 || t > 1 {
75 return 0, Vec2{}, false
76 }
77 n := p0.Add(d.Scale(t)).Sub(c.C)
78 *w += rootWork
79 l := n.Len()
80 if l == 0 {
81 return 0, Vec2{}, false
82 }
83 return t, n.Scale(1 / l), true
84}