Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

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}