Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove DualQuaternion #92

Merged
merged 22 commits into from
Oct 6, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
name = "Quaternions"
uuid = "94ee1d12-ae83-5a48-8b1c-48b8ff168ae0"
version = "0.5.6"
version = "0.6.0-DEV"

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

[compat]
DualNumbers = "0.5, 0.6"
julia = "1"

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

[targets]
test = ["ForwardDiff", "Test"]
test = ["Test"]
5 changes: 5 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
Quaternions = "94ee1d12-ae83-5a48-8b1c-48b8ff168ae0"

[compat]
ForwardDiff = "0.10"
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ makedocs(;
),
pages=[
"Home" => "index.md",
"Examples" => ["examples/dual_quaternions.md"],
],
)

Expand Down
159 changes: 159 additions & 0 deletions docs/src/examples/dual_quaternions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Dual quaternions

## Introduction

The [dual quaternions](https://en.wikipedia.org/wiki/Dual_quaternion) are an example of "biquaternions."
hyrodium marked this conversation as resolved.
Show resolved Hide resolved
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

```math
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
```

or as a quaternion where the scalar part and three imaginary parts are all dual numbers

```math
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.
```

Like unit quaternions can compactly representation rotations in 3D space, dual quaternions can compactly represent rigid transformations (rotation with translation).

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:

!!! note
Previously this package contained a specialized `DualQuaternion` type.
This was removed in v0.6.0 because offered nothing extra over composing [ForwardDiff](https://github.com/JuliaDiff/ForwardDiff.jl) and Quaternions.

## Utility functions

First let's load the packages:

```@example dualquat
using ForwardDiff, Quaternions, Random
sethaxen marked this conversation as resolved.
Show resolved Hide resolved
```

Then we'll create some utility types/functions:

```@example dualquat
const DualQuaternion{T} = Quaternion{ForwardDiff.Dual{Nothing,T,1}}

purequat(p::AbstractVector) = quat(false, @views(p[begin:begin+2])...)

dual(x::Real, v::Real) = ForwardDiff.Dual(x, v)

function dualquat(_q0::Union{Real,Quaternion}, _qe::Union{Real,Quaternion})
q0 = quat(_q0)
qe = quat(_qe)
Quaternion(
dual(real(q0), real(qe)),
dual.(imag_part(q0), imag_part(qe))...,
)
end

function primal(d::DualQuaternion)
return Quaternion(
ForwardDiff.value(real(d)),
ForwardDiff.value.(imag_part(d))...,
)
end

function tangent(d::DualQuaternion)
return Quaternion(
ForwardDiff.partials(real(d), 1),
ForwardDiff.partials.(imag_part(d), 1)...,
)
end

function Base.conj(d::DualQuaternion, type::Int)
sethaxen marked this conversation as resolved.
Show resolved Hide resolved
type == 1 && return conj(d)
type == 2 && return dualquat(primal(d), -tangent(d))
de = tangent(d)
type == 3 && return dualquat(conj(primal(d)), quat(-de.s, imag_part(de)...))
throw(ArgumentError("type=$type is not recognized. type must be in {1,2,3}."))
end

rotation_part(d::DualQuaternion) = primal(d)

translation_part(d::DualQuaternion) = dualquat(true, conj(rotation_part(d)) * tangent(d))

# first=true returns the translation performed before the rotation: R(p+t)
# first=false returns the translation performed after the rotation: R(p)+t
function translation(d::DualQuaternion; first::Bool=true)
v = first ? primal(d)' * tangent(d) : tangent(d) * primal(d)'
return collect(2 .* imag_part(v))
end

function transform(d::DualQuaternion, p::AbstractVector)
dp = dualquat(true, purequat(p))
dpnew = d * dp * conj(d, 3)
pnew_parts = imag_part(tangent(dpnew))
pnew = similar(p, eltype(pnew_parts))
pnew .= pnew_parts
return pnew
end

function transformationmatrix(d::DualQuaternion)
R = rotationmatrix(rotation_part(d))
t = translation(d; first=false)
T = similar(R, 4, 4)
T[1:3, 1:3] .= R
T[1:3, 4] .= t
T[4, 1:3] .= 0
T[4, 4] = 1
return T
end

randdualquat(rng::AbstractRNG,T=Float64) = dualquat(rand(rng, Quaternion{T}), rand(rng, Quaternion{T}))
randdualquat(T=Float64) = randdualquat(Random.GLOBAL_RNG,T)
```
sethaxen marked this conversation as resolved.
Show resolved Hide resolved

## Example: transforming a point

Now we'll create a dual quaternion.
```@example dualquat
x = sign(randdualquat())
```

`sign(q) == q / abs(q)` both normalizes the primal part of the dual quaternion and makes the tangent part perpendicular to it.

```@example dualquat
abs(primal(x)) ≈ 1
isapprox(real(primal(x)' * tangent(x)), 0; atol=1e-10)
```
sethaxen marked this conversation as resolved.
Show resolved Hide resolved

Here's how we use dual quaternions to transform a point:

```@example dualquat
p = randn(3)
```

```@example
sethaxen marked this conversation as resolved.
Show resolved Hide resolved
transform(x, p)
```

We can check that this gives the same result as transforming with an affine transformation matrix:

```@example dualquat
transform(x, p) ≈ (transformationmatrix(x) * vcat(p, 1))[1:3]
```

sethaxen marked this conversation as resolved.
Show resolved Hide resolved
## Example: motion planning

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.
Similarly, we can interpolate between unit dual quaternions to plan motion between two rigid poses.
Conveniently, we can do this using the exact same `slerp` implementation.

```@example dualquat
y = sign(randdualquat())
```

```@example dualquat
slerp(x, y, 0) ≈ x
```

```@example dualquat
slerp(x, y, 1) ≈ y
```

```@example dualquat
slerp(x, y, 0.3)
```
2 changes: 1 addition & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Quaternions.jl

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)
A Julia package implementing [quaternions](https://en.wikipedia.org/wiki/Quaternion) and [octonions](https://en.wikipedia.org/wiki/Octonion).

!!! note "Documentation"
The documentation is still work in progress.
Expand Down
177 changes: 0 additions & 177 deletions src/DualQuaternion.jl

This file was deleted.

Loading