Skip to content

Commit

Permalink
Implement RotateDeg (#50)
Browse files Browse the repository at this point in the history
Closes #23

---------

Authored-by: Karolos Lykos <[email protected]>
  • Loading branch information
KarolosLykos authored Oct 26, 2023
1 parent b9e95d3 commit 9e7a7a8
Show file tree
Hide file tree
Showing 3 changed files with 374 additions and 0 deletions.
10 changes: 10 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,13 @@ import (
)

const radToDegFactor float64 = 180 / math.Pi

const degToRadFactor float64 = math.Pi / 180

type axis int

const (
xAxis axis = iota
yAxis
zAxis
)
128 changes: 128 additions & 0 deletions rotateDeg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package govec

import "math"

// V2F

// RotateDeg rotates the vector counterclockwise by the specified number of degrees and returns a new vector.
func (v V2F[T]) RotateDeg(degrees float64) V2F[T] {
d := degrees * degToRadFactor

return V2F[T]{
X: T(math.Cos(d)*float64(v.X) - math.Sin(d)*float64(v.Y)),
Y: T(math.Sin(d)*float64(v.X) + math.Cos(d)*float64(v.Y)),
}
}

// RotateDegInPlace modifies v by rotating the vector counterclockwise by the specified number of degrees.
func (v *V2F[T]) RotateDegInPlace(degrees float64) {
d := degrees * degToRadFactor

x1 := T(math.Cos(d)*float64(v.X) - math.Sin(d)*float64(v.Y))
y1 := T(math.Sin(d)*float64(v.X) + math.Cos(d)*float64(v.Y))

v.X = x1
v.Y = y1
}

// V2I

// RotateDeg rotates the vector counterclockwise by the specified number of degrees and returns a new V2F vector.
func (v V2I[T]) RotateDeg(degrees float64) V2F[float64] {
d := degrees * degToRadFactor

t := V2F[float64]{
X: math.Cos(d)*float64(v.X) - math.Sin(d)*float64(v.Y),
Y: math.Sin(d)*float64(v.X) + math.Cos(d)*float64(v.Y),
}

return t
}

// V3F

// RotateDeg rotates the vector counterclockwise by the specified number of degrees, around axis and returns a new vector.
func (v V3F[T]) RotateDeg(degrees float64, axis axis) V3F[T] {
d := degrees * degToRadFactor

switch axis {
case zAxis:
return V3F[T]{
X: T(math.Cos(d)*float64(v.X) - math.Sin(d)*float64(v.Y)),
Y: T(math.Sin(d)*float64(v.X) + math.Cos(d)*float64(v.Y)),
Z: v.Z,
}
case yAxis:
return V3F[T]{
X: T(math.Cos(d)*float64(v.X) - math.Sin(d)*float64(v.Y)),
Y: v.Y,
Z: T(-math.Sin(d)*float64(v.X) + math.Cos(d)*float64(v.Z)),
}
default:
return V3F[T]{
X: v.X,
Y: T(math.Cos(d)*float64(v.Y) - math.Sin(d)*float64(v.Z)),
Z: T(-math.Sin(d)*float64(v.Z) + math.Cos(d)*float64(v.Z)),
}
}
}

// RotateDegInPlace modifies v by rotating the vector counterclockwise by the specified number of degrees, around axis.
func (v *V3F[T]) RotateDegInPlace(degrees float64, axis axis) {
d := degrees * degToRadFactor

switch axis {
case zAxis:
x := T(math.Cos(d)*float64(v.X) - math.Sin(d)*float64(v.Y))
y := T(math.Sin(d)*float64(v.X) + math.Cos(d)*float64(v.Y))
z := T(float64(v.Z))

v.X = x
v.Y = y
v.Z = z
case yAxis:
x := T(math.Cos(d)*float64(v.X) - math.Sin(d)*float64(v.Y))
y := T(float64(v.Y))
z := T(-math.Sin(d)*float64(v.X) + math.Cos(d)*float64(v.Z))

v.X = x
v.Y = y
v.Z = z
default:
x := T(float64(v.X))
y := T(math.Cos(d)*float64(v.Y) - math.Sin(d)*float64(v.Z))
z := T(-math.Sin(d)*float64(v.Z) + math.Cos(d)*float64(v.Z))

v.X = x
v.Y = y
v.Z = z
}
}

// V3I

// RotateDeg rotates the vector counterclockwise by the specified number of degrees, around axis and returns a new V3F vector.
func (v V3I[T]) RotateDeg(degrees float64, axis axis) V3F[float64] {
d := degrees * degToRadFactor

switch axis {
case zAxis:
return V3F[float64]{
X: math.Cos(d)*float64(v.X) - math.Sin(d)*float64(v.Y),
Y: math.Sin(d)*float64(v.X) + math.Cos(d)*float64(v.Y),
Z: float64(v.Z),
}
case yAxis:
return V3F[float64]{
X: math.Cos(d)*float64(v.X) - math.Sin(d)*float64(v.Y),
Y: float64(v.Y),
Z: -math.Sin(d)*float64(v.X) + math.Cos(d)*float64(v.Z),
}
default:
return V3F[float64]{
X: float64(v.X),
Y: math.Cos(d)*float64(v.Y) - math.Sin(d)*float64(v.Z),
Z: -math.Sin(d)*float64(v.Z) + math.Cos(d)*float64(v.Z),
}
}
}
236 changes: 236 additions & 0 deletions rotateDeg_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
package govec

import (
"testing"
)

func TestV2F_RotateDeg(t *testing.T) {
type expected struct {
X float64
Y float64
}

tt := []struct {
name string
degrees float64
expected expected
}{
{name: "0", expected: expected{X: 0, Y: 1}},
{name: "90", degrees: 90, expected: expected{X: -1, Y: 0}},
{name: "180", degrees: 180, expected: expected{X: 0, Y: -1}},
}

for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
v1 := V2F[float64]{X: 0, Y: 1}
v2 := v1.RotateDeg(tc.degrees)

if !almostEqual(v2.X, tc.expected.X, 1e-10) {
t.Errorf("V2F RotateDeg failed expected %v: got %v", tc.expected.X, v2.X)
}

if !almostEqual(v2.Y, tc.expected.Y, 1e-10) {
t.Errorf("V2F RotateDeg failed expected %v: got %v", tc.expected.Y, v2.Y)
}
})
}
}

