diff --git a/vector2/vector2.go b/vector2/vector2.go index 47ad3b7..ee0c0ee 100644 --- a/vector2/vector2.go +++ b/vector2/vector2.go @@ -89,6 +89,14 @@ func Lerp[T vector.Number](a, b Vector[T], t float64) Vector[T] { } } +func LerpClamped[T vector.Number](a, b Vector[T], t float64) Vector[T] { + tClean := vector.Clamp(t, 0, 1) + return Vector[T]{ + x: T((float64(b.x-a.x) * tClean) + float64(a.x)), + y: T((float64(b.y-a.y) * tClean) + float64(a.y)), + } +} + func Min[T vector.Number](a, b Vector[T]) Vector[T] { return New( T(math.Min(float64(a.x), float64(b.x))), @@ -149,6 +157,14 @@ func FromArray[T vector.Number](data []T) Vector[T] { return v } +func (v Vector[T]) ToArr() []T { + return []T{v.x, v.y} +} + +func (v Vector[T]) ToFixedArr() [2]T { + return [2]T{v.x, v.y} +} + func Rand(r *rand.Rand) Vector[float64] { return Vector[float64]{ x: r.Float64(), diff --git a/vector2/vector2_test.go b/vector2/vector2_test.go index 7273f06..df5e9d5 100644 --- a/vector2/vector2_test.go +++ b/vector2/vector2_test.go @@ -94,6 +94,28 @@ func TestOperations(t *testing.T) { } } +func TestLerpClamped(t *testing.T) { + tests := map[string]struct { + left vector2.Float64 + right vector2.Float64 + t float64 + want vector2.Float64 + }{ + "(0, 0, 0) =(0)=> (0, 0, 0) = (0, 0, 0)": {left: vector2.New(0., 0.), right: vector2.New(0., 0.), t: 0, want: vector2.New(0., 0.)}, + "(0, 0, 0) =(0.5)=> (1, 2, 3) = (0.5, 1, 1.5)": {left: vector2.New(0., 0.), right: vector2.New(1., 2.), t: 0.5, want: vector2.New(0.5, 1.)}, + "(0, 0, 0) =(1)=> (1, 2, 3) = (1, 2, 3)": {left: vector2.New(0., 0.), right: vector2.New(1., 2.), t: 1, want: vector2.New(1., 2.)}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + got := vector2.LerpClamped(tc.left, tc.right, tc.t) + + assert.InDelta(t, tc.want.X(), got.X(), 0.000001) + assert.InDelta(t, tc.want.Y(), got.Y(), 0.000001) + }) + } +} + func TestAdd(t *testing.T) { tests := map[string]struct { left vector2.Float64 @@ -277,6 +299,20 @@ func TestBadJSON(t *testing.T) { assert.Equal(t, 0., out.Y()) } +func TestToArray(t *testing.T) { + v := vector2.New(1., 2.) + + arr := v.ToArr() + assert.Len(t, arr, 2) + assert.Equal(t, 1., arr[0]) + assert.Equal(t, 2., arr[1]) + + arrFixed := v.ToFixedArr() + assert.Len(t, arrFixed, 2) + assert.Equal(t, 1., arrFixed[0]) + assert.Equal(t, 2., arrFixed[1]) +} + func TestDot(t *testing.T) { a := vector2.New(2, 3) b := vector2.New(6, 7) diff --git a/vector3/vector3.go b/vector3/vector3.go index 9f024ba..82a6d58 100644 --- a/vector3/vector3.go +++ b/vector3/vector3.go @@ -110,6 +110,15 @@ func Lerp[T vector.Number](a, b Vector[T], t float64) Vector[T] { } } +func LerpClamped[T vector.Number](a, b Vector[T], t float64) Vector[T] { + tClean := vector.Clamp(t, 0, 1) + return Vector[T]{ + x: T((float64(b.x-a.x) * tClean) + float64(a.x)), + y: T((float64(b.y-a.y) * tClean) + float64(a.y)), + z: T((float64(b.z-a.z) * tClean) + float64(a.z)), + } +} + func Min[T vector.Number](a, b Vector[T]) Vector[T] { return New( T(math.Min(float64(a.x), float64(b.x))), @@ -189,6 +198,10 @@ func (v Vector[T]) ToArr() []T { return []T{v.x, v.y, v.z} } +func (v Vector[T]) ToFixedArr() [3]T { + return [3]T{v.x, v.y, v.z} +} + func (v Vector[T]) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { X float64 `json:"x"` diff --git a/vector3/vector3_test.go b/vector3/vector3_test.go index 3db2da3..112b314 100644 --- a/vector3/vector3_test.go +++ b/vector3/vector3_test.go @@ -262,6 +262,29 @@ func TestLerp(t *testing.T) { } } +func TestLerpClamped(t *testing.T) { + tests := map[string]struct { + left vector3.Float64 + right vector3.Float64 + t float64 + want vector3.Float64 + }{ + "(0, 0, 0) =(0)=> (0, 0, 0) = (0, 0, 0)": {left: vector3.New(0., 0., 0.), right: vector3.New(0., 0., 0.), t: 0, want: vector3.New(0., 0., 0.)}, + "(0, 0, 0) =(0.5)=> (1, 2, 3) = (0.5, 1, 1.5)": {left: vector3.New(0., 0., 0.), right: vector3.New(1., 2., 3.), t: 0.5, want: vector3.New(0.5, 1., 1.5)}, + "(0, 0, 0) =(1)=> (1, 2, 3) = (1, 2, 3)": {left: vector3.New(0., 0., 0.), right: vector3.New(1., 2., 3.), t: 1, want: vector3.New(1., 2., 3.)}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + got := vector3.LerpClamped(tc.left, tc.right, tc.t) + + assert.InDelta(t, tc.want.X(), got.X(), 0.000001) + assert.InDelta(t, tc.want.Y(), got.Y(), 0.000001) + assert.InDelta(t, tc.want.Z(), got.Z(), 0.000001) + }) + } +} + func TestMin(t *testing.T) { tests := map[string]struct { left vector3.Float64 @@ -348,11 +371,18 @@ func TestScaleVecInt(t *testing.T) { func TestToArray(t *testing.T) { v := vector3.New(1., 2., 3.) + arr := v.ToArr() assert.Len(t, arr, 3) assert.Equal(t, 1., arr[0]) assert.Equal(t, 2., arr[1]) assert.Equal(t, 3., arr[2]) + + arrFixed := v.ToFixedArr() + assert.Len(t, arrFixed, 3) + assert.Equal(t, 1., arrFixed[0]) + assert.Equal(t, 2., arrFixed[1]) + assert.Equal(t, 3., arrFixed[2]) } func TestNearZero(t *testing.T) { diff --git a/vector4/vector4.go b/vector4/vector4.go index 018a14e..deac328 100644 --- a/vector4/vector4.go +++ b/vector4/vector4.go @@ -101,6 +101,16 @@ func Lerp[T vector.Number](a, b Vector[T], t float64) Vector[T] { } } +func LerpClamped[T vector.Number](a, b Vector[T], t float64) Vector[T] { + tClean := vector.Clamp(t, 0, 1) + return Vector[T]{ + x: T((float64(b.x-a.x) * tClean) + float64(a.x)), + y: T((float64(b.y-a.y) * tClean) + float64(a.y)), + z: T((float64(b.z-a.z) * tClean) + float64(a.z)), + w: T((float64(b.w-a.w) * tClean) + float64(a.w)), + } +} + func (v Vector[T]) Scale(t float64) Vector[T] { return Vector[T]{ x: T(float64(v.x) * t), @@ -209,6 +219,14 @@ func FromArray[T vector.Number](data []T) Vector[T] { return v } +func (v Vector[T]) ToArr() []T { + return []T{v.x, v.y, v.z, v.w} +} + +func (v Vector[T]) ToFixedArr() [4]T { + return [4]T{v.x, v.y, v.z, v.w} +} + func (v Vector[T]) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { X float64 `json:"x"` diff --git a/vector4/vector4_test.go b/vector4/vector4_test.go index 51a5ddc..943a09f 100644 --- a/vector4/vector4_test.go +++ b/vector4/vector4_test.go @@ -285,6 +285,63 @@ func TestLerp(t *testing.T) { } } +func TestLerpClamped(t *testing.T) { + tests := map[string]struct { + left vector4.Float64 + right vector4.Float64 + t float64 + want vector4.Float64 + }{ + "(0, 0, 0, 0) =(0)=> (0, 0, 0, 0) = (0, 0, 0, 0)": { + left: vector4.New(0., 0., 0., 0.), + right: vector4.New(0., 0., 0., 0.), + t: 0, + want: vector4.New(0., 0., 0., 0.), + }, + "(0, 0, 0, 0) =(0.5)=> (1, 2, 3, 4) = (0.5, 1, 1.5, 2.0)": { + left: vector4.New(0., 0., 0., 0.), + right: vector4.New(1., 2., 3., 4.), + t: 0.5, + want: vector4.New(0.5, 1., 1.5, 2.), + }, + "(0, 0, 0, 0) =(1)=> (1, 2, 3, 4) = (1, 2, 3, 4)": { + left: vector4.New(0., 0., 0., 0.), + right: vector4.New(1., 2., 3., 4.), + t: 1, + want: vector4.New(1., 2., 3., 4.), + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + got := vector4.LerpClamped(tc.left, tc.right, tc.t) + + assert.InDelta(t, tc.want.X(), got.X(), 0.000001) + assert.InDelta(t, tc.want.Y(), got.Y(), 0.000001) + assert.InDelta(t, tc.want.Z(), got.Z(), 0.000001) + assert.InDelta(t, tc.want.W(), got.W(), 0.000001) + }) + } +} + +func TestToArray(t *testing.T) { + v := vector4.New(1., 2., 3., 4.) + + arr := v.ToArr() + assert.Len(t, arr, 4) + assert.Equal(t, 1., arr[0]) + assert.Equal(t, 2., arr[1]) + assert.Equal(t, 3., arr[2]) + assert.Equal(t, 4., arr[3]) + + arrFixed := v.ToFixedArr() + assert.Len(t, arrFixed, 4) + assert.Equal(t, 1., arrFixed[0]) + assert.Equal(t, 2., arrFixed[1]) + assert.Equal(t, 3., arrFixed[2]) + assert.Equal(t, 4., arrFixed[3]) +} + func TestMin(t *testing.T) { tests := map[string]struct { left vector4.Float64