-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathquaternion.go
166 lines (143 loc) · 4.54 KB
/
quaternion.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*
Golang package implementing quaternion math
Purpose is to provide quaternion support under the MIT license as existing
Go quaternion packages are under more restrictive or unspecified licenses.
This project is licensed under the terms of the MIT license.
*/
package quaternion
import (
"math"
)
// New returns a new quaternion
func New(w, x, y, z float64) Quaternion {
return Quaternion{W: w, X: x, Y: y, Z: z}
}
// Pure returns a new pure quaternion (no scalar part)
func Pure(x, y, z float64) Quaternion {
return Quaternion{X: x, Y: y, Z: z}
}
// Vec3 represents a vector in 3d space
type Vec3 struct {
X float64
Y float64
Z float64
}
// Quaternion represents a quaternion W+X*i+Y*j+Z*k
type Quaternion struct {
W float64 // Scalar component
X float64 // i component
Y float64 // j component
Z float64 // k component
}
// Conj returns the conjugate of a Quaternion (W,X,Y,Z) -> (W,-X,-Y,-Z)
func (qin Quaternion) Conj() Quaternion {
qin.X = -qin.X
qin.Y = -qin.Y
qin.Z = -qin.Z
return qin
}
// Norm2 returns the L2-Norm of a Quaternion (W,X,Y,Z) -> W*W+X*X+Y*Y+Z*Z
func (qin Quaternion) Norm2() float64 {
return qin.W*qin.W + qin.X*qin.X + qin.Y*qin.Y + qin.Z*qin.Z
}
// Neg returns the negative
func (qin Quaternion) Neg() Quaternion {
qin.W = -qin.W
qin.X = -qin.X
qin.Y = -qin.Y
qin.Z = -qin.Z
return qin
}
// Norm returns the L1-Norm of a Quaternion (W,X,Y,Z) -> Sqrt(W*W+X*X+Y*Y+Z*Z)
func (qin Quaternion) Norm() float64 {
return math.Sqrt(qin.Norm2())
}
// Scalar returns a scalar-only Quaternion representation of a float (W,0,0,0)
func Scalar(w float64) Quaternion {
return Quaternion{W: w}
}
// Sum returns the vector sum of any number of Quaternions
func Sum(qin ...Quaternion) Quaternion {
qout := Quaternion{}
for _, q := range qin {
qout.W += q.W
qout.X += q.X
qout.Y += q.Y
qout.Z += q.Z
}
return qout
}
// Prod returns the non-commutative product of any number of Quaternions
func Prod(qin ...Quaternion) Quaternion {
qout := Quaternion{1, 0, 0, 0}
var w, x, y, z float64
for _, q := range qin {
w = qout.W*q.W - qout.X*q.X - qout.Y*q.Y - qout.Z*q.Z
x = qout.W*q.X + qout.X*q.W + qout.Y*q.Z - qout.Z*q.Y
y = qout.W*q.Y + qout.Y*q.W + qout.Z*q.X - qout.X*q.Z
z = qout.W*q.Z + qout.Z*q.W + qout.X*q.Y - qout.Y*q.X
qout = Quaternion{w, x, y, z}
}
return qout
}
// Unit returns the Quaternion rescaled to unit-L1-norm
func (qin Quaternion) Unit() Quaternion {
k := qin.Norm()
return Quaternion{qin.W / k, qin.X / k, qin.Y / k, qin.Z / k}
}
// Inv returns the Quaternion conjugate rescaled so that Q Q* = 1
func (qin Quaternion) Inv() Quaternion {
k2 := qin.Norm2()
q := qin.Conj()
return Quaternion{q.W / k2, q.X / k2, q.Y / k2, q.Z / k2}
}
// RotateVec3 returns the vector rotated by the quaternion.
func (qin Quaternion) RotateVec3(vec Vec3) Vec3 {
conj := qin.Conj()
aug := Quaternion{0, vec.X, vec.Y, vec.Z}
rot := Prod(qin, aug, conj)
return Vec3{rot.X, rot.Y, rot.Z}
}
// Rotate returns the vector rotated by the quaternion.
func (vin Vec3) Rotate(q Quaternion) Vec3 {
conj := q.Conj()
aug := Quaternion{0, vin.X, vin.Y, vin.Z}
rot := Prod(q, aug, conj)
return Vec3{rot.X, rot.Y, rot.Z}
}
// Euler returns the Euler angles phi, theta, psi corresponding to a Quaternion
func (q Quaternion) Euler() (float64, float64, float64) {
r := q.Unit()
phi := math.Atan2(2*(r.W*r.X+r.Y*r.Z), 1-2*(r.X*r.X+r.Y*r.Y))
theta := math.Asin(2 * (r.W*r.Y - r.Z*r.X))
psi := math.Atan2(2*(r.X*r.Y+r.W*r.Z), 1-2*(r.Y*r.Y+r.Z*r.Z))
return phi, theta, psi
}
// FromEuler returns a Quaternion corresponding to Euler angles phi, theta, psi
func FromEuler(phi, theta, psi float64) Quaternion {
q := Quaternion{}
q.W = math.Cos(phi/2)*math.Cos(theta/2)*math.Cos(psi/2) +
math.Sin(phi/2)*math.Sin(theta/2)*math.Sin(psi/2)
q.X = math.Sin(phi/2)*math.Cos(theta/2)*math.Cos(psi/2) -
math.Cos(phi/2)*math.Sin(theta/2)*math.Sin(psi/2)
q.Y = math.Cos(phi/2)*math.Sin(theta/2)*math.Cos(psi/2) +
math.Sin(phi/2)*math.Cos(theta/2)*math.Sin(psi/2)
q.Z = math.Cos(phi/2)*math.Cos(theta/2)*math.Sin(psi/2) -
math.Sin(phi/2)*math.Sin(theta/2)*math.Cos(psi/2)
return q
}
// RotMat returns the rotation matrix (as float array) corresponding to a Quaternion
func (qin Quaternion) RotMat() [3][3]float64 {
q := qin.Unit()
m := [3][3]float64{}
m[0][0] = 1 - 2*(q.Y*q.Y+q.Z*q.Z)
m[0][1] = 2 * (q.X*q.Y - q.W*q.Z)
m[0][2] = 2 * (q.W*q.Y + q.X*q.Z)
m[1][1] = 1 - 2*(q.Z*q.Z+q.X*q.X)
m[1][2] = 2 * (q.Y*q.Z - q.W*q.X)
m[1][0] = 2 * (q.W*q.Z + q.Y*q.X)
m[2][2] = 1 - 2*(q.X*q.X+q.Y*q.Y)
m[2][0] = 2 * (q.Z*q.X - q.W*q.Y)
m[2][1] = 2 * (q.W*q.X + q.Z*q.Y)
return m
}