func TestV2I_RotateDeg(t *testing.T) {
type expected struct {
X float64
Y float64
}

tt := []struct {
name string
degrees float64
expected expected
}{
{name: "0", expected: expected{X: 0, Y: 1}},
{name: "90", degrees: 90, expected: expected{X: -1, Y: 0}},
{name: "180", degrees: 180, expected: expected{X: 0, Y: -1}},
}

for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
v1 := V2I[int]{X: 0, Y: 1}
v2 := v1.RotateDeg(tc.degrees)

if !almostEqual(v2.X, tc.expected.X, 1e-10) {
t.Errorf("V2I RotateDeg failed expected %v: got %v", tc.expected.X, v2.X)
}

if !almostEqual(v2.Y, tc.expected.Y, 1e-10) {
t.Errorf("V2I RotateDeg failed expected %v: got %v", tc.expected.Y, v2.Y)
}
})
}
}

func TestV2F_RotateDegInPlace(t *testing.T) {
type expected struct {
X float64
Y float64
}

tt := []struct {
name string
degrees float64
expected expected
}{
{name: "0", expected: expected{X: 0, Y: 1}},
{name: "90", degrees: 90, expected: expected{X: -1, Y: 0}},
{name: "180", degrees: 180, expected: expected{X: 0, Y: -1}},
}

for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
v1 := V2F[float64]{X: 0, Y: 1}
v1.RotateDegInPlace(tc.degrees)

if !almostEqual(v1.X, tc.expected.X, 1e-10) {
t.Errorf("V2F RotateDegInPlace failed expected %v: got %v", tc.expected.X, v1.X)
}

if !almostEqual(v1.Y, tc.expected.Y, 1e-10) {
t.Errorf("V2F RotateDegInPlace failed expected %v: got %v", tc.expected.Y, v1.Y)
}
})
}
}

func TestV3F_RotateDeg(t *testing.T) {
type expected struct {
X float64
Y float64
Z float64
}

tt := []struct {
name string
degrees float64
axis axis
expected expected
}{
{name: "z0", axis: zAxis, expected: expected{X: 0, Y: 1, Z: 0}},
{name: "z90", degrees: 90, axis: zAxis, expected: expected{X: -1, Y: 0, Z: 0}},
{name: "z180", degrees: 180, axis: zAxis, expected: expected{X: 0, Y: -1, Z: 0}},

{name: "y0", axis: yAxis, expected: expected{X: 0, Y: 1, Z: 0}},
{name: "y90", degrees: 90, axis: yAxis, expected: expected{X: -1, Y: 1, Z: 0}},
{name: "y180", degrees: 180, axis: yAxis, expected: expected{X: 0, Y: 1, Z: 0}},

{name: "x0", axis: xAxis, expected: expected{X: 0, Y: 1, Z: 0}},
{name: "x90", degrees: 90, axis: xAxis, expected: expected{X: 0, Y: 0, Z: 0}},
{name: "x180", degrees: 180, axis: xAxis, expected: expected{X: 0, Y: -1, Z: 0}},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
v1 := V3F[float64]{X: 0, Y: 1, Z: 0}
v2 := v1.RotateDeg(tc.degrees, tc.axis)

if !almostEqual(v2.X, tc.expected.X, 1e-10) {
t.Errorf("V3F RotateDeg failed expected %v: got %v", tc.expected.X, v2.X)
}

if !almostEqual(v2.Y, tc.expected.Y, 1e-10) {
t.Errorf("V3F RotateDeg failed expected %v: got %v", tc.expected.Y, v2.Y)
}

if !almostEqual(v2.Z, tc.expected.Z, 1e-10) {
t.Errorf("V3F RotateDeg failed expected %v: got %v", tc.expected.Z, v2.Z)
}
})
}
}

