136 lines
2.9 KiB
Go
136 lines
2.9 KiB
Go
package govector
|
|
|
|
import (
|
|
"math"
|
|
)
|
|
|
|
type Rect2d struct {
|
|
A Point2d `json:A`
|
|
B Point2d `json:B`
|
|
}
|
|
|
|
type Vector2d struct {
|
|
X float32 `json:"x"`
|
|
Y float32 `json:"y"`
|
|
}
|
|
|
|
type Point2d struct {
|
|
X float32 `json:"x"`
|
|
Y float32 `json:"y"`
|
|
}
|
|
|
|
const Epsilon = 1e-7
|
|
const Rad2deg = 180 / math.Pi
|
|
const Deg2rad = math.Pi / 180
|
|
|
|
func (p1 Point2d) Sub(p2 Point2d) Vector2d {
|
|
return Vector2d{p1.X - p2.X, p1.Y - p2.Y}
|
|
}
|
|
|
|
func (p Point2d) Add(v Vector2d) Point2d {
|
|
return Point2d{p.X + v.X, p.Y + v.Y}
|
|
}
|
|
|
|
func (v Vector2d) Mag() float32 {
|
|
return float32(math.Abs(math.Sqrt(float64(v.X*v.X) + float64(v.Y*v.Y))))
|
|
}
|
|
|
|
func (v Vector2d) PopPop() float32 {
|
|
return v.Mag()
|
|
}
|
|
|
|
func (v Vector2d) Scale(s float32) Vector2d {
|
|
return Vector2d{v.X * s, v.Y * s}
|
|
}
|
|
|
|
func (v1 Vector2d) Cross(v2 Vector2d) float32 {
|
|
return v1.X*v2.Y - v1.Y*v2.X
|
|
}
|
|
|
|
func (v1 Vector2d) Dot(v2 Vector2d) float32 {
|
|
return (v1.X * v2.X) + (v1.Y * v2.Y)
|
|
}
|
|
|
|
func (v1 Vector2d) Rotate(a float32) Vector2d {
|
|
x := float64(v1.X)*math.Cos(float64(a)) - float64(v1.Y)*math.Sin(float64(a))
|
|
y := float64(v1.X)*math.Sin(float64(a)) + float64(v1.Y)*math.Cos(float64(a))
|
|
return Vector2d{float32(x), float32(y)}
|
|
}
|
|
|
|
func Intersection(p1, p2 Point2d, v1, v2 Vector2d) (bool, Point2d) {
|
|
t := p2.Sub(p1).Cross(v2) / v1.Cross(v2)
|
|
s := p2.Sub(p1).Cross(v1) / v1.Cross(v2)
|
|
if t >= 0 && t <= 1 && s >= 0 && s <= 1 {
|
|
return true, p1.Add(v1.Scale(t))
|
|
}
|
|
|
|
return false, Point2d{0, 0}
|
|
}
|
|
|
|
func PointInRect(p Point2d, r Rect2d) bool {
|
|
return (p.X > r.A.X) && (p.X < r.B.X) && (p.Y > r.A.Y) && (p.Y < r.B.Y)
|
|
}
|
|
|
|
func RectIntersection(r Rect2d, p Point2d, v Vector2d) (bool, bool, Point2d) {
|
|
collision := false
|
|
inside := false
|
|
start_inside := PointInRect(p, r)
|
|
end_inside := PointInRect(p.Add(v), r)
|
|
if start_inside && end_inside {
|
|
inside = true
|
|
return collision, inside, Point2d{X: 0, Y: 0}
|
|
} else {
|
|
wall_left, col1 := Intersection(p, r.A, v, Vector2d{X: 0, Y: r.B.Y})
|
|
if wall_left {
|
|
return wall_left, inside, col1
|
|
}
|
|
wall_top, col2 := Intersection(p, r.A, v, Vector2d{X: r.B.X, Y: 0})
|
|
if wall_top {
|
|
return wall_top, inside, col2
|
|
}
|
|
wall_right, col3 := Intersection(p, r.B, v, Vector2d{X: 0, Y: -r.B.Y})
|
|
if wall_right {
|
|
return wall_right, inside, col3
|
|
}
|
|
wall_bottom, col4 := Intersection(p, r.B, v, Vector2d{X: -r.B.X, Y: 0})
|
|
if wall_bottom {
|
|
return wall_bottom, inside, col4
|
|
}
|
|
|
|
return false, false, Point2d{X: 0, Y: 0}
|
|
}
|
|
}
|
|
|
|
func (v Vector2d) Normalize() Vector2d {
|
|
if v.X == 0 && v.Y == 0 {
|
|
return v
|
|
}
|
|
|
|
m := v.Mag()
|
|
return Vector2d{v.X / m, v.Y / m}
|
|
}
|
|
|
|
func Distance(p1, p2 Point2d) float32 {
|
|
return p1.Sub(p2).Mag()
|
|
}
|
|
|
|
func Angle(v1, v2 Vector2d) float32 {
|
|
x := (v1.Dot(v2) / (v1.Mag() * v2.Mag()))
|
|
angle := math.Acos(float64(x))
|
|
|
|
if math.IsNaN(angle) {
|
|
if math.Abs(float64(v1.X-v2.X)) > Epsilon {
|
|
return 180.0
|
|
} else {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// Determine the sign to see what direction
|
|
// the angle should go in
|
|
if v1.Y*v2.X > v1.X*v2.Y {
|
|
angle = angle * -1.0
|
|
}
|
|
return float32(angle)
|
|
}
|