Skip to content

Commit 5758dad

Browse files
sethaxenhyrodium
andauthored
Remove DualQuaternion (#92)
* Remove DualQuaternion code * Remove DualQuaternion tests * Remove DualNumbers dependency * Remove dual quaternions from docs * Remove ForwardDiff from extras * Document example of dual quaternions * Mark as dev version * Apply suggestions from code review Co-authored-by: Yuto Horikawa <[email protected]> * Update README.md * Rename conj to dualconj * Set correct example name * Update docs/src/examples/dual_quaternions.md * Apply suggestions from code review Co-authored-by: Yuto Horikawa <[email protected]> * Add missing word * Use repl mode * Move transformation matrix check to that section * Remove redundant y creation * Describe affine transformation matrix * Specify that it's unit Co-authored-by: Yuto Horikawa <[email protected]>
1 parent b1f50c9 commit 5758dad

File tree

10 files changed

+188
-637
lines changed

10 files changed

+188
-637
lines changed

Project.toml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
name = "Quaternions"
22
uuid = "94ee1d12-ae83-5a48-8b1c-48b8ff168ae0"
3-
version = "0.5.7"
3+
version = "0.6.0-DEV"
44

55
[deps]
6-
DualNumbers = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74"
76
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
87
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
98

109
[compat]
11-
DualNumbers = "0.5, 0.6"
1210
julia = "1"
1311

1412
[extras]
15-
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
1613
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1714

1815
[targets]
19-
test = ["ForwardDiff", "Test"]
16+
test = ["Test"]

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Quaternions.jl
2-
A Julia module with quaternion, octonion and dual-quaternion functionality
2+
A Julia implementation of quaternions.
33

44
[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://JuliaGeometry.github.io/Quaternions.jl/stable)
55
[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://JuliaGeometry.github.io/Quaternions.jl/dev)
@@ -62,5 +62,5 @@ Implemented functions are:
6262
rand
6363
randn
6464

65-
Currently, this package supports `DualQuaternion` and `Octonion` types, but these will be removed in the next breaking release.
66-
See https://github.com/JuliaGeometry/Quaternions.jl/issues/90 and https://github.com/JuliaGeometry/Quaternions.jl/pull/92 for more information.
65+
Currently, this package supports the `Octonion` type, but this will be removed in the next breaking release.
66+
See https://github.com/JuliaGeometry/Quaternions.jl/issues/90 for more information.

docs/Project.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
[deps]
22
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
3+
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
4+
Quaternions = "94ee1d12-ae83-5a48-8b1c-48b8ff168ae0"
5+
6+
[compat]
7+
ForwardDiff = "0.10"

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ makedocs(;
1414
),
1515
pages=[
1616
"Home" => "index.md",
17+
"Examples" => ["examples/dual_quaternions.md"],
1718
],
1819
)
1920

docs/src/examples/dual_quaternions.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Dual quaternions
2+
3+
## Introduction
4+
5+
The [dual quaternions](https://en.wikipedia.org/wiki/Dual_quaternion) are an example of "biquaternions."
6+
They can be represented equivalently either as a [dual number](https://en.wikipedia.org/wiki/Dual_number) where both both the "primal" and "tangent" part are quaternions
7+
8+
```math
9+
d = q_0 + q_e \epsilon = (s_0 + a_0 i + b_0 j + c_0 k) + (s_e + a_e i + b_e j + c_e k) \epsilon
10+
```
11+
12+
or as a quaternion where the scalar part and three imaginary parts are all dual numbers
13+
14+
```math
15+
d = s + ai + bj + ck = (s_0 + s_e \epsilon) + (a_0 + a_e \epsilon) i + (b_0 + b_e \epsilon) j + (c_0 + c_e \epsilon) k.
16+
```
17+
18+
Like unit quaternions can compactly representation rotations in 3D space, dual quaternions can compactly represent rigid transformations (rotation with translation).
19+
20+
Without any special glue code, we can construct a dual quaternion by composing `ForwardDiff.Dual` and [`Quaternion`](@ref); this uses the second representation described above:
21+
22+
!!! note
23+
Previously this package contained a specialized `DualQuaternion` type.
24+
This was removed in v0.6.0 because it offered nothing extra over composing [ForwardDiff](https://github.com/JuliaDiff/ForwardDiff.jl) and Quaternions.
25+
26+
## Utility functions
27+
28+
First let's load the packages:
29+
30+
```@example dualquat
31+
using Quaternions, ForwardDiff, Random
32+
```
33+
34+
Then we'll create some utility types/functions:
35+
36+
```@example dualquat
37+
const DualQuaternion{T} = Quaternion{ForwardDiff.Dual{Nothing,T,1}}
38+
39+
purequat(p::AbstractVector) = quat(false, @views(p[begin:begin+2])...)
40+
41+
dual(x::Real, v::Real) = ForwardDiff.Dual(x, v)
42+
43+
function dualquat(_q0::Union{Real,Quaternion}, _qe::Union{Real,Quaternion})
44+
q0 = quat(_q0)
45+
qe = quat(_qe)
46+
Quaternion(
47+
dual(real(q0), real(qe)),
48+
dual.(imag_part(q0), imag_part(qe))...,
49+
)
50+
end
51+
52+
function primal(d::DualQuaternion)
53+
return Quaternion(
54+
ForwardDiff.value(real(d)),
55+
ForwardDiff.value.(imag_part(d))...,
56+
)
57+
end
58+
59+
function tangent(d::DualQuaternion)
60+
return Quaternion(
61+
ForwardDiff.partials(real(d), 1),
62+
ForwardDiff.partials.(imag_part(d), 1)...,
63+
)
64+
end
65+
66+
function dualconj(d::DualQuaternion)
67+
de = tangent(d)
68+
return dualquat(conj(primal(d)), quat(-real(de), imag_part(de)...))
69+
end
70+
71+
rotation_part(d::DualQuaternion) = primal(d)
72+
73+
translation_part(d::DualQuaternion) = dualquat(true, conj(rotation_part(d)) * tangent(d))
74+
75+
# first=true returns the translation performed before the rotation: R(p+t)
76+
# first=false returns the translation performed after the rotation: R(p)+t
77+
function translation(d::DualQuaternion; first::Bool=true)
78+
v = first ? primal(d)' * tangent(d) : tangent(d) * primal(d)'
79+
return collect(2 .* imag_part(v))
80+
end
81+
82+
function transform(d::DualQuaternion, p::AbstractVector)
83+
dp = dualquat(true, purequat(p))
84+
dpnew = d * dp * dualconj(d)
85+
pnew_parts = imag_part(tangent(dpnew))
86+
pnew = similar(p, eltype(pnew_parts))
87+
pnew .= pnew_parts
88+
return pnew
89+
end
90+
91+
function transformationmatrix(d::DualQuaternion)
92+
R = rotationmatrix(rotation_part(d))
93+
t = translation(d; first=false)
94+
T = similar(R, 4, 4)
95+
T[1:3, 1:3] .= R
96+
T[1:3, 4] .= t
97+
T[4, 1:3] .= 0
98+
T[4, 4] = 1
99+
return T
100+
end
101+
102+
randdualquat(rng::AbstractRNG,T=Float64) = dualquat(rand(rng, Quaternion{T}), rand(rng, Quaternion{T}))
103+
randdualquat(T=Float64) = randdualquat(Random.GLOBAL_RNG,T)
104+
nothing # hide
105+
```
106+
107+
## Example: transforming a point
108+
109+
Now we'll create a unit dual quaternion.
110+
```@repl dualquat
111+
x = sign(randdualquat())
112+
```
113+
114+
`sign(q) == q / abs(q)` both normalizes the primal part of the dual quaternion and makes the tangent part perpendicular to it.
115+
116+
```@repl dualquat
117+
abs(primal(x)) ≈ 1
118+
isapprox(real(primal(x)' * tangent(x)), 0; atol=1e-10)
119+
```
120+
121+
Here's how we use dual quaternions to transform a point:
122+
123+
```@repl dualquat
124+
p = randn(3)
125+
```
126+
127+
```@repl dualquat
128+
transform(x, p)
129+
```
130+
131+
## Example: homomorphism from unit dual quaternions to the transformation matrices
132+
133+
Each unit dual quaternion can be mapped to an affine transformation matrix ``T``.
134+
``T`` can be used to transform a vector ``p`` like this:
135+
136+
```math
137+
T \begin{pmatrix} p \\ 1\end{pmatrix} = \begin{pmatrix} R & t \\ 0^\mathrm{T} & 1\end{pmatrix} \begin{pmatrix} p \\ 1\end{pmatrix} = \begin{pmatrix} Rp + t \\ 1\end{pmatrix},
138+
```
139+
where ``R`` is a rotation matrix, and ``t`` is a translation vector.
140+
Our helper function `transformationmatrix` maps from a unit dual quaternion to such an affine matrix.
141+
142+
```@repl dualquat
143+
y = sign(randdualquat())
144+
```
145+
146+
```@repl dualquat
147+
X = transformationmatrix(x)
148+
Y = transformationmatrix(y)
149+
XY = transformationmatrix(x*y)
150+
X*Y ≈ XY
151+
```
152+
153+
We can check that our transformation using the unit dual quaternion gives the same result as transforming with an affine transformation matrix:
154+
155+
```@repl dualquat
156+
transform(x, p) ≈ (X * vcat(p, 1))[1:3]
157+
```
158+
159+
## Example: motion planning
160+
161+
For unit quaternions, spherical linear interpolation with [`slerp`](@ref) can be used to interpolate between two rotations with unit quaternions, which can be used to plan motion between two orientations.
162+
Similarly, we can interpolate between unit dual quaternions to plan motion between two rigid poses.
163+
Conveniently, we can do this using the exact same `slerp` implementation.
164+
165+
```@repl dualquat
166+
slerp(x, y, 0) ≈ x
167+
```
168+
169+
```@repl dualquat
170+
slerp(x, y, 1) ≈ y
171+
```
172+
173+
```@repl dualquat
174+
slerp(x, y, 0.3)
175+
```

docs/src/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Quaternions.jl
22

3-
A Julia package implementing [quaternions](https://en.wikipedia.org/wiki/Quaternion), [octonions](https://en.wikipedia.org/wiki/Octonion) and [dual-quaternions](https://en.wikipedia.org/wiki/Dual_quaternion)
3+
A Julia package implementing [quaternions](https://en.wikipedia.org/wiki/Quaternion) and [octonions](https://en.wikipedia.org/wiki/Octonion).
44

55
!!! note "Documentation"
66
The documentation is still work in progress.

0 commit comments

Comments
 (0)