2020-07-04 08:02:47 -04:00
|
|
|
// Package d2vector is an Implementation of 2-dimensional vectors with big.Float components
|
|
|
|
package d2vector
|
2020-07-03 02:26:59 -04:00
|
|
|
|
|
|
|
import (
|
2020-07-04 19:25:53 -04:00
|
|
|
"fmt"
|
2020-07-03 02:26:59 -04:00
|
|
|
"math"
|
|
|
|
"math/big"
|
|
|
|
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// Epsilon is the threshold for what is `smol enough`
|
|
|
|
epsilon float64 = 0.0001
|
|
|
|
|
|
|
|
// d2precision is how much precision we want from big.Float
|
|
|
|
d2precision uint = 64 // was chosen arbitrarily
|
|
|
|
|
|
|
|
// for convenience in negating sign
|
|
|
|
negative1 float64 = -1.0
|
|
|
|
|
|
|
|
// for convenience
|
|
|
|
zero float64 = 0.0
|
|
|
|
)
|
|
|
|
|
2020-07-04 08:02:47 -04:00
|
|
|
// New creates a new Vector2 and returns a pointer to it.
|
|
|
|
func New(x, y float64) *Vector2 {
|
2020-07-03 02:26:59 -04:00
|
|
|
xbf, ybf := big.NewFloat(x), big.NewFloat(y)
|
|
|
|
xbf.SetPrec(d2precision)
|
|
|
|
ybf.SetPrec(d2precision)
|
|
|
|
result := &Vector2{xbf, ybf}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-07-04 08:02:47 -04:00
|
|
|
// Vector2 has two big.Floats x and y and a set of methods
|
|
|
|
// for common vector operations.
|
2020-07-03 02:26:59 -04:00
|
|
|
type Vector2 struct {
|
|
|
|
x *big.Float
|
|
|
|
y *big.Float
|
|
|
|
}
|
|
|
|
|
|
|
|
// X returns the x member of the Vector
|
|
|
|
func (v *Vector2) X() *big.Float {
|
|
|
|
return v.x
|
|
|
|
}
|
|
|
|
|
|
|
|
// Y returns the y member of the Vector
|
|
|
|
func (v *Vector2) Y() *big.Float {
|
|
|
|
return v.y
|
|
|
|
}
|
|
|
|
|
|
|
|
// Marshal converts the Vector into a slice of bytes
|
|
|
|
func (v *Vector2) Marshal() ([]byte, error) {
|
|
|
|
// TODO not sure how to do this properly
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unmarshal converts a slice of bytes to x/y *big.Float
|
|
|
|
// and assigns them to itself
|
|
|
|
func (v *Vector2) Unmarshal(buf []byte) error {
|
|
|
|
// TODO not sure how to do this properly
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clone creates a copy of this Vector
|
|
|
|
func (v *Vector2) Clone() d2interface.Vector {
|
2020-07-04 19:25:53 -04:00
|
|
|
result := New(0, 0)
|
2020-07-03 02:26:59 -04:00
|
|
|
result.Copy(v)
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy copies the src x/y members to this Vector x/y members
|
|
|
|
func (v *Vector2) Copy(src d2interface.Vector) d2interface.Vector {
|
|
|
|
v.x.Copy(src.X())
|
|
|
|
v.y.Copy(src.Y())
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetFromEntity copies the vector of a world entity
|
|
|
|
// func (v *Vector2) SetFromEntity(entity d2interface.WorldEntity) d2interface.Vector {
|
|
|
|
// return v.Copy(entity.Position())
|
|
|
|
// }
|
|
|
|
|
|
|
|
// Set the x,y members of the Vector
|
|
|
|
func (v *Vector2) Set(x, y *big.Float) d2interface.Vector {
|
|
|
|
v.x = x
|
|
|
|
v.y = y
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetToPolar sets the `x` and `y` values of this object
|
|
|
|
// from a given polar coordinate.
|
|
|
|
func (v *Vector2) SetToPolar(azimuth, radius *big.Float) d2interface.Vector {
|
|
|
|
// HACK we should do this better, with the big.Float
|
|
|
|
a, _ := azimuth.Float64()
|
|
|
|
r, _ := radius.Float64()
|
|
|
|
v.x.SetFloat64(math.Cos(a) * r)
|
|
|
|
v.y.SetFloat64(math.Sin(a) * r)
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Equals check whether this Vector is equal to a given Vector.
|
|
|
|
func (v *Vector2) Equals(src d2interface.Vector) bool {
|
|
|
|
return v.x.Cmp(src.X()) == 0 && v.y.Cmp(src.Y()) == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// FuzzyEquals checks if the Vector is approximately equal
|
|
|
|
// to the given Vector. epsilon is what we consider `smol enough`
|
|
|
|
func (v *Vector2) FuzzyEquals(src d2interface.Vector) bool {
|
|
|
|
smol := big.NewFloat(epsilon)
|
|
|
|
d := v.Distance(src)
|
|
|
|
d.Abs(d)
|
|
|
|
|
|
|
|
return d.Cmp(smol) < 1 || d.Cmp(smol) < 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// Abs returns a clone that is positive
|
|
|
|
func (v *Vector2) Abs() d2interface.Vector {
|
|
|
|
clone := v.Clone()
|
|
|
|
neg1 := big.NewFloat(-1.0)
|
|
|
|
|
|
|
|
if clone.X().Sign() == -1 { // is negative1
|
|
|
|
clone.X().Mul(clone.X(), neg1)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.Y().Sign() == -1 { // is negative1
|
|
|
|
clone.Y().Mul(clone.Y(), neg1)
|
|
|
|
}
|
|
|
|
|
|
|
|
return clone
|
|
|
|
}
|
|
|
|
|
|
|
|
// Angle computes the angle in radians with respect
|
|
|
|
// to the positive x-axis
|
|
|
|
func (v *Vector2) Angle() *big.Float {
|
|
|
|
// HACK we should find a way to do this purely
|
|
|
|
// with big.Float
|
|
|
|
floatX, _ := v.X().Float64()
|
|
|
|
floatY, _ := v.Y().Float64()
|
|
|
|
floatAngle := math.Atan2(floatY, floatX)
|
|
|
|
|
|
|
|
if floatAngle < 0 {
|
|
|
|
floatAngle += 2.0 * math.Pi
|
|
|
|
}
|
|
|
|
|
|
|
|
return big.NewFloat(floatAngle)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetAngle sets the angle of this Vector
|
|
|
|
func (v *Vector2) SetAngle(angle *big.Float) d2interface.Vector {
|
|
|
|
return v.SetToPolar(angle, v.Length())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add to this Vector the components of the given Vector
|
|
|
|
func (v *Vector2) Add(src d2interface.Vector) d2interface.Vector {
|
|
|
|
v.x.Add(v.x, src.X())
|
|
|
|
v.y.Add(v.y, src.Y())
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Subtract from this Vector the components of the given Vector
|
|
|
|
func (v *Vector2) Subtract(src d2interface.Vector) d2interface.Vector {
|
|
|
|
v.x.Sub(v.x, src.X())
|
|
|
|
v.y.Sub(v.y, src.Y())
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Multiply this Vector with the components of the given Vector
|
|
|
|
func (v *Vector2) Multiply(src d2interface.Vector) d2interface.Vector {
|
|
|
|
v.x.Mul(v.x, src.X())
|
|
|
|
v.y.Mul(v.y, src.Y())
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scale this Vector by the given value
|
|
|
|
func (v *Vector2) Scale(s *big.Float) d2interface.Vector {
|
|
|
|
v.x.Sub(v.x, s)
|
|
|
|
v.y.Sub(v.y, s)
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Divide this Vector by the given Vector
|
|
|
|
func (v *Vector2) Divide(src d2interface.Vector) d2interface.Vector {
|
|
|
|
v.x.Quo(v.x, src.X())
|
|
|
|
v.y.Quo(v.y, src.Y())
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Negate thex and y components of this Vector
|
|
|
|
func (v *Vector2) Negate() d2interface.Vector {
|
|
|
|
return v.Scale(big.NewFloat(negative1))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Distance calculate the distance between this Vector and the given Vector
|
|
|
|
func (v *Vector2) Distance(src d2interface.Vector) *big.Float {
|
|
|
|
dist := v.DistanceSq(src)
|
|
|
|
|
|
|
|
return dist.Sqrt(dist)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DistanceSq calculate the distance suared between this Vector and the given
|
|
|
|
// Vector
|
|
|
|
func (v *Vector2) DistanceSq(src d2interface.Vector) *big.Float {
|
|
|
|
delta := src.Clone().Subtract(v)
|
|
|
|
deltaSq := delta.Multiply(delta)
|
|
|
|
|
|
|
|
return big.NewFloat(zero).Add(deltaSq.X(), deltaSq.Y())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Length returns the length of this Vector
|
|
|
|
func (v *Vector2) Length() *big.Float {
|
|
|
|
xsq, ysq := v.LengthSq()
|
|
|
|
|
|
|
|
return xsq.Add(xsq, ysq)
|
|
|
|
}
|
|
|
|
|
2020-07-04 08:02:47 -04:00
|
|
|
// LengthSq returns the x and y values squared
|
2020-07-03 02:26:59 -04:00
|
|
|
func (v *Vector2) LengthSq() (*big.Float, *big.Float) {
|
|
|
|
clone := v.Clone()
|
|
|
|
x, y := clone.X(), clone.Y()
|
|
|
|
|
|
|
|
return x.Mul(x, x), y.Mul(y, y)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetLength sets the length of this Vector
|
|
|
|
func (v *Vector2) SetLength(length *big.Float) d2interface.Vector {
|
|
|
|
return v.Normalize().Scale(length)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Normalize Makes the vector a unit length vector (magnitude of 1) in the same
|
|
|
|
// direction.
|
|
|
|
func (v *Vector2) Normalize() d2interface.Vector {
|
|
|
|
xsq, ysq := v.LengthSq()
|
|
|
|
length := big.NewFloat(zero).Add(xsq, ysq)
|
|
|
|
one := big.NewFloat(1.0)
|
|
|
|
|
|
|
|
if length.Cmp(one) > 0 {
|
|
|
|
length.Quo(one, length.Sqrt(length))
|
|
|
|
|
|
|
|
v.x.Mul(v.x, length)
|
|
|
|
v.y.Mul(v.y, length)
|
|
|
|
}
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// NormalizeRightHand rotate this Vector to its perpendicular,
|
2020-07-04 08:02:47 -04:00
|
|
|
// in the positive direction.
|
2020-07-03 02:26:59 -04:00
|
|
|
func (v *Vector2) NormalizeRightHand() d2interface.Vector {
|
|
|
|
x := v.x
|
|
|
|
v.x = v.y.Mul(v.y, big.NewFloat(negative1))
|
|
|
|
v.y = x
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// NormalizeLeftHand rotate this Vector to its perpendicular,
|
2020-07-04 08:02:47 -04:00
|
|
|
// in the negative1 direction.
|
2020-07-03 02:26:59 -04:00
|
|
|
func (v *Vector2) NormalizeLeftHand() d2interface.Vector {
|
|
|
|
x := v.x
|
|
|
|
v.x = v.y
|
|
|
|
v.y = x.Mul(x, big.NewFloat(negative1))
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2020-07-04 08:02:47 -04:00
|
|
|
// Dot returns the dot product of this Vector and the given Vector.
|
2020-07-03 02:26:59 -04:00
|
|
|
func (v *Vector2) Dot(src d2interface.Vector) *big.Float {
|
|
|
|
c := v.Clone()
|
|
|
|
c.X().Mul(c.X(), src.X())
|
|
|
|
c.Y().Mul(c.Y(), src.Y())
|
|
|
|
|
|
|
|
return c.X().Add(c.X(), c.Y())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cross Calculate the cross product of this Vector and the given Vector.
|
|
|
|
func (v *Vector2) Cross(src d2interface.Vector) *big.Float {
|
|
|
|
c := v.Clone()
|
|
|
|
c.X().Mul(c.X(), src.X())
|
|
|
|
c.Y().Mul(c.Y(), src.Y())
|
|
|
|
|
|
|
|
return c.X().Sub(c.X(), c.Y())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lerp Linearly interpolate between this Vector and the given Vector.
|
|
|
|
func (v *Vector2) Lerp(
|
|
|
|
src d2interface.Vector,
|
|
|
|
t *big.Float,
|
|
|
|
) d2interface.Vector {
|
|
|
|
vc, sc := v.Clone(), src.Clone()
|
|
|
|
x, y := vc.X(), vc.Y()
|
|
|
|
v.x.Set(x.Add(x, t.Mul(t, sc.X().Sub(sc.X(), x))))
|
|
|
|
v.y.Set(y.Add(y, t.Mul(t, sc.Y().Sub(sc.Y(), y))))
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset this Vector the zero vector (0, 0).
|
|
|
|
func (v *Vector2) Reset() d2interface.Vector {
|
|
|
|
v.x.SetFloat64(zero)
|
|
|
|
v.y.SetFloat64(zero)
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Limit the length (or magnitude) of this Vector
|
|
|
|
func (v *Vector2) Limit(max *big.Float) d2interface.Vector {
|
|
|
|
length := v.Length()
|
|
|
|
|
|
|
|
if max.Cmp(length) < 0 {
|
|
|
|
v.Scale(length.Quo(max, length))
|
|
|
|
}
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reflect this Vector off a line defined by a normal.
|
|
|
|
func (v *Vector2) Reflect(normal d2interface.Vector) d2interface.Vector {
|
|
|
|
clone := v.Clone()
|
|
|
|
clone.Normalize()
|
|
|
|
|
|
|
|
two := big.NewFloat(2.0) // there's some matrix algebra magic here
|
|
|
|
dot := v.Clone().Dot(normal)
|
|
|
|
normal.Scale(two.Mul(two, dot))
|
|
|
|
|
|
|
|
return v.Subtract(normal)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mirror reflect this Vector across another.
|
|
|
|
func (v *Vector2) Mirror(axis d2interface.Vector) d2interface.Vector {
|
|
|
|
return v.Reflect(axis).Negate()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rotate this Vector by an angle amount.
|
|
|
|
func (v *Vector2) Rotate(angle *big.Float) d2interface.Vector {
|
|
|
|
// HACK we should do this only with big.Float, not float64
|
|
|
|
// we are throwing away the precision here
|
|
|
|
floatAngle, _ := angle.Float64()
|
|
|
|
cos := math.Cos(floatAngle)
|
|
|
|
sin := math.Sin(floatAngle)
|
|
|
|
|
|
|
|
oldX, _ := v.x.Float64()
|
|
|
|
oldY, _ := v.y.Float64()
|
|
|
|
|
|
|
|
newX := big.NewFloat(cos*oldX - sin*oldY)
|
|
|
|
newY := big.NewFloat(sin*oldX + cos*oldY)
|
|
|
|
|
|
|
|
v.Set(newX, newY)
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
2020-07-04 08:02:47 -04:00
|
|
|
|
2020-07-04 19:25:53 -04:00
|
|
|
// Floor rounds the vector down to the nearest whole numbers.
|
|
|
|
func (v *Vector2) Floor() d2interface.Vector {
|
|
|
|
var xi, yi big.Int
|
|
|
|
v.x.Int(&xi)
|
|
|
|
v.y.Int(&yi)
|
|
|
|
v.X().SetInt(&xi)
|
|
|
|
v.Y().SetInt(&yi)
|
|
|
|
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Vector2) String() string {
|
|
|
|
return fmt.Sprintf("Vector2{%s, %s}", v.x.Text('f', 5), v.y.Text('f', 5))
|
|
|
|
}
|
|
|
|
|
2020-07-04 08:02:47 -04:00
|
|
|
// Up returns a new vector (0, 1)
|
2020-07-04 19:25:53 -04:00
|
|
|
func Up() d2interface.Vector {
|
2020-07-04 08:02:47 -04:00
|
|
|
return New(0, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Down returns a new vector (0, -1)
|
2020-07-04 19:25:53 -04:00
|
|
|
func Down() d2interface.Vector {
|
2020-07-04 08:02:47 -04:00
|
|
|
return New(0, -1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Right returns a new vector (1, 0)
|
2020-07-04 19:25:53 -04:00
|
|
|
func Right() d2interface.Vector {
|
2020-07-04 08:02:47 -04:00
|
|
|
return New(1, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Left returns a new vector (-1, 0)
|
2020-07-04 19:25:53 -04:00
|
|
|
func Left() d2interface.Vector {
|
2020-07-04 08:02:47 -04:00
|
|
|
return New(-1, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// One returns a new vector (1, 1)
|
2020-07-04 19:25:53 -04:00
|
|
|
func One() d2interface.Vector {
|
2020-07-04 08:02:47 -04:00
|
|
|
return New(1, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Zero returns a new vector (0, 0)
|
2020-07-04 19:25:53 -04:00
|
|
|
func Zero() d2interface.Vector {
|
2020-07-04 08:02:47 -04:00
|
|
|
return New(0, 0)
|
|
|
|
}
|