Kawa extends the Scheme numeric tower to include quaternions as a proper superset of the complex numbers. Quaternions provide a convenient notation to represent rotations in three-dimensional space, and are therefore commonly found in applications such as computer graphics, robotics, and spacecraft engineering. The Kawa quaternion API is modeled after this with some additions.
A quaternion is a number that can be expressed in the form
‘w+xi+yj+zk
’, where w
, x
, y
, and z
are
real, and i
, j
, and k
are imaginary units
satisfying i2 = j2 = k2 = ijk = -1. The magnitude of a
quaternion is defined to be its Euclidean norm when viewed as a point in
R4.
The real–part of a quaternion is also called its ‘scalar
’, while
the i–part, j–part, and k–part taken together are also called its
‘vector
’. A quaternion with zero j–part and k–part is an
ordinary complex number. (If the i–part is also zero, then it is a
real). A quaternion with zero real–part is called a
‘vector quaternion
’.
The reader syntax for number literals has been extended to support both
rectangular and polar (hyperspherical) notation for quaternions. The
rectangular notation is as above, i.e. w+xi+yj+zk
. The polar
notation takes the form r@t%u&v
, where r
is the
magnitude, t
is the first angle, and u
and v
are
two other angles called the “colatitude” and “longitude”.
The rectangular coordinates and polar coordinates are related by the equations:
w
=r
* cost
x
=r
* sint
* cosu
y
=r
* sint
* sinu
* cosv
z
=r
* sint
* sinu
* sinv
With either notation, zero elements may be omitted.
Procedure: make-rectangular
w
x
Procedure: make-rectangular
w
x
y
z
These procedures construct quaternions from Cartesian coordinates.
These procedures construct quaternions from polar coordinates.
All of the arithmetic and transcendental functions defined for complex arguments have been extended to support quaternions.
Quaternion multiplication is not commutative, so there are two possible interpretations of
(/ q1 q2)
which would yield different results: either(* q1 (/ q2))
, or(* (/ q2) q1)
. Division in this implementation has been defined such that(/ q1 q2 ...)
is equivalent to(* q1 (/ q2) ...)
, but it is recommended to use reciprocals (unary/
) and multiplication.
Return the real–part of
q
.(real-part 0) ⇒ 0 (real-part -i) ⇒ 0 (real-part 1+2i-3j+4k) ⇒ 1
Return the i–part of
q
.(imag-part 0) ⇒ 0 (imag-part -i) ⇒ -1 (imag-part 1+2i-3j+4k) ⇒ 2
Return the Euclidean norm of
q
. Ifq
isa+bi+cj+dk
, then(magnitude q)
is(sqrt (apply + (map square (list a b c d))))
Return the angle of
q
.
The following additional functionality is made available by doing one of:
(require 'quaternions) ;; or (import (kawa quaternions))
An alias for
gnu.math.Quaternion
, useful for type declarations.
Return
#t
ifx
is a quaternion, i.e. an ordinary number, and#f
otherwise.(quaternion? 0) ⇒ #t (quaternion? -i) ⇒ #t (quaternion? 1+2i-3j+4k) ⇒ #t (quaternion? 10.0m) ⇒ #f (quaternion? "x") ⇒ #f
Return the j–part of
q
.(jmag-part 0) ⇒ 0 (jmag-part -i) ⇒ 0 (jmag-part 1+2i-3j+4k) ⇒ -3
(kmag-part 0) ⇒ 0 (kmag-part -i) ⇒ 0 (kmag-part 1+2i-3j+4k) ⇒ 4
Return the projection of
q
into the complex plane:(+ (real-part q) (* +i (imag-part q)))
(complex-part 0) ⇒ 0 (complex-part -i) ⇒ -1i (complex-part 1+2i-3j+4k) ⇒ 1+2i
Return the vector–part of
q
.(vector-part 0) ⇒ 0 (vector-part -i) ⇒ -1i (vector-part 1+2i-3j+4k) ⇒ +2i-3j+4k
Return a quaternion of unit magnitude with the same direction as
q
. Ifq
is zero, return zero. This is like a 4D version of a signum function.(unit-quaternion 0) ⇒ 0 (unit-quaternion -i) ⇒ -1i (unit-quaternion 1+2i-3j+4k) ⇒ 0.18257418583505536+0.3651483716701107i-0.5477225575051661j+0.7302967433402214k
Return the vector–part of
q
, scaled to have magnitude 1. If the vector–part is zero, then return zero.(unit-vector 0) ⇒ 0 (unit-vector -i) ⇒ -1i (unit-vector 1+2i-3j+4k) ⇒ +0.3713906763541037i-0.5570860145311556j+0.7427813527082074k
Return the colatitude of
q
.
Return the longitude of
q
.
Procedure: vector-quaternion?
obj
Return
#t
ifobj
is a vector quaternion, i.e. a quaternion with zero real–part.
Procedure: make-vector-quaternion
x
y
z
Construct vector quaternion
xi+yj+zk
. This is equivalent to(make-rectangular 0 x y z)
.
Procedure: vector-quaternion->list
vq
Return a newly allocated list of the x, y, and z components of
vq
. This is equivalent to(list (imag-part vq) (jmag-part vq) (kmag-part vq))
.
For two vector quaternions
q
1 =ai+bj+ck
andq
2 =di+ej+fk
, returnad + be + cf
. This is equal to the R^3 dot product for vectors (a,b,c) and (d,e,f), and is also equal to(- (real-part (* q1 q2)))
. It is an error if eitherq
1 orq
2 has a non-zero real–part.
Procedure: cross-product
q
1
q
2
For two vector quaternions
q
1 =ai+bj+ck
andq
2 =di+ej+fk
, return the R^3 cross product for vectors (a,b,c) and (d,e,f), which is equal to(vector-part (* q1 q2))
. It is an error if eitherq
1 orq
2 has a non-zero real–part.
Return
(+ (real-part q) (* -1 (vector-part q)))
.(conjugate 0) ⇒ 0 (conjugate -i) ⇒ +1i (conjugate 1+2i-3j+4k) ⇒ 1-2i+3j-4k
The (kawa rotations)
library provides a set of functions which
use unit quaternions to represent 3D spatial rotations. To use these
functions, the library must be imported:
(import (kawa rotations))
These functions normalize their quaternion inputs as needed to be of length 1.
Conversions to and from several alternate representations of rotations are supported.
The set of unit quaternions provides a double covering of all
possible 3D rotations: q
and -q
represent the same
rotation. Most other representations also have multiple numerical
values which map to the same rotation (for example, the rotation about
axis-vec
by angle
is the same as the rotation about
-axis-vec
by -angle+2pi
). Therefore, these functions do
not necessarily act as inverses in the sense of equal?
.
Furthermore, rotations involve trigonometric functions, so there will
typically be some floating point error: (acos (cos 0.1))
returns
0.09999999999999945, which is very close to 0.1 but not exact.
Procedure: quaternion->rotation-matrix
q
Procedure: rotation-matrix->quaternion
m
The
quaternion->rotation-matrix
procedure returns a 3x3 rotation matrix representing the same rotation asq
. The rotation matrix is instantiated as a SRFI-25 multi-dimensional array backed by an f64vector.The
rotation-matrix->quaternion
procedure performs the reverse operation, producing an equivalent unit quaternion for the rotation matrix (multi-dimensional array)m
.(rotation-matrix->quaternion (quaternion->rotation-matrix -1)) ⇒ 1.0
Procedure: rotation-axis/angle
q
The
rotation-axis
procedure returns the axis of rotation of the quaternionq
as a unit-length vector quaternion. If the axis of rotation is not well-defined (the angle of rotation is 0), then+i
is arbitrarily chosen as the axis.The
rotation-angle
procedure returns the corresponding angle of rotation. Note that this is not the same as the result of theangle
procedure.The
rotation-axis/angle
procedure returns the rotation axis and angle as multiple values.(let* ((q 1/2+1/2i+1/2j+1/2k) (ar (rotation-angle q)) (ad (java.lang.Math:toDegrees ar)) (exact-ad (exact ad))) (rationalize exact-ad 1/10)) ⇒ 120
Procedure: make-axis/angle
axis-vec
angle
Procedure: make-axis/angle
axis-x
axis-y
axis-z
angle
The
make-axis/angle
procedure returns a quaternion representing the given axis/angle rotation. The axis is specified as either a single vector quaternion argumentaxis-vec
, or as three realsaxis-x
,axis-y
, andaxis-z
.
The procedures
rotx
,roty
, androtz
return quaternions representing rotations about the X-, Y-, and Z-axes.
The intrinsic angle sets represent arbitrary rotations as a sequence of three rotations about coordinate frame axes attached to the rotating body (i.e. the axes rotate with the body).
There are twelve possible angle sets which neatly divide into two groups of six. The six with same first and third axes are also known as “Euler angles”. The six with different first and third axes are also known as “Tait-Bryan angles”.
These functions decompose the rotation represented by
q
into Euler angles of the given set (XYX, XZX, YXY, YZY, ZXZ, or ZYZ) and returns the three angles as multiple values. The middle angle will be in the range [0,pi]. If it is on the edges of that range (within 1.0E-12 of 0 or pi), such that the first and third axes are colinear, then the first angle will be set to 0.(intrinsic-zyz (* (rotz 0.3) (roty 0.8) (rotz -0.6))) ⇒ 0.3000000000000001 0.7999999999999999 -0.5999999999999999
Aliases for the corresponding
intrinsic-
procedures.
These functions decompose the rotation represented by
q
into Tait-Bryan angles of the given set (XYZ, XZY, YXZ, YZX, ZXY, or ZYX) and returns the three angles as multiple values. The middle angle will be in the range [-pi/2,pi/2]. If it is on the edges of that range, such that the first and third axes are colinear, then the first angle will be set to 0.
Aliases for the corresponding
intrinsic-
procedures.
Procedure: make-intrinsic-xyx
alpha
beta
gamma
Procedure: make-intrinsic-xzx
alpha
beta
gamma
Procedure: make-intrinsic-yxy
alpha
beta
gamma
Procedure: make-intrinsic-yzy
alpha
beta
gamma
Procedure: make-intrinsic-zxz
alpha
beta
gamma
Procedure: make-intrinsic-zyz
alpha
beta
gamma
These functions return quaternions representing the given Euler angle rotations.
Aliases for the corresponding
make-intrinsic-
procedures.(let-values (((a b c) (euler-xyx (make-euler-xyx 1.0 0.0 2.0)))) (list a b c)) ⇒ (0.0 0.0 3.0)
Procedure: make-intrinsic-xyz
alpha
beta
gamma
Procedure: make-intrinsic-xzy
alpha
beta
gamma
Procedure: make-intrinsic-yxz
alpha
beta
gamma
Procedure: make-intrinsic-yzx
alpha
beta
gamma
Procedure: make-intrinsic-zxy
alpha
beta
gamma
Procedure: make-intrinsic-zyx
alpha
beta
gamma
These functions return quaternions representing the given Tait-Bryan angle rotations.
Aliases for the corresponding
make-intrinsic-
procedures.
The extrinsic angle sets represent arbitrary rotations as a sequence of three rotations about fixed-frame axes (i.e. the axes do not rotate with the body).
There are twelve possible extrinsic angle sets, and each is the dual of
an intrinsic set. The extrinsic rotation about axes A
, B
,
and C
by angles a
, b
, and c
is the same as
the intrinsic rotation about axes C
, B
, and A
by
angles c
, b
, and a
, with the order of the three
axes reversed.
These functions decompose the rotation represented by
q
into extrinsic angles of the given set and returns the three angles as multiple values.
Procedure: make-extrinsic-xyx
gamma
beta
alpha
Procedure: make-extrinsic-xyz
gamma
beta
alpha
Procedure: make-extrinsic-xzx
gamma
beta
alpha
Procedure: make-extrinsic-xzy
gamma
beta
alpha
Procedure: make-extrinsic-yxy
gamma
beta
alpha
Procedure: make-extrinsic-yxz
gamma
beta
alpha
Procedure: make-extrinsic-yzx
gamma
beta
alpha
Procedure: make-extrinsic-yzy
gamma
beta
alpha
Procedure: make-extrinsic-zxy
gamma
beta
alpha
Procedure: make-extrinsic-zxz
gamma
beta
alpha
Procedure: make-extrinsic-zyx
gamma
beta
alpha
Procedure: make-extrinsic-zyz
gamma
beta
alpha
These functions return quaternions representing the given extrinsic angle rotations.
Aliases for
extrinsic-xyz
andmake-extrinsic-xyz
.(let ((r (make-rpy 0.12 -0.23 0.34))) (let-values (((a b c) (tait-bryan-zyx r))) (list a b c))) ⇒ (0.3400000000000001 -0.2300000000000001 0.12000000000000002)
Procedure: rotate-vector
rq
vq
Applies the rotation represented by quaternion
rq
to the vector represented by vector quaternionvq
, and returns the rotated vector. This is equivalent to(* rq vq (conjugate rq))
for normalizedrq
.(rotate-vector +k +2i) ⇒ -2i (rotate-vector 1/2+1/2i+1/2j+1/2k +i+2j+3k) ⇒ +3.0i+1.0j+2.0k
Procedure: make-rotation-procedure
rq
A partial application of
rotate-vector
. Returns a single-argument procedure which will take a vector quaternion argument and rotate it byrq
. The returned procedure closes over bothrq
and its conjugate, so this will likely be more efficient thanrotate-vector
at rotating many vectors by the same rotation.