1 (in-package :cl-tuples
)
3 (def-tuple-type quaternion
4 :tuple-element-type fast-float
8 (export-tuple-operations quaternion
)
10 (def-tuple-type angle-axis
11 :tuple-element-type fast-float
12 :initial-element
0.0f0
15 (export-tuple-operations angle-axis
)
17 (def-tuple-op angle-axis-matrix33
*
18 ((aa angle-axis
(x y z a
)))
34 (def-tuple-op matrix33-trace
*
35 ((m matrix33
#.
(tuple-elements 'matrix33
)))
39 (def-tuple-op vector3d-angle-axis
*
40 ((axis vector3d
(x y z
))
43 (angle-axis-values* x y z angle
)))
45 ;; TODO: the matrix has to be a rotation matrix, but then we can also
46 ;; restrict the values to their respective ranges, which eliminates the
47 ;; warning about the empty COND case and yields better compilation
48 ;; results. the question is: are these correct?
49 (def-tuple-op matrix33-angle-axis
*
50 ((m matrix33
#.
(tuple-elements 'matrix33
)))
52 (let* ((trace (matrix33-trace* m
))
53 (angle (acos (/ (1- trace
) 2))))
54 (declare (type (single-float -
1.0f0
3.0f0
) trace
)
55 (type (single-float 0.0f0
#.fast-pi
)))
58 (angle-axis-key-values))
59 ((< 0.0f0 angle fast-pi
)
70 (let* ((u0 (/ (the single-float
(sqrt (1+ (- e00 e11 e22
)))) 2))
72 (vector3d-values* u0
(/ e01
2u0) (/ e02
2u0))))
74 (let* ((u1 (/ (the single-float
(sqrt (1+ (- e11 e00 e22
)))) 2))
76 (vector3d-values* (/ e01
2u1) u1
(/ e12
2u1))))
78 (let* ((u2 (/ (the single-float
(sqrt (1+ (- e22 e00 e11
)))) 2))
80 (vector3d-values* (/ e02
2u2) (/ e12
2u2) u2
))))
92 ;; need conjugate, angle-axis conversion, slerp
94 (def-tuple-op quaternion-conjugate
*
95 ((q quaternion
(x y z w
)))
96 (quaternion-values* (- x
) (- y
) (- z
) w
))
98 (def-tuple-op quaternion-scale
*
99 ((q quaternion
(x y z w
))
101 "Multiply a quat by a scalar"
104 (*) (quaternion-key-values :initial-element s
) q
)))
106 (def-tuple-op quaternion-dot
*
109 "Dot product of two quaternions."
112 (+) (quaternion-map* (*) q0 q1
))))
114 (def-tuple-op quaternion-mag-square
*
117 (quaternion-dot* q q
)))
119 (def-tuple-op quaternion-mag
*
122 (sqrt (quaternion-mag-square* q
))))
124 (def-tuple-op quaternion-normalize
*
126 "Ensure a quaternion is a unit"
129 q
(/ (quaternion-mag* q
)))))
131 (def-tuple-op quaternion-sum
*
134 "Sum the components of two quaternions"
136 (quaternion-map* (+) q0 q1
)))
138 (def-tuple-op quaternion-inverse
*
140 "Inverse of quaternion"
143 q
(/ (quaternion-mag-square* q
)))))
145 (def-tuple-op quaternion-power
*
146 ((q quaternion
(x y z w
))
149 (let* ((angle (acos w
))
153 (let ((factor (* angle times
(/ sin
))))
158 (cos (* times angle
))))))))
160 (def-tuple-op quaternion-product
*
161 ((q-lhs quaternion
(x1 y1 z1 w1
))
162 (q-rhs quaternion
(x2 y2 z2 w2
)))
163 "Multiple of two quaternions"
165 (quaternion-values* (- (+ (* w1 x2
) (* x1 w2
) (* y1 z2
)) (* z1 y2
))
166 (- (+ (* w1 y2
) (* y1 w2
) (* z1 x2
)) (* x1 z2
))
167 (- (+ (* w1 z2
) (* x1 y2
) (* z1 w2
)) (* y1 x2
))
168 (- (* w1 w2
) (* x1 x2
) (* y1 y2
) (* z1 z2
)))))
170 (def-tuple-op quaternion-matrix33
*
171 ((q quaternion
(x y z w
)))
172 "Convert a quaternion to a 3x3 rotation matrix."
175 (- 1 (* 2 y y
) (* 2 z z
)) (- (* 2 x y
) (* 2 w z
)) (+ (* 2 x z
) (* 2 w y
))
176 (+ (* 2 x y
) (* 2 w z
)) (- 1 (* 2 x x
) (* 2 z z
)) (- (* 2 y z
) (* 2 w x
))
177 (- (* 2 x z
) (* 2 w y
)) (+ (* 2 y z
) (* 2 w x
)) (- 1 (* 2 x x
) (* 2 y y
)))))
179 (def-tuple-op matrix33-quaternion
*
180 ((m matrix33
:default
))
182 (let ((trace (matrix33-trace* m
)))
184 (let* ((w (/ (sqrt (1+ trace
)) 2))
192 (let* ((x (/ (sqrt (1+ (- e00 e11 e22
))) 2))
198 (/ (- e21 e21
) 4x
))))
200 (let* ((y (/ (sqrt (1+ (- e11 e00 e22
))) 2))
206 (/ (- e20 e02
) 4y
))))
208 (let* ((z (/ (sqrt (1+ (- e22 e00 e11
))) 2))
214 (/ (- e01 e10
) 4z
)))))
225 (def-tuple-op angle-axis-quaternion
*
226 ((aa angle-axis
(x y z a
)))
227 "Convert an angle-axis tuple to a quaternion tuple"
229 (let* ((a/2 (* 0.5f0 a
))
230 (sin-angle (sin a
/2)))
237 (def-tuple-op quaternion-angle-axis
*
238 ((q quaternion
(x y z w
)))
239 "Convert an quaternion tuple to an angle-axis tuple. If the angle is
240 zero, the axis isn't defined, so the unit x vector is returned instead."
242 ;; either test for one, disable traps, or catch the exception
243 (if (or (= w
1.0f0
) (= w -
1.0f0
))
244 (angle-axis-key-values x
1.0f0
)
245 (let ((angle (* 2 (acos w
))))
246 (vector3d-angle-axis*
248 (vector3d-values* x y z
)
249 (/ (sqrt (- 1 (* w w
)))))
252 (def-tuple-op quaternion-transform-vector3d
*
253 ((vector vector3d
(vx vy vz
))
254 (quat quaternion
(qx qy qz qw
)))
255 "Transform a 3d vector with a quaternion"
261 (vector3d-quaternion* vector
))
262 (quaternion-conjugate* quat
))
264 (vector3d-values* rx ry rz
))))
266 (def-tuple-op vector3d-quaternion
*
267 ((vector vector3d
(vx vy vz
)))
268 "Convert a 3d vector into q auqt for angular velocity purposes"
270 (quaternion-values* vx vy vz
0.0f0
)))