Skip to content

Commit 2766eeb

Browse files
committed
Docs: Guide about quaternion rotations
1 parent c1cfc1a commit 2766eeb

File tree

3 files changed

+196
-2
lines changed

3 files changed

+196
-2
lines changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ makedocs(
4747
pages = [
4848
"Introduction" => "index.md",
4949
"Tutorial" => "tutorial-cubecopter.md",
50+
"Guides" => ["guide-quaternions.md"],
5051
"Reference" => [
5152
"Quaternions" => "ref-quaternions.md",
5253
"DeformableBodies" => "ref-models.md"

docs/src/guide-quaternions.md

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Representing rotations by quaternions
2+
3+
```@setup quat
4+
using DeformableBodies
5+
```
6+
7+
The most usual ways to represent a rotation in three-dimensional space
8+
are [rotations matrices](https://en.wikipedia.org/wiki/Rotation_matrix)
9+
or [Euler angles](https://en.wikipedia.org/wiki/Euler_angles).
10+
In this package, nevertheless,
11+
they are represented using unit quaternions.
12+
13+
Quaternions are a four-dimensional non-commutative algebra
14+
with the property that any rotation may be represented by a quaternion of norm 1.
15+
Some of their advantages consist in that we only need 4 parameters to represent a quaternion
16+
(as opposed to the 9 needed by rotation matrices)
17+
and that they do not suffer from the phenomenon of
18+
[gimbal lock](https://en.wikipedia.org/wiki/Gimbal_lock)
19+
(as opposed to Euler angles).
20+
21+
If a quaternion ``q`` represents a rotation matrix ``R``,
22+
their action on a vector is defined as
23+
```math
24+
R v = q v q^{-1}.
25+
```
26+
27+
!!! warning
28+
This package assumes that your coordinate system follows the right-hand rule.
29+
If, for whatever reason, you want to use a left-handed system,
30+
``q`` and ``q^{-1}`` must be transposed on the above formula.
31+
32+
## The `rotate` function
33+
34+
All rotations on `DeformableBodies.jl` are done by the function [`rotate`](@ref).
35+
Its signature consists of
36+
37+
```julia
38+
rotate(v::Vector, q::Quaternion, center::Vector)
39+
```
40+
41+
This function rotates the vector `v` by the rotation represented by `q`
42+
in the frame of reference centered on `center`.
43+
44+
If the third argument is left blank, it defaults to the origin.
45+
46+
```@repl quat
47+
v = [3,0,0]
48+
q = Quaternion(1,2,3,4)
49+
rotate(v, q, [0,0,0])
50+
rotate(v, q)
51+
```
52+
53+
## Representing the identity rotation
54+
The identity rotation is the matrix ``I`` satisfying ``I v = v``
55+
for all ``v``.
56+
This is represented by the quaternion ``1``,
57+
which can be gotten using Julia's multiple dispatch via the expression
58+
`one(Quaternion)`.
59+
60+
```@repl quat
61+
q = one(Quaternion)
62+
rotate([1,2,3], q)
63+
```
64+
65+
## Composition of rotations
66+
67+
The composition of rotations translates to the quaternion world
68+
as the product of quaternions.
69+
Thus, it is the same to rotate a vector by ``q_1`` and then by ``q_2``
70+
and to rotate by ``q_2 * q_1``.
71+
72+
```@repl quat
73+
v = [9, 0, 0]
74+
q1 = Quaternion(1,2,3,4)
75+
q2 = Quaternion(4,3,2,1)
76+
rotate(v, q2*q1)
77+
rotate(rotate(v, q1), q2)
78+
```
79+
80+
!!! note
81+
Rotations are non-commutative.
82+
Applying ``q_2`` after ``q_1`` is the same as multiplying
83+
``q_2`` to the **left** of ``q_1``.
84+
85+
## Axis-angle representation
86+
87+
An important property of three-dimensional space it that every rotation fixes a line.
88+
Therefore, they accept an axis-angle representation.
89+
That is, a rotation ``R`` is defined by a unit vector ``\hat{n}`` and an angle ``\theta``
90+
such that ``R`` rotates a vector counterclockwise by an amount of ``\theta`` around the line defined by ``\hat{n}``.
91+
92+
To get the quaternion representing an rotation of `θ` around `n`,
93+
use the method [`axistoquaternion`](@ref).
94+
You may pass an unormalized vector and the method will normalize it.
95+
96+
```@repl quat
97+
q = axistoquaternion(axis = [0,0,2], angle = π/2)
98+
rotate([1,0,0], q)
99+
```
100+
101+
In fact, this combination is so useful that the function `rotate`
102+
is overloaded to directly convert from an axis-angle pair.
103+
104+
```@repl quat
105+
rotate([1,0,0], axis=[0,0,1], angle=π/2)
106+
```
107+
108+
!!! info
109+
This version of [`rotate`](@ref) also accepts the optional argument `center`,
110+
which defaults to the origin.
111+
```@repl quat
112+
rotate([1,0,0], axis=[0,0,1], angle=π/2, center=[0,1,0])
113+
```
114+
115+
## Converting between matrices and quaternions
116+
117+
Sometimes a rotation may already come represented as a matrix
118+
or the transition rotations from [`solve!`](@ref) may be needed in matrix form some application.
119+
To deal with these cases,
120+
the package provides two auxiliary functions
121+
[`matrixtoquaternion`](@ref) and [`quaterniontomatrix`](@ref).
122+
123+
To convert a rotation matrix to a unit quaternion, do
124+
```@repl quat
125+
R = [cos(π/4) -sin(π/4) 0;
126+
sin(π/4) cos(π/4) 0;
127+
0 0 1]
128+
R * [sqrt(2), 0, 0]
129+
q = matrixtoquaternion(R)
130+
rotate([sqrt(2), 0, 0], q)
131+
```
132+
Some minor differences may occur due to floating-point rounding errors.
133+
134+
!!! danger
135+
The function [`matrixtoquaternion`](@ref)
136+
assumes that the input is a _rotation matrix_
137+
but, for efficency reasons, no check is done in this regard.
138+
If you do not make sure beforehand that the matrix is orthogonal,
139+
bad things may happen.
140+
141+
To convert a quaternion to a matrix simply do
142+
```@repl quat
143+
q = Quaternion(1,2,3,4)
144+
R = quaterniontomatrix(q)
145+
rotate([3,0,0], q)
146+
R * [3,0,0]
147+
```
148+
149+
The function [`quaterniontomatrix`](@ref)
150+
works for every quaternion,
151+
and does not require the input to be a unit quaternion.
152+
153+
!!! note
154+
There is a **unique** rotation matrix representing a given quaternion
155+
but there are **two** unit quaternions representing the same matrix.
156+
157+
This means that `quaterniontomatrix ∘ matrixtoquaternion` equals the identity
158+
(disconsidering floating-point rouding errors)
159+
but the opposite is not in general true.
160+
For a simple example.
161+
```@repl quat
162+
q = Quaternion(-1.0)
163+
(matrixtoquaternion ∘ quaterniontomatrix)(q)
164+
```
165+
166+
Nevertheless, both these quaternions produce the same rotations.
167+
168+
## Rotating a PointMass
169+
170+
All the previous functionalities only require the submodule [`Quaternions`](@ref)
171+
and work directly with vectors.
172+
Nevertheless, the models on `DeformableBodies.jl` are constructed with respect
173+
to the type [`PointMass`](@ref).
174+
To help with that,
175+
the function rotate is overloaded to directly rotate the position of a `PointMass`
176+
without interfering with its mass.
177+
178+
```julia
179+
rotate(p::PointMass, q::Quaternion, center::Vector)
180+
rotate(p::PointMass; axis::Vector, angle::Real, center::Vector)
181+
```
182+
183+
The usage is identical to the version for vectors including the fact that
184+
the argument `center` must be a Vector and not another PointMass.
185+
186+
An usual application consists in rotating a body around its center of mass.
187+
```@repl quat
188+
body = [ PointMass(rand(), rand(3)) for i in 1:5 ]
189+
center_of_mass(body)
190+
q = Quaternion(1,2,3,4)
191+
rotated = [ rotate(p, q, center_of_mass(body)) for p in body ]
192+
center_of_mass(rotated)
193+
```

src/Quaternions.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,12 +328,12 @@ end
328328
matrixtoquaternion(R)
329329
330330
Given a rotation matrix `R`,
331-
return a quaternion `q` such that `rotate(v,q) = R*v` for all `v`.
331+
return a unit quaternion `q` such that `rotate(v,q) = R*v` for all `v`.
332332
333333
The matrix `R` is assumed to be orthogonal
334334
but, for efficiency reasons, no check is made to guarantee that.
335335
336-
Since there are, in general, two quaternions representing the same rotation matrix,
336+
Since there are, in general, two unit quaternions representing the same rotation matrix,
337337
it is not guaranteed that `matrixtoquaternion ∘ quaterniontomatrix` equals the identity.
338338
"""
339339
function matrixtoquaternion(r)

0 commit comments

Comments
 (0)