DelayNoMore/resolv_tailored/vector.go

282 lines
5.3 KiB
Go
Raw Normal View History

package resolv
import (
"math"
)
// Vector is the definition of a row vector that contains scalars as
// 64 bit floats
type Vector []float64
// Axis is an integer enum type that describes vector axis
type Axis int
const (
// the consts below are used to represent vector axis, they are useful
// to lookup values within the vector.
X Axis = iota
Y
Z
)
// Clone a vector
func Clone(v Vector) Vector {
return v.Clone()
}
// Clone a vector
func (v Vector) Clone() Vector {
clone := make(Vector, len(v))
copy(clone, v)
return clone
}
// Add a vector with a vector or a set of vectors
func Add(v1 Vector, vs ...Vector) Vector {
return v1.Clone().Add(vs...)
}
// Add a vector with a vector or a set of vectors
func (v Vector) Add(vs ...Vector) Vector {
dim := len(v)
for i := range vs {
if len(vs[i]) > dim {
axpyUnitaryTo(v, 1, v, vs[i][:dim])
} else {
axpyUnitaryTo(v, 1, v, vs[i])
}
}
return v
}
// Sub subtracts a vector with another vector or a set of vectors
func Sub(v1 Vector, vs ...Vector) Vector {
return v1.Clone().Sub(vs...)
}
// Sub subtracts a vector with another vector or a set of vectors
func (v Vector) Sub(vs ...Vector) Vector {
dim := len(v)
for i := range vs {
if len(vs[i]) > dim {
axpyUnitaryTo(v, -1, vs[i][:dim], v)
} else {
axpyUnitaryTo(v, -1, vs[i], v)
}
}
return v
}
// Scale vector with a given size
func Scale(v Vector, size float64) Vector {
return v.Clone().Scale(size)
}
// Scale vector with a given size
func (v Vector) Scale(size float64) Vector {
scalUnitaryTo(v, size, v)
return v
}
// Equal compares that two vectors are equal to each other
func Equal(v1, v2 Vector) bool {
return v1.Equal(v2)
}
// Equal compares that two vectors are equal to each other
func (v Vector) Equal(v2 Vector) bool {
if len(v) != len(v2) {
return false
}
for i := range v {
if math.Abs(v[i]-v2[i]) > 1e-8 {
return false
}
}
return true
}
// Magnitude of a vector
func Magnitude(v Vector) float64 {
return v.Magnitude()
}
// Magnitude of a vector
func (v Vector) Magnitude() float64 {
return math.Sqrt(v.Magnitude2())
}
func (v Vector) Magnitude2() float64 {
var result float64
for _, scalar := range v {
result += scalar * scalar
}
return result
}
// Unit returns a direction vector with the length of one.
func Unit(v Vector) Vector {
return v.Clone().Unit()
}
// Unit returns a direction vector with the length of one.
func (v Vector) Unit() Vector {
l := v.Magnitude()
if l < 1e-8 {
return v
}
for i := range v {
v[i] = v[i] / l
}
return v
}
// Dot product of two vectors
func Dot(v1, v2 Vector) float64 {
result, dim1, dim2 := 0., len(v1), len(v2)
if dim1 > dim2 {
v2 = append(v2, make(Vector, dim1-dim2)...)
}
if dim1 < dim2 {
v1 = append(v1, make(Vector, dim2-dim1)...)
}
for i := range v1 {
result += v1[i] * v2[i]
}
return result
}
// Dot product of two vectors
func (v Vector) Dot(v2 Vector) float64 {
return Dot(v, v2)
}
// Cross product of two vectors
func Cross(v1, v2 Vector) Vector {
return v1.Cross(v2)
}
// Cross product of two vectors
func (v Vector) Cross(v2 Vector) Vector {
if len(v) != 3 || len(v2) != 3 {
return nil
}
return Vector{
v[Y]*v2[Z] - v[Z]*v2[Y],
v[Z]*v2[X] - v[X]*v2[Z],
v[X]*v2[Z] - v[Z]*v2[X],
}
}
// Rotate is rotating a vector around a specified axis.
// If no axis are specified, it will default to the Z axis.
//
// If a vector with more than 3-dimensions is rotated, it will cut the extra
// dimensions and return a 3-dimensional vector.
//
// NOTE: the ...Axis is just syntactic sugar that allows the axis to not be
// specified and default to Z, if multiple axis is passed the first will be
// set as the rotation axis
func Rotate(v Vector, angle float64, as ...Axis) Vector {
return v.Clone().Rotate(angle, as...)
}
// Rotate is rotating a vector around a specified axis.
// If no axis are specified, it will default to the Z axis.
//
// If a vector with more than 3-dimensions is rotated, it will cut the extra
// dimensions and return a 3-dimensional vector.
//
// NOTE: the ...Axis is just syntactic sugar that allows the axis to not be
// specified and default to Z, if multiple axis is passed the first will be
// set as the rotation axis
func (v Vector) Rotate(angle float64, as ...Axis) Vector {
axis, dim := Z, len(v)
if dim == 0 {
return v
}
if len(as) > 0 {
axis = as[0]
}
if dim == 1 && axis != Z {
v = append(v, 0, 0)
}
if (dim < 2 && axis == Z) || (dim == 2 && axis != Z) {
v = append(v, 0)
}
x, y := v[X], v[Y]
cos, sin := math.Cos(angle), math.Sin(angle)
switch axis {
case X:
z := v[Z]
v[Y] = y*cos - z*sin
v[Z] = y*sin + z*cos
case Y:
z := v[Z]
v[X] = x*cos + z*sin
v[Z] = -x*sin + z*cos
case Z:
v[X] = x*cos - y*sin
v[Y] = x*sin + y*cos
}
if dim > 3 {
return v[:3]
}
return v
}
// X is corresponding to doing a v[0] lookup, if index 0 does not exist yet, a
// 0 will be returned instead
func (v Vector) X() float64 {
if len(v) < 1 {
return 0.
}
return v[X]
}
// Y is corresponding to doing a v[1] lookup, if index 1 does not exist yet, a
// 0 will be returned instead
func (v Vector) Y() float64 {
if len(v) < 2 {
return 0.
}
return v[Y]
}
// Z is corresponding to doing a v[2] lookup, if index 2 does not exist yet, a
// 0 will be returned instead
func (v Vector) Z() float64 {
if len(v) < 3 {
return 0.
}
return v[Z]
}