func TestV3I_RotateDeg(t *testing.T) {
type expected struct {
X float64
Y float64
Z float64
}

tt := []struct {
name string
degrees float64
axis axis
expected expected
}{
{name: "z0", axis: zAxis, expected: expected{X: 0, Y: 1, Z: 0}},
{name: "z90", degrees: 90, axis: zAxis, expected: expected{X: -1, Y: 0, Z: 0}},
{name: "z180", degrees: 180, axis: zAxis, expected: expected{X: 0, Y: -1, Z: 0}},

{name: "y0", axis: yAxis, expected: expected{X: 0, Y: 1, Z: 0}},
{name: "y90", degrees: 90, axis: yAxis, expected: expected{X: -1, Y: 1, Z: 0}},
{name: "y180", degrees: 180, axis: yAxis, expected: expected{X: 0, Y: 1, Z: 0}},

{name: "x0", axis: xAxis, expected: expected{X: 0, Y: 1, Z: 0}},
{name: "x90", degrees: 90, axis: xAxis, expected: expected{X: 0, Y: 0, Z: 0}},
{name: "x180", degrees: 180, axis: xAxis, expected: expected{X: 0, Y: -1, Z: 0}},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
v1 := V3I[int]{X: 0, Y: 1, Z: 0}
v2 := v1.RotateDeg(tc.degrees, tc.axis)

if !almostEqual(v2.X, tc.expected.X, 1e-10) {
t.Errorf("V3I RotateDeg failed expected %v: got %v", tc.expected.X, v2.X)
}

if !almostEqual(v2.Y, tc.expected.Y, 1e-10) {
t.Errorf("V3I RotateDeg failed expected %v: got %v", tc.expected.Y, v2.Y)
}

if !almostEqual(v2.Z, tc.expected.Z, 1e-10) {
t.Errorf("V3I RotateDeg failed expected %v: got %v", tc.expected.Z, v2.Z)
}
})
}
}

func TestV3F_RotateDegInPlace(t *testing.T) {
type expected struct {
X float64
Y float64
Z float64
}

tt := []struct {
name string
degrees float64
axis axis
expected expected
}{
{name: "z0", axis: zAxis, expected: expected{X: 0, Y: 1, Z: 0}},
{name: "z90", degrees: 90, axis: zAxis, expected: expected{X: -1, Y: 0, Z: 0}},
{name: "z180", degrees: 180, axis: zAxis, expected: expected{X: 0, Y: -1, Z: 0}},

{name: "y0", axis: yAxis, expected: expected{X: 0, Y: 1, Z: 0}},
{name: "y90", degrees: 90, axis: yAxis, expected: expected{X: -1, Y: 1, Z: 0}},
{name: "y180", degrees: 180, axis: yAxis, expected: expected{X: 0, Y: 1, Z: 0}},

{name: "x0", axis: xAxis, expected: expected{X: 0, Y: 1, Z: 0}},
{name: "x90", degrees: 90, axis: xAxis, expected: expected{X: 0, Y: 0, Z: 0}},
{name: "x180", degrees: 180, axis: xAxis, expected: expected{X: 0, Y: -1, Z: 0}},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
v1 := V3F[float64]{X: 0, Y: 1, Z: 0}
v1.RotateDegInPlace(tc.degrees, tc.axis)

if !almostEqual(v1.X, tc.expected.X, 1e-10) {
t.Errorf("V3F RotateDegInPlace failed expected %v: got %v", tc.expected.X, v1.X)
}

if !almostEqual(v1.Y, tc.expected.Y, 1e-10) {
t.Errorf("V3F RotateDegInPlace failed expected %v: got %v", tc.expected.Y, v1.Y)
}

if !almostEqual(v1.Z, tc.expected.Z, 1e-10) {
t.Errorf("V3F RotateDegInPlace failed expected %v: got %v", tc.expected.Z, v1.Z)
}
})
}
}

0 comments on commit 9e7a7a8

Please sign in to comment.