vec2.gno
1.71 Kb · 58 lines
1// Package physics is a 2D rolling-ball engine: it answers where a ball goes,
2// never what that means.
3//
4// +X is right, +Y is down, 1.0 is one board unit, angles are in radians.
5// Deterministic: float64 only, no map iteration, no clock, no randomness.
6//
7// Inputs must be finite: a NaN or an infinity gives no panic and no endless
8// loop, but no meaningful answer either. Callers check them.
9package physics
10
11import "math"
12
13// Vec2 is a point or a displacement on the board, in board units.
14type Vec2 struct{ X, Y float64 }
15
16// V is Vec2{x, y}, short so a course's coordinates read as coordinates.
17func V(x, y float64) Vec2 { return Vec2{X: x, Y: y} }
18
19// Add is a+b.
20func (a Vec2) Add(b Vec2) Vec2 { return Vec2{a.X + b.X, a.Y + b.Y} }
21
22// Sub is a-b.
23func (a Vec2) Sub(b Vec2) Vec2 { return Vec2{a.X - b.X, a.Y - b.Y} }
24
25// Scale is a times s.
26func (a Vec2) Scale(s float64) Vec2 { return Vec2{a.X * s, a.Y * s} }
27
28// Dot is the dot product of a and b.
29func (a Vec2) Dot(b Vec2) float64 { return a.X*b.X + a.Y*b.Y }
30
31// Len is the length of a.
32func (a Vec2) Len() float64 { return math.Sqrt(a.Dot(a)) }
33
34// LenCmp compares a.Len() with r: -1 shorter, 0 equal, 1 longer, exactly as
35// comparing a.Len() would. It takes the costly square root only within 1e-12
36// of r², where a.Dot(a) alone could round the other way. For board-scale r.
37func (a Vec2) LenCmp(r float64) int {
38 d2, r2 := a.Dot(a), r*r
39 switch {
40 case r < 0 || d2 > r2*(1+1e-12):
41 return 1
42 case d2 < r2*(1-1e-12):
43 return -1
44 }
45 l := math.Sqrt(d2)
46 if l < r {
47 return -1
48 }
49 if l > r {
50 return 1
51 }
52 return 0
53}
54
55// FromPolar builds a vector of length r pointing at angle (radians).
56func FromPolar(angle, r float64) Vec2 {
57 return Vec2{r * math.Cos(angle), r * math.Sin(angle)}
58}