From 3a5b7a88cb62ad243f99e3a384f04351761d222a Mon Sep 17 00:00:00 2001 From: Frederik Kvartborg Albertsen Date: Tue, 23 Nov 2021 22:23:52 +0100 Subject: [PATCH] Update readme to reflect current api (#18) * Update readme to reflect current API Update docstrings and rename UnsafeVector to MutableVector. * Remove build link --- README.md | 37 +++----------- doc.go | 4 +- mutable_vector.go | 128 ++++++++++++++++++++++++++++++++++++++++++++++ unsafe_vector.go | 122 ------------------------------------------- 4 files changed, 138 insertions(+), 153 deletions(-) create mode 100644 mutable_vector.go delete mode 100644 unsafe_vector.go diff --git a/README.md b/README.md index 1d9cbe5..02f5088 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # vector [![Version](https://img.shields.io/github/release/kvartborg/vector.svg)](https://github.com/kvartborg/vector/releases) -[![Build Status](https://travis-ci.org/kvartborg/vector.svg?branch=master)](https://travis-ci.org/kvartborg/vector) [![GoDoc](https://godoc.org/github.com/kvartborg/vector?status.svg)](https://pkg.go.dev/github.com/kvartborg/vector?tab=doc) [![Go Report Card](https://goreportcard.com/badge/github.com/kvartborg/vector)](https://goreportcard.com/report/github.com/kvartborg/vector) @@ -42,7 +41,7 @@ result := vec{1, 2}.Add(vec{2, 4}) ``` A nice side effect of representing a vector as a list of `float64` values is that -we easily can turn a slice of `float64` into a vector by using type casting. +a slice of `float64` values can easily be turned into a vector by using type casting. This elimitates the need for any constructor functions for the vector type. ```go // Turn a list of floats into a vector @@ -50,39 +49,19 @@ v := vec([]float64{1, 2, 3}) ``` ### Mutability vs Immutability -Most of the arithmetic operations provided by this package has a mutable and immutable implementation. -The way this is separated is that all package level functions are immutable -and methods called on the vector it self are mutable on the calling vector. +All arithmetic operations are immutable by default. But if needed a `Vector` can be +turned into a `MutableVector` with the `vector.In` function, see example below. +A mutable vector performs arithemetic operations much faster without taking up +any memory. ```go // create vectors v1, v2 := vec{1, 2}, vec{2, 4} -// Immutable addition, returns a new vector -result := vector.Add(v1, v2) - -// Mutable addition, will do the calculation in place in the v1 vector +// Immutable addition, will return a new vector containing the result. result := v1.Add(v2) -``` - -The mutable implementation is a lot faster and uses less memory compared to the immutable, this is because the calculation is done in place. -Mutable operations shall be used with care, because it can lead to bugs that can be hard to spot. A use case i find useful is to use the mutable operations when you are inlining the instantiation of the vectors. - -```go -// example of safe usage of mutable operations when inlining vector instantiation. -result := vec{1, 2}.Add(vec{2, 4}) -``` - -Or if you are creating a receiving vector where the calculation can be done in place. - -```go -// Create result vector where the calculation can be done in place. -result := make(vec, 2) -// Create vectors -v1, v2 := vec{1, 2}, vec{2, 4} - -// safe usage of mutable operation when called on a result vector -result.Add(v1, v2) +// Mutable addition, will do the calculation in place in the v1 vector +vector.In(v1).Add(v2) ``` ### Slicing a vector diff --git a/doc.go b/doc.go index ada2a29..d750eb3 100644 --- a/doc.go +++ b/doc.go @@ -10,6 +10,6 @@ // // Create a vector from a list of float64 values // v2 := vec([]float64{2, 6}) // -// // Do operations with the vectors -// result := vector.Add(v1, v2) +// // Do arithmetic operations with the vectors +// result := v1.Add(v2) package vector diff --git a/mutable_vector.go b/mutable_vector.go new file mode 100644 index 0000000..1fb32cc --- /dev/null +++ b/mutable_vector.go @@ -0,0 +1,128 @@ +package vector + +// MutableVector is a vector where all arithmetic operations will be done in +// place on the calling vector. This will increase performance and minimize the +// memory consumption. +type MutableVector []float64 + +// In takes a vector and turns it into a mutable vector. +func In(a Vector) MutableVector { + return MutableVector(a) +} + +// Clone a mutable vector. +func (a MutableVector) Clone() MutableVector { + return clone(a) +} + +// Add a vector in place in the mutable vector +func (a MutableVector) Add(b Vector) MutableVector { + return add(a, b) +} + +// Sum a vector with a vector or a set of vectors +func (a MutableVector) Sum(vectors ...Vector) MutableVector { + return sum(a, vectors) +} + +// Sub subtracts a vector with another vector or a set of vectors +func (a MutableVector) Sub(b Vector) MutableVector { + return sub(a, b) +} + +// Invert inverts the vector, and then returns it +func (a MutableVector) Invert() MutableVector { + return invert(a) +} + +// Scale vector with a given size +func (a MutableVector) Scale(size float64) MutableVector { + return scale(a, size) +} + +// Equal compares that two vectors are equal to each other +func (a MutableVector) Equal(b Vector) bool { + return equal(a, b) +} + +// Magnitude of a vector +func (a MutableVector) Magnitude() float64 { + return magnitude(a) +} + +// Unit returns a direction vector with the length of one. +func (a MutableVector) Unit() MutableVector { + return unit(a) +} + +// Dot product of two vectors +func (a MutableVector) Dot(b Vector) float64 { + return dot(a, b) +} + +// Cross product of two vectors +func (a MutableVector) Cross(b Vector) (Vector, error) { + return cross(a, b) +} + +// Rotate is rotating a vector around an abitrary vector axis +// If no axis are specified it will default to rotate around 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 ...MutableVector is just syntactic sugar that allows the vector axis to not be +// specified and default to the Z axis, if multiple axis is passed the first will be +// set as the rotational axis +func (a MutableVector) Rotate(angle float64, axis ...Vector) MutableVector { + as := Z + + if len(axis) > 0 { + as = axis[0] + } + + return rotate(a, angle, clone(as)) +} + +// Angle returns the angle in radians from the first MutableVector to the second, and an error if the two MutableVectors +// aren't of equal dimensions (length). For 0-dimension MutableVectors, the returned angle is 0. For 1-dimension MutableVectors, +// the angle is Pi if the second MutableVector's coordinate is less than the first MutableVector's coordinate, and 0 otherwise. +func (a MutableVector) Angle(axis ...Vector) float64 { + as := X + + if len(axis) > 0 { + as = axis[0] + } + + return angle(a, as) +} + +// X is corresponding to doing a MutableVector[0] lookup, if index 0 does not exist yet, a +// 0 will be returned instead +func (a MutableVector) X() float64 { + if len(a) < 1 { + return 0 + } + + return a[x] +} + +// Y is corresponding to doing a MutableVector[1] lookup, if index 1 does not exist yet, a +// 0 will be returned instead +func (a MutableVector) Y() float64 { + if len(a) < 2 { + return 0 + } + + return a[y] +} + +// Z is corresponding to doing a MutableVector[2] lookup, if index 2 does not exist yet, a +// 0 will be returned instead +func (a MutableVector) Z() float64 { + if len(a) < 3 { + return 0 + } + + return a[z] +} diff --git a/unsafe_vector.go b/unsafe_vector.go deleted file mode 100644 index f7dd2e4..0000000 --- a/unsafe_vector.go +++ /dev/null @@ -1,122 +0,0 @@ -package vector - -type UnsafeVector []float64 - -func In(a Vector) UnsafeVector { - return UnsafeVector(a) -} - -func (a UnsafeVector) Clone() UnsafeVector { - return clone(a) -} - -func (a UnsafeVector) Add(b Vector) UnsafeVector { - return add(a, b) -} - -// Sum a vector with a vector or a set of vectors -func (a UnsafeVector) Sum(vectors ...Vector) UnsafeVector { - return sum(a, vectors) -} - -// Sub subtracts a vector with another vector or a set of vectors -func (a UnsafeVector) Sub(b Vector) UnsafeVector { - return sub(a, b) -} - -// Invert inverts the vector, and then returns it -func (a UnsafeVector) Invert() UnsafeVector { - return invert(a) -} - -// Scale vector with a given size -func (a UnsafeVector) Scale(size float64) UnsafeVector { - return scale(a, size) -} - -// Equal compares that two vectors are equal to each other -func (a UnsafeVector) Equal(b Vector) bool { - return equal(a, b) -} - -// Magnitude of a vector -func (a UnsafeVector) Magnitude() float64 { - return magnitude(a) -} - -// Unit returns a direction vector with the length of one. -func (a UnsafeVector) Unit() UnsafeVector { - return unit(a) -} - -// Dot product of two vectors -func (a UnsafeVector) Dot(b Vector) float64 { - return dot(a, b) -} - -// Cross product of two vectors -func (a UnsafeVector) Cross(b Vector) (Vector, error) { - return cross(a, b) -} - -// Rotate is rotating a vector around an abitrary vector axis -// If no axis are specified it will default to rotate around 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 ...UnsafeVector is just syntactic sugar that allows the vector axis to not be -// specified and default to the Z axis, if multiple axis is passed the first will be -// set as the rotational axis -func (a UnsafeVector) Rotate(angle float64, axis ...Vector) UnsafeVector { - as := Z - - if len(axis) > 0 { - as = axis[0] - } - - return rotate(a, angle, clone(as)) -} - -// Angle returns the angle in radians from the first UnsafeVector to the second, and an error if the two UnsafeVectors -// aren't of equal dimensions (length). For 0-dimension UnsafeVectors, the returned angle is 0. For 1-dimension UnsafeVectors, -// the angle is Pi if the second UnsafeVector's coordinate is less than the first UnsafeVector's coordinate, and 0 otherwise. -func (a UnsafeVector) Angle(axis ...Vector) float64 { - as := X - - if len(axis) > 0 { - as = axis[0] - } - - return angle(a, as) -} - -// X is corresponding to doing a UnsafeVector[0] lookup, if index 0 does not exist yet, a -// 0 will be returned instead -func (a UnsafeVector) X() float64 { - if len(a) < 1 { - return 0 - } - - return a[x] -} - -// Y is corresponding to doing a UnsafeVector[1] lookup, if index 1 does not exist yet, a -// 0 will be returned instead -func (a UnsafeVector) Y() float64 { - if len(a) < 2 { - return 0 - } - - return a[y] -} - -// Z is corresponding to doing a UnsafeVector[2] lookup, if index 2 does not exist yet, a -// 0 will be returned instead -func (a UnsafeVector) Z() float64 { - if len(a) < 3 { - return 0 - } - - return a[z] -}