Skip to content

EliCDavis/vector

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

115 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Vector

Coverage Go Report Card GoDoc

Collection of generic, immutable vector math functions I've written overtime for different hobby projects.

API

Function Vector2 Vector3 Vector4 Description
Abs βœ… βœ… βœ… Returns a vector with each component's absolute value
Add βœ… βœ… βœ… Component Wise Addition
Angle βœ… βœ… Returns the angle between two vectors
ToArr βœ… βœ… βœ… Returns a slice containing the vector component data
ToFixedArr βœ… βœ… βœ… Returns a array containing the vector component data
Ceil βœ… βœ… βœ… Ceils each vectors component to the nearest integer
Clamp βœ… βœ… βœ… Clamps each component between two values
ContainsNaN βœ… βœ… βœ… Returns true if any component of the vector is NaN
Cross βœ… Returns the cross product between two vectors
Dot βœ… βœ… βœ… Returns the dot product between two vectors
DivByVector βœ… βœ… βœ… Component wise division
Flip βœ… βœ… βœ… Scales the vector by -1
FlipX βœ… βœ… βœ… Returns a vector with the X component multiplied by -1
FlipY βœ… βœ… βœ… Returns a vector with the Y component multiplied by -1
FlipZ βœ… βœ… Returns a vector with the Z component multiplied by -1
FlipW βœ… Returns a vector with the W component multiplied by -1
Floor βœ… βœ… βœ… Floors each vectors component
Format βœ… βœ… βœ… Build a string with vector data
Length βœ… βœ… βœ… Returns the length of the vector
LengthSquared βœ… βœ… βœ… Returns the squared length of the vector
Max βœ… βœ… βœ… Returns a new vector where each component is the largest value between the two vectors
MaxX βœ… βœ… βœ… Returns the largest X component between the two vectors
MaxY βœ… βœ… βœ… Returns the largest Y component between the two vectors
MaxZ βœ… βœ… Returns the largest Z component between the two vectors
MaxW βœ… Returns the largest W component between the two vectors
MaxComponent βœ… βœ… βœ… Returns the vectors largest component
Midpoint βœ… βœ… βœ… Finds the mid point between two vectors
Min βœ… βœ… βœ… Returns a new vector where each component is the smallest value between the two vectors
MinX βœ… βœ… βœ… Returns the smallest X component between the two vectors
MinY βœ… βœ… βœ… Returns the smallest Y component between the two vectors
MinZ βœ… βœ… Returns the smallest Z component between the two vectors
MinW βœ… Returns the smallest W component between the two vectors
MinComponent βœ… βœ… βœ… Returns the vectors smallest component
MultByVector βœ… βœ… βœ… component wise multiplication, also known as Hadamard product
Normalized βœ… βœ… βœ… Returns the normalized vector
NearZero βœ… βœ… βœ… Returns true if all of the components are near 0
Round βœ… βœ… βœ… Rounds each vectors component to the nearest integer
Scale βœ… βœ… βœ… Scales the vector by some constant
Sqrt βœ… βœ… βœ… Returns a vector with each component's square root
Sub βœ… βœ… βœ… Component Wise Subtraction
Values βœ… βœ… βœ… Returns all components of the vector
X βœ… βœ… βœ… Returns the x component of the vector
Y βœ… βœ… βœ… Returns the y component of the vector
Z βœ… βœ… Returns the z component of the vector
W βœ… Returns the w component of the vector
XY βœ… βœ… Equivalent to vector2.New[T](v.x, v.y)
YZ βœ… βœ… Equivalent to vector2.New[T](v.y, v.z)
XZ βœ… βœ… Equivalent to vector2.New[T](v.x, v.z)
YX βœ… βœ… βœ… Equivalent to vector2.New[T](v.y, v.x)
ZX βœ… βœ… Equivalent to vector2.New[T](v.z, v.x)
ZY βœ… βœ… Equivalent to vector2.New[T](v.z, v.y)
Lerp βœ… βœ… βœ… Interpolates between two vectors by t.
LerpClamped βœ… βœ… βœ… Interpolates between two vectors by t. T is clamped 0 to 1
Log βœ… βœ… βœ… Returns the natural logarithm for each component
Log2 βœ… βœ… βœ… Returns the binary logarithm for each component
Log10 βœ… βœ… βœ… Returns the decimal logarithm for each component
Exp βœ… βœ… βœ… Returns e**x, the base-e exponential for each component
Exp2 βœ… βœ… βœ… Returns 2**x, the base-2 exponential for each component
Expm1 βœ… βœ… βœ… Returns e**x - 1, the base-e exponential for each component minus 1. It is more accurate than Exp(x) - 1 when the component is near zero
Write βœ… βœ… βœ… Write vector component data as binary to io.Writer

