-
Notifications
You must be signed in to change notification settings - Fork 0
/
spherical.go
125 lines (106 loc) · 2.73 KB
/
spherical.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package space
import (
"fmt"
"math"
)
// Spherical represents a point in space in spherical coordinates
type Spherical struct {
// R is distance from the origin
R float64
// T is rotation about Z
T float64
// P is tilt from Z
P float64
}
var _ Vector = (*Spherical)(nil)
// NewSpherical creates a new Spherical from a rotation and tilt
func NewSpherical(radius, theta, phi float64) Spherical {
s := Spherical{
R: radius,
}
s = s.Rotate(theta)
s = s.Tilt(phi)
return s
}
// Cartesian returns the Cartesian version of s
func (s Spherical) Cartesian() Cartesian {
sinT, cosT := math.Sincos(s.T)
sinP, cosP := math.Sincos(s.P)
radius := s.R
return Cartesian{
X: radius * sinP * cosT,
Y: radius * sinP * sinT,
Z: radius * cosP,
}
}
// Spherical return s
func (s Spherical) Spherical() Spherical {
return s
}
// Translate shifts a Spherical by a Vector (addition in cartesian space)
func (s Spherical) Translate(v Vector) Vector {
c := s.Cartesian()
return c.Translate(v)
}
// Scale scales a Spherical by i
func (s Spherical) Scale(i float64) Vector {
s.R *= i
return s
}
// Transform Multiplyiplies a Spherical by a given matrix
func (s Spherical) Transform(m Matrix) Vector {
c := s.Cartesian()
return c.Transform(m)
}
// Project returns the projection of v onto s
func (s Spherical) Project(v Vector) Vector {
c := s.Cartesian()
return c.Project(v)
}
// Rotate will adjust the rotation about Z by theta
func (s Spherical) Rotate(theta float64) Spherical {
wrappedT := s.T + theta
unwrappedT := math.Mod(wrappedT, math.Pi*2)
if unwrappedT >= 0 {
s.T = unwrappedT
} else {
s.T = 2*math.Pi + unwrappedT
}
return s
}
// Tilt will adjust the tilt from Z by phi
func (s Spherical) Tilt(phi float64) Spherical {
wrappedP := s.P + phi
newP := math.Mod(wrappedP, math.Pi*2)
// Check if tilt is negative
if newP < 0 {
newP = (math.Pi * 2) + newP
}
// Check if tilt is beyond range of [0, pi]
if newP > math.Pi {
s.P = (math.Pi * 2) - newP
return s.Rotate(math.Pi)
}
s.P = newP
return s
}
// PortionOrtagonal returns the portion of o2 which is orthogonal to o
func (s Spherical) PortionOrtagonal(o2 Spherical) Spherical {
v := s.Cartesian()
u := o2.Cartesian()
vProju := v.Project(u)
vProjuN := vProju.Scale(-1.0)
portUOrthoV := u.Translate(vProjuN)
sp := portUOrthoV.Spherical()
return sp
}
// RotationMatrix produces a matrix which will rotate based on the spherical coordinates
func (s Spherical) RotationMatrix() Matrix {
rotateZbyT := NewRotationMatrixZ(s.T)
rotateYbyP := NewRotationMatrixY(s.P)
rotateZbyTinverse := NewRotationMatrixZ(-s.T)
return rotateZbyT.Multiply(rotateYbyP).Multiply(rotateZbyTinverse)
}
func (s Spherical) String() string {
return fmt.Sprintf("{R:%4.2f, T:%4.2f, P:%4.2f}", s.R, s.T, s.P)
}