-
Notifications
You must be signed in to change notification settings - Fork 3
/
vector.go
113 lines (91 loc) · 3.01 KB
/
vector.go
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
package cs
import (
"fmt"
"math"
)
// A simple 2D vector with handy functions for moving things in space, calculating distance, etc
// Many of these functions were taken from Godot source, thanks Godot folks.
type Vector struct {
X float64 `json:"x"`
Y float64 `json:"y"`
}
func (v Vector) String() string {
return fmt.Sprintf("(%0.0f, %0.0f)", v.X, v.Y)
}
func (v Vector) DistanceSquaredTo(to Vector) float64 {
return (v.X-to.X)*(v.X-to.X) + (v.Y-to.Y)*(v.Y-to.Y)
}
func (v Vector) DistanceTo(to Vector) float64 {
return math.Sqrt((v.X-to.X)*(v.X-to.X) + (v.Y-to.Y)*(v.Y-to.Y))
}
func (minuend Vector) Subtract(subtrahend Vector) Vector {
return Vector{minuend.X - subtrahend.X, minuend.Y - subtrahend.Y}
}
func (v1 Vector) Add(v2 Vector) Vector {
return Vector{v1.X + v2.X, v1.Y + v2.Y}
}
func (v Vector) Scale(scale float64) Vector {
return Vector{v.X * scale, v.Y * scale}
}
func (v Vector) LengthSquared() float64 {
return (v.X * v.X) + (v.Y * v.Y)
}
func (v Vector) Length() float64 {
return math.Sqrt((v.X * v.X) + (v.Y * v.Y))
}
func (v Vector) Normalized() Vector {
lengthsq := v.LengthSquared()
if lengthsq == 0 {
v.X = 0
v.Y = 0
} else {
length := math.Sqrt(lengthsq)
v.X /= length
v.Y /= length
}
return v
}
func (v Vector) Dot(other Vector) float64 {
return v.X*other.X + v.Y*other.Y
}
func (v Vector) Round() Vector {
return Vector{math.Round(v.X), math.Round(v.Y)}
}
// SegmentIntersectsCircle checks whether a segment intersects a circle or not.
// This returns what percent of the segment is NOT in the circle, or -1 if it doesn't
// intersect
// Who would have thought the godot developers would write a perfect function for determining if we collide with a minefield
// while moving through space?
// https://github.com/godotengine/godot/blob/4.1.2-stable/core/math/geometry_2d.h#L217
func segmentIntersectsCircle(segmentFrom, segmentTo, circlePosition Vector, circleRadius float64) float64 {
lineVec := segmentTo.Subtract(segmentFrom)
vecToLine := segmentFrom.Subtract(circlePosition)
// Create a quadratic formula of the form ax^2 + bx + c = 0
var a, b, c float64
a = lineVec.Dot(lineVec)
b = 2 * vecToLine.Dot(lineVec)
c = vecToLine.Dot(vecToLine) - circleRadius*circleRadius
// Solve for t.
sqrtterm := b*b - 4*a*c
// If the term we intend to square root is less than 0 then the answer won't be real,
// so it definitely won't be t in the range 0 to 1.
if sqrtterm < 0 {
return -1
}
// If we can assume that the line segment starts outside the circle (e.g. for continuous time collision detection)
// then the following can be skipped and we can just return the equivalent of res1.
sqrtterm = math.Sqrt(sqrtterm)
res1 := (-b - sqrtterm) / (2 * a)
res2 := (-b + sqrtterm) / (2 * a)
if res1 >= 0 && res1 <= 1 {
return res1
}
if res2 >= 0 && res2 <= 1 {
return res2
}
return -1
}
// Returns true if this point is in a circle
func isPointInCircle(point, circlePosition Vector, circleRadius float64) bool {
return point.DistanceSquaredTo(circlePosition) <= circleRadius*circleRadius
}