Example

Below is an example on how to implement the different sign distance field functions in a generic fashion to work for both int8, int16, int32 int, int64, float32, and float64.

The code below produces this output:

out.gif

package main

import (
	"fmt"
	"image"
	"image/color"
	"image/png"
	"math"
	"os"

	"github.com/EliCDavis/vector"
	"github.com/EliCDavis/vector/vector2"
	"github.com/EliCDavis/vector/vector3"
)

type Field[T vector.Number] func(v vector3.Vector[T]) float64

func Sphere[T vector.Number](pos vector3.Vector[T], radius float64) Field[T] {
	return func(v vector3.Vector[T]) float64 {
		return v.Distance(pos) - radius
	}
}

func Box[T vector.Number](pos vector3.Vector[T], bounds vector3.Vector[T]) Field[T] {
	halfBounds := bounds.Scale(0.5)
	// It's best to watch the video to understand
	// https://www.youtube.com/watch?v=62-pRVZuS5c
	return func(v vector3.Vector[T]) float64 {
		q := v.Sub(pos).Abs().Sub(halfBounds)
		inside := math.Min(float64(q.MaxComponent()), 0)
		return vector3.Max(q, vector3.Zero[T]()).Length() + inside
	}
}

func Union[T vector.Number](fields ...Field[T]) Field[T] {
	return func(v vector3.Vector[T]) float64 {
		min := math.MaxFloat64

		for _, f := range fields {
			fv := f(v)
			if fv < min {
				min = fv
			}
		}

		return min
	}
}

func Intersect[T vector.Number](fields ...Field[T]) Field[T] {
	return func(v vector3.Vector[T]) float64 {
		max := -math.MaxFloat64

		for _, f := range fields {
			fv := f(v)
			if fv > max {
				max = fv
			}
		}

		return max
	}
}

func Subtract[T vector.Number](minuend, subtrahend Field[T]) Field[T] {
	return func(f vector3.Vector[T]) float64 {
		return math.Max(minuend(f), -subtrahend(f))
	}
}

func Translate[T vector.Number](field Field[T], translation vector3.Vector[T]) Field[T] {
	return func(v vector3.Vector[T]) float64 {
		return field(v.Sub(translation))
	}
}

func evaluateAtDepth[T vector.Number](dimension int, field Field[T], depth T, i int) {
	img := image.NewRGBA(image.Rectangle{
		image.Point{0, 0},
		image.Point{dimension, dimension},
	})

	for x := 0; x < dimension; x++ {
		for y := 0; y < dimension; y++ {
			v := field(vector3.New[T](T(x), T(y), depth))
			byteVal := (v / float64(dimension/20)) * 255
			var c color.Color
			if v > 0 {
				c = color.RGBA{R: 0, G: 0, B: byte(byteVal), A: 255}
			} else {
				c = color.RGBA{R: 0, G: byte(-byteVal), B: 0, A: 255}
			}
			img.Set(x, y, c)
		}
	}

	f, err := os.Create(fmt.Sprintf("field_%04d.png", i))
	if err != nil {
		panic(err)
	}
	defer f.Close()

	err = png.Encode(f, img)
	if err != nil {
		panic(err)
	}
}

func main() {
	dimension := 512
	quarterDim := float64(dimension) / 4.

	middleCoord := vector2.
		Fill(dimension).
		Scale(0.5).
		ToFloat64()

	middleCord3D := vector3.New(middleCoord.X(), middleCoord.Y(), 0)

	smallRing := Subtract(
		Sphere(middleCord3D, 100),
		Sphere(middleCord3D, 50),
	)

	field := Intersect(
		Subtract(
			Sphere(middleCord3D, 200),
			Sphere(middleCord3D, 100),
		),
		Union(
			Translate(smallRing, vector3.New(1., 1., 0.).Scale(quarterDim)),
			Translate(smallRing, vector3.New(1., -1., 0.).Scale(quarterDim)),
			Translate(smallRing, vector3.New(-1., 1., 0.).Scale(quarterDim)),
			Translate(smallRing, vector3.New(-1., -1., 0.).Scale(quarterDim)),
			Box(middleCord3D, middleCord3D),
		),
	)

	for i := 0; i < 100; i++ {
		evaluateAtDepth(dimension, field, float64(-dimension/2)+(float64(i*dimension)*0.01), i)
	}
}