Skip to content

Commit bdf34b9

Browse files
hyrodiumsethaxen
andauthored
Remove rotation-related functions (#111)
* remove rotation-realted functions * remove exports for rotations * fix tests for rotations * add an example page for rotation with quaternion * replace rotationmatrix with rotmatrix_from_quat * Apply suggestion (fix english and LaTeX) Co-authored-by: Seth Axen <[email protected]> * Apply suggestion (fix english) Co-authored-by: Seth Axen <[email protected]> * apply english suggestion from code review * update the documentation for basic rotation with unit quaternion * add U(1,\mathbb{H}) * update the symbols for rotating a vector * update around SO(3) and SU(2) * fix typo * fix LaTeX command * fix typo * add more argument type specification in rotations.md Co-authored-by: Seth Axen <[email protected]>
1 parent d4494f2 commit bdf34b9

File tree

6 files changed

+195
-214
lines changed

6 files changed

+195
-214
lines changed

docs/make.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ makedocs(;
1515
pages=[
1616
"Home" => "index.md",
1717
"APIs" => "api.md",
18-
"Examples" => ["examples/dual_quaternions.md"],
18+
"Examples" => [
19+
"examples/rotations.md",
20+
"examples/dual_quaternions.md"
21+
],
1922
],
2023
)
2124

docs/src/examples/dual_quaternions.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,18 @@ function transform(d::DualQuaternion, p::AbstractVector)
8888
return pnew
8989
end
9090
91+
function rotmatrix_from_quat(q::Quaternion)
92+
sx, sy, sz = 2q.s * q.v1, 2q.s * q.v2, 2q.s * q.v3
93+
xx, xy, xz = 2q.v1^2, 2q.v1 * q.v2, 2q.v1 * q.v3
94+
yy, yz, zz = 2q.v2^2, 2q.v2 * q.v3, 2q.v3^2
95+
r = [1 - (yy + zz) xy - sz xz + sy;
96+
xy + sz 1 - (xx + zz) yz - sx;
97+
xz - sy yz + sx 1 - (xx + yy)]
98+
return r
99+
end
100+
91101
function transformationmatrix(d::DualQuaternion)
92-
R = rotationmatrix(rotation_part(d))
102+
R = rotmatrix_from_quat(rotation_part(d))
93103
t = translation(d; first=false)
94104
T = similar(R, 4, 4)
95105
T[1:3, 1:3] .= R

docs/src/examples/rotations.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Rotations with quaternions
2+
3+
One of the most useful application of quaternions is representation of 3D-rotations.
4+
See also [Rotations.jl documentation](https://juliageometry.github.io/Rotations.jl/stable/3d_quaternion/)
5+
6+
```@example rotation
7+
using Quaternions
8+
using LinearAlgebra
9+
```
10+
11+
## Basics
12+
A 3D rotation can be represented by a [unit quaternion (versor)](https://en.wikipedia.org/wiki/Versor).
13+
For example, a 90° rotation around the ``y``-axis is ``q = \frac{1}{\sqrt{2}} + 0i + \frac{1}{\sqrt{2}} j + 0k``.
14+
Rotations with quaternions have the following properties:
15+
16+
* A unit quaternion (4 real numbers) is more efficient for representing a rotation than a rotation matrix (9 real numbers).
17+
* This results in higher computational performance in terms of time, memory usage, and accuracy.
18+
* The negative of a unit quaternion represents the same rotation.
19+
* The conjugate of a unit quaternion represents the inverse rotation.
20+
* The quaternion has unit length, so conjugate and multiplicative inverse is the same.
21+
* The set of unit quaternion ``\left\{w + ix + jy + kz \in \mathbb{H} \ | \ x, y, z \in \mathbb{R} \right\} = U(1,\mathbb{H}) \simeq S^3`` forms a group, and the group is homomorphic to the following groups.
22+
* ``SU(2) = \{R \in \mathcal{M}(2,\mathbb{C}) \ | \ R R^{*} = I\}`` is isomorphic to ``U(1,\mathbb{H})``.
23+
* ``SO(3) = \{R \in \mathcal{M}(3,\mathbb{R}) \ | \ R R^\top = I\}`` is homomorphic to ``U(1,\mathbb{H})``, and the mapping ``U(1,\mathbb{H}) \to SO(3)`` is double covering.
24+
25+
## Rotation around a vector
26+
A ``\theta`` rotation around a unit vector ``v = (v_x, v_y, v_z)`` can be obtained as
27+
```math
28+
q = \cos(\theta/2) + \sin(\theta/2)(iv_x + jv_y + kv_z).
29+
```
30+
31+
```@example rotation
32+
function quat_from_axisangle(axis::AbstractVector, theta::Real)
33+
if length(axis) != 3
34+
error("Must be a 3-vector")
35+
end
36+
s, c = sincos(theta / 2)
37+
axis = normalize(axis)
38+
return Quaternion(c, s*axis[1], s*axis[2], s*axis[3])
39+
end
40+
nothing # hide
41+
```
42+
43+
```@repl rotation
44+
q1 = quat_from_axisangle([0,1,0], deg2rad(90)) # 90° rotation around y-axis
45+
q2 = quat_from_axisangle([1,1,1], deg2rad(120))
46+
q3 = -q2 # additive inverse quaternion represents the same rotation
47+
```
48+
49+
## Rotate a vector with a quaternion
50+
A vector ``u = (u_x, u_y, u_z)`` can be rotated by a unit quaternion ``q``.
51+
The rotated vector ``v = (v_x, v_y, v_z)`` can be obtained as
52+
```math
53+
\begin{aligned}
54+
q_u &= iu_x + ju_y + ku_z \\
55+
q_v &= q q_u \bar{q} = 0 + iv_x + jv_y + kv_z \\
56+
v &= (v_x, v_y, v_z).
57+
\end{aligned}
58+
```
59+
60+
```@example rotation
61+
function rotate_vector(q::Quaternion, u::AbstractVector)
62+
if length(u) != 3
63+
error("Must be a 3-vector")
64+
end
65+
q_u = Quaternion(0, u[1], u[2], u[3])
66+
q_v = q*q_u*conj(q)
67+
return [imag_part(q_v)...]
68+
end
69+
nothing # hide
70+
```
71+
72+
```@repl rotation
73+
rotate_vector(q1, [1,2,3])
74+
rotate_vector(q2, [1,2,3])
75+
rotate_vector(q3, [1,2,3]) # Same as q2
76+
```
77+
78+
## Convert a quaternion to a rotation matrix
79+
A unit quaternion can be converted to a rotation matrix.
80+
81+
```@example rotation
82+
function rotmatrix_from_quat(q::Quaternion)
83+
sx, sy, sz = 2q.s * q.v1, 2q.s * q.v2, 2q.s * q.v3
84+
xx, xy, xz = 2q.v1^2, 2q.v1 * q.v2, 2q.v1 * q.v3
85+
yy, yz, zz = 2q.v2^2, 2q.v2 * q.v3, 2q.v3^2
86+
r = [1 - (yy + zz) xy - sz xz + sy;
87+
xy + sz 1 - (xx + zz) yz - sx;
88+
xz - sy yz + sx 1 - (xx + yy)]
89+
return r
90+
end
91+
nothing # hide
92+
```
93+
94+
```@repl rotation
95+
m1 = rotmatrix_from_quat(q1)
96+
m2 = rotmatrix_from_quat(q2)
97+
m3 = rotmatrix_from_quat(q3) # Same as q2
98+
```
99+
100+
This function does not return [`StaticMatrix`](https://juliaarrays.github.io/StaticArrays.jl/dev/pages/api/#StaticArraysCore.StaticArray), so the implementation is not much effective.
101+
If you need more performance, please consider using [Rotations.jl](https://github.com/JuliaGeometry/Rotations.jl).
102+
103+
## Convert a rotation matrix to a quaternion
104+
A rotation matrix can be converted to a unit quaternion.
105+
The following implementation is based on [https://arxiv.org/pdf/math/0701759.pdf](https://arxiv.org/pdf/math/0701759.pdf).
106+
Note that the following mapping ``SO(3) \to SU(2)`` is not surjective.
107+
108+
```@example rotation
109+
function quat_from_rotmatrix(dcm::AbstractMatrix{T}) where {T<:Real}
110+
a2 = 1 + dcm[1,1] + dcm[2,2] + dcm[3,3]
111+
a = sqrt(a2)/2
112+
b,c,d = (dcm[3,2]-dcm[2,3])/4a, (dcm[1,3]-dcm[3,1])/4a, (dcm[2,1]-dcm[1,2])/4a
113+
return Quaternion(a,b,c,d)
114+
end
115+
nothing # hide
116+
```
117+
118+
```@repl rotation
119+
quat_from_rotmatrix(m1)
120+
quat_from_rotmatrix(m2)
121+
quat_from_rotmatrix(m3)
122+
quat_from_rotmatrix(m1) ≈ q1
123+
quat_from_rotmatrix(m2) ≈ q2
124+
quat_from_rotmatrix(m3) ≈ q3 # q2 == -q3
125+
```
126+
127+
## Interpolate two rotations (slerp)
128+
Slerp (spherical linear interpolation) is a method to interpolate between two unit quaternions.
129+
This function [`slerp`](@ref) equates antipodal points, and interpolates the shortest path.
130+
Therefore, the output `slerp(q1, q2, 1)` may be different from `q2`. (`slerp(q1, q2, 0)` is always equal to `q1`.)
131+
132+
```@repl rotation
133+
slerp(q1, q2, 0) ≈ q1
134+
slerp(q1, q2, 1) ≈ q2
135+
slerp(q1, q3, 1) ≈ q3
136+
slerp(q1, q3, 1) ≈ -q3
137+
r = slerp(q1, q2, 1/2)
138+
abs(q1-r) ≈ abs(q2-r) # Same distance
139+
abs(r) # Interpolates on the unit sphere S³
140+
```

src/Quaternion.jl

Lines changed: 0 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -187,20 +187,6 @@ Base.:(==)(q::Quaternion, w::Quaternion) = (q.s == w.s) & (q.v1 == w.v1) & (q.v2
187187

188188
angleaxis(q::Quaternion) = angle(q), axis(q)
189189

190-
function Base.angle(q::Quaternion)
191-
Base.depwarn("`Base.angle(::Quaternion)` is deprecated. Please consider using Rotations package instead.", :angle)
192-
2 * atan(abs_imag(q), real(q))
193-
end
194-
195-
function axis(q::Quaternion)
196-
Base.depwarn("`axis(::Quaternion)` is deprecated. Please consider using Rotations package instead.", :axis)
197-
q = sign(q)
198-
s = sin(angle(q) / 2)
199-
abs(s) > 0 ?
200-
[q.v1, q.v2, q.v3] / s :
201-
[1.0, 0.0, 0.0]
202-
end
203-
204190
"""
205191
extend_analytic(f, q::Quaternion)
206192
@@ -299,81 +285,6 @@ function Base.randn(rng::AbstractRNG, ::Type{Quaternion{T}}) where {T<:AbstractF
299285
)
300286
end
301287

302-
## Rotations
303-
304-
function qrotation(axis::AbstractVector{T}, theta) where {T <: Real}
305-
Base.depwarn("`qrotation(::AbstractVector)` is deprecated. Please consider using Rotations package instead.", :qrotation)
306-
if length(axis) != 3
307-
error("Must be a 3-vector")
308-
end
309-
normaxis = norm(axis)
310-
if iszero(normaxis)
311-
normaxis = oneunit(normaxis)
312-
theta = zero(theta)
313-
end
314-
s,c = sincos(theta / 2)
315-
scaleby = s / normaxis
316-
Quaternion(c, scaleby * axis[1], scaleby * axis[2], scaleby * axis[3])
317-
end
318-
319-
# Variant of the above where norm(rotvec) encodes theta
320-
function qrotation(rotvec::AbstractVector{T}) where {T <: Real}
321-
Base.depwarn("`qrotation(::AbstractVector)` is deprecated. Please consider using Rotations package instead.", :qrotation)
322-
if length(rotvec) != 3
323-
error("Must be a 3-vector")
324-
end
325-
theta = norm(rotvec)
326-
s,c = sincos(theta / 2)
327-
scaleby = s / (iszero(theta) ? one(theta) : theta)
328-
Quaternion(c, scaleby * rotvec[1], scaleby * rotvec[2], scaleby * rotvec[3])
329-
end
330-
331-
function qrotation(dcm::AbstractMatrix{T}) where {T<:Real}
332-
Base.depwarn("`qrotation(::AbstractMatrix)` is deprecated. Please consider using Rotations package instead.", :qrotation)
333-
# See https://arxiv.org/pdf/math/0701759.pdf
334-
a2 = 1 + dcm[1,1] + dcm[2,2] + dcm[3,3]
335-
b2 = 1 + dcm[1,1] - dcm[2,2] - dcm[3,3]
336-
c2 = 1 - dcm[1,1] + dcm[2,2] - dcm[3,3]
337-
d2 = 1 - dcm[1,1] - dcm[2,2] + dcm[3,3]
338-
339-
if a2 max(b2, c2, d2)
340-
a = sqrt(a2)/2
341-
b,c,d = (dcm[3,2]-dcm[2,3])/4a, (dcm[1,3]-dcm[3,1])/4a, (dcm[2,1]-dcm[1,2])/4a
342-
elseif b2 max(c2, d2)
343-
b = sqrt(b2)/2
344-
a,c,d = (dcm[3,2]-dcm[2,3])/4b, (dcm[2,1]+dcm[1,2])/4b, (dcm[1,3]+dcm[3,1])/4b
345-
elseif c2 d2
346-
c = sqrt(c2)/2
347-
a,b,d = (dcm[1,3]-dcm[3,1])/4c, (dcm[2,1]+dcm[1,2])/4c, (dcm[3,2]+dcm[2,3])/4c
348-
else
349-
d = sqrt(d2)/2
350-
a,b,c = (dcm[2,1]-dcm[1,2])/4d, (dcm[1,3]+dcm[3,1])/4d, (dcm[3,2]+dcm[2,3])/4d
351-
end
352-
if a > 0
353-
return Quaternion(a,b,c,d)
354-
else
355-
return Quaternion(-a,-b,-c,-d)
356-
end
357-
end
358-
359-
function qrotation(dcm::AbstractMatrix{T}, qa::Quaternion) where {T<:Real}
360-
Base.depwarn("`qrotation(::AbstractMatrix, ::Quaternion)` is deprecated. Please consider using Rotations package instead.", :qrotation)
361-
q = qrotation(dcm)
362-
abs(q-qa) < abs(q+qa) ? q : -q
363-
end
364-
365-
rotationmatrix(q::Quaternion) = rotationmatrix_normalized(sign(q))
366-
367-
function rotationmatrix_normalized(q::Quaternion)
368-
Base.depwarn("`rotationmatrix_normalized(::Quaternion)` is deprecated. Please consider using Rotations package instead.", :rotationmatrix_normalized)
369-
sx, sy, sz = 2q.s * q.v1, 2q.s * q.v2, 2q.s * q.v3
370-
xx, xy, xz = 2q.v1^2, 2q.v1 * q.v2, 2q.v1 * q.v3
371-
yy, yz, zz = 2q.v2^2, 2q.v2 * q.v3, 2q.v3^2
372-
[1 - (yy + zz) xy - sz xz + sy;
373-
xy + sz 1 - (xx + zz) yz - sx;
374-
xz - sy yz + sx 1 - (xx + yy)]
375-
end
376-
377288
"""
378289
slerp(qa::Quaternion, qb::Quaternion, t::Real)
379290

src/Quaternions.jl

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,7 @@ module Quaternions
1313
QuaternionF64
1414
export quat
1515
export imag_part
16-
export angleaxis
17-
export angle
18-
export axis
1916
export quatrand
2017
export nquatrand
21-
export qrotation
22-
export rotationmatrix
2318
export slerp
2419
end

0 commit comments

Comments
 (0)