Table of Contents
Quaternions
A quat is a quaternion of four single floats, X, Y and Z, its vector
part, then W, its real part. Rotations are unit quaternions, of length 1.
dquat is the same of doubles; see Doubles.
Functions that return a quaternion or a vector take an optional last
argument, result; see Results and Allocation.
Making rotations
(gm:make-quat) ; => #(0.0 0.0 0.0 1.0)
(gm:quat-from-axis-angle (gm:make-vec3 :y 1) (gm:radians 90))
;; => #(0.0 0.70710677 0.0 0.70710677)
(gm:make-quat)without arguments is the identity: no rotation.quat-identityreturns it too, and can write it into aresult.quat-from-axis-anglemakes the rotation by an angle, in radians, about an axis, counterclockwise when the axis points at you. The axis need not be normalised; a zero axis signalsgmath-error.gm:make-quatmakes any quaternion from its components, given by keyword,(gm:make-quat :x x :y y :z z :w w): X, Y and Z are 0 if not given, and W 1.quat-x,quat-y,quat-zandquat-wread its components, andsetfwrites them with single floats, as for vectors:
(gm:quat-w (gm:quat-from-axis-angle (gm:make-vec3 :y 1) (gm:radians 90))) ; => 0.70710677
Rotating
quat-rotate rotates a vec3. A quarter turn about +Y takes +Z to +X:
(gm:quat-rotate (gm:quat-from-axis-angle (gm:make-vec3 :y 1) (gm:radians 90)) (gm:make-vec3 :z 1))
;; => #(0.99999994 0.0 5.9604645e-8)
Combining
quat* is the Hamilton product: (gm:quat* a b) rotates by B, then by
A, as mat4* applies its right-hand matrix first:
(let* ((a (gm:quat-from-axis-angle (gm:make-vec3 :z 1) (gm:radians 90)))
(b (gm:quat-from-axis-angle (gm:make-vec3 :x 1) (gm:radians 90))))
(gm:vec3-approx= (gm:quat-rotate (gm:quat* a b) (gm:make-vec3 :y 1))
(gm:quat-rotate a (gm:quat-rotate b (gm:make-vec3 :y 1)))))
;; => T
Interpolating
quat-slerp returns the rotation a fraction of the way from one rotation
to another, at a constant angular speed and along the shorter arc: the
first at 0, the second at 1.
(let ((start (gm:make-quat))
(end (gm:quat-from-axis-angle (gm:make-vec3 :z 1) (gm:radians 90))))
(gm:quat-rotate (gm:quat-slerp start end 1/2) (gm:make-vec3 :x 1)))
;; => #(0.7071068 0.70710677 0.0)
Half way through a quarter turn, +X points at 45 degrees. A quaternion and its negation are the same rotation, so slerp takes whichever of the two is nearer, and never goes the long way round.
Other operations
| Function | Returns |
|---|---|
quat-conjugate |
The vector part negated: for a rotation, the opposite rotation. |
quat-inverse |
The inverse. For a rotation, quat-conjugate gives the same, more cheaply. Signals gmath-error for the zero quaternion. |
quat-normalize |
The quaternion scaled to length 1, the rotation it stands for. Signals gmath-error for the zero quaternion. Normalise now and then a rotation built up from many products, which drifts from length 1. |
quat-length, quat-dot |
The length and the dot product, as 4-vectors. |
quat-copy |
A copy. |
quat-approx= |
True if no component differs by more than an epsilon, 1e-5 by default. A quaternion and its negation are the same rotation but not equal here. |
(let ((q (gm:quat-from-axis-angle (gm:make-vec3 :y 1) 1.0)))
(gm:quat-approx= (gm:quat* q (gm:quat-conjugate q)) (gm:make-quat)))
;; => T
As matrices
mat4-from-quat makes the mat4 that rotates as a unit quaternion does,
and mat4-compose builds a node's whole transform from a translation, a
quaternion and a scale. See
Transforms and Cameras.
(gm:mat4-approx= (gm:mat4-from-quat (gm:quat-from-axis-angle (gm:make-vec3 :x 1 :y 2 :z 3) 0.7))
(gm:mat4-rotation (gm:make-vec3 :x 1 :y 2 :z 3) 0.7))
;; => T