ginger/gim/geo/geo.go

143 lines
3.0 KiB
Go

// Package geo implements basic geometric concepts used by gim
package geo
import "math"
// XY describes a 2-dimensional position or vector. The origin of the
// 2-dimensional space is a 0,0, with the x-axis going to the left and the
// y-axis going down.
type XY [2]int
// Zero is the zero point, or a zero vector, depending on what you're doing
var Zero = XY{0, 0}
// Unit vectors
var (
Up = XY{0, -1}
Down = XY{0, 1}
Left = XY{-1, 0}
Right = XY{1, 0}
)
// Units is the set of unit vectors
var Units = []XY{
Up,
Down,
Left,
Right,
}
func (xy XY) toF64() [2]float64 {
return [2]float64{
float64(xy[0]),
float64(xy[1]),
}
}
func abs(i int) int {
if i < 0 {
return i * -1
}
return i
}
// Abs returns the XY with all fields made positive, if they weren't already
func (xy XY) Abs() XY {
return XY{abs(xy[0]), abs(xy[1])}
}
// Unit returns the XY with each field divided by its absolute value (i.e.
// scaled down to 1 or -1). Fields which are 0 are left alone
func (xy XY) Unit() XY {
for i := range xy {
if xy[i] > 0 {
xy[i] = 1
} else if xy[i] < 0 {
xy[i] = -1
}
}
return xy
}
// Len returns the length (aka magnitude) of the XY as a vector, using the
// Rounder to round to an int
func (xy XY) Len(r Rounder) int {
if xy[0] == 0 {
return abs(xy[1])
} else if xy[1] == 0 {
return abs(xy[0])
}
xyf := xy.toF64()
lf := math.Sqrt((xyf[0] * xyf[0]) + (xyf[1] * xyf[1]))
return r.Round(lf)
}
// Add returns the result of adding the two XYs' fields individually
func (xy XY) Add(xy2 XY) XY {
xy[0] += xy2[0]
xy[1] += xy2[1]
return xy
}
// Mul returns the result of multiplying the two XYs' fields individually
func (xy XY) Mul(xy2 XY) XY {
xy[0] *= xy2[0]
xy[1] *= xy2[1]
return xy
}
// Div returns the results of dividing the two XYs' field individually, using
// the Rounder to resolve floating results
func (xy XY) Div(xy2 XY, r Rounder) XY {
xyf, xy2f := xy.toF64(), xy2.toF64()
return XY{
r.Round(xyf[0] / xy2f[0]),
r.Round(xyf[1] / xy2f[1]),
}
}
// Scale returns the result of multiplying both of the XY's fields by the scalar
func (xy XY) Scale(scalar int) XY {
return xy.Mul(XY{scalar, scalar})
}
// Inv inverses the XY, a shortcut for xy.Scale(-1)
func (xy XY) Inv() XY {
return xy.Scale(-1)
}
// Sub subtracts xy2 from xy and returns the result. A shortcut for
// xy.Add(xy2.Inv())
func (xy XY) Sub(xy2 XY) XY {
return xy.Add(xy2.Inv())
}
// Midpoint returns the midpoint between the two XYs. The rounder indicates what
// to do about non-whole values when they're come across
func (xy XY) Midpoint(xy2 XY, r Rounder) XY {
return xy.Add(xy2.Sub(xy).Div(XY{2, 2}, r))
}
// Min returns an XY whose fields are the minimum values of the two XYs'
// fields compared individually
func (xy XY) Min(xy2 XY) XY {
for i := range xy {
if xy2[i] < xy[i] {
xy[i] = xy2[i]
}
}
return xy
}
// Max returns an XY whose fields are the Maximum values of the two XYs'
// fields compared individually
func (xy XY) Max(xy2 XY) XY {
for i := range xy {
if xy2[i] > xy[i] {
xy[i] = xy2[i]
}
}
return xy
}