3D Rotations (Quaternions)

This section provides the rotation geometry for the tomographic pipeline using quaternions robust to Gimbal-lock.

HeteroTomo3D.UnitQuaternionType
UnitQuaternion{T<:Real}

Represents a unit quaternion $\mathbf{q} = (\omega, x, y, z) \in \mathbb{S}^{3}$ for singularity-free 3D rotations.

source
Base.conjMethod
conj(q::UnitQuaternion{T}) -> UnitQuaternion{T}

For a unit quaternion $\mathbf{q} = (\omega, x, y, z)$, the inverse is equal to its conjugate $\mathbf{q}^{-1} = (\omega, -x, -y, -z)$.

source
Base.invMethod
inv(q::UnitQuaternion{T}) -> UnitQuaternion{T}

For a unit quaternion $\mathbf{q} = (\omega, x, y, z)$, the inverse is equal to its conjugate $\mathbf{q}^{-1} = (\omega, -x, -y, -z)$.

source
Base.:*Method
*(q1::UnitQuaternion{T}, q2::UnitQuaternion{T}) -> UnitQuaternion{T}

Overloads the multiplication operator to compose two unit quaternions 'q1' and 'q2'.

julia> using HeteroTomo3D

julia> q_id = UnitQuaternion(1.0, 0.0, 0.0, 0.0);

julia> q_test = UnitQuaternion(0.5, 0.5, 0.5, 0.5);

julia> q_test * q_id == q_test
true
source
Base.absMethod
abs(q::UnitQuaternion{T}) -> T

Computes the absolute value operator to compute the norm of a unit quaternion.

julia> using HeteroTomo3D

julia> q1 = UnitQuaternion(0.5, 0.5, 0.5, 0.5);

julia> q2 = UnitQuaternion(1.0, 0.0, 0.0, 0.0);

julia> abs(q1) ≈ 1.0
true

julia> abs(q2) ≈ 1.0
true

julia> q = q1 * q2
UnitQuaternion{Float64}(0.5, 0.5, 0.5, 0.5)

julia> abs(q) ≈ abs(q1) * abs(q2)
true
source
Base.randMethod
rand([rng::AbstractRNG], ::Type{UnitQuaternion})

Generates a random UnitQuaternion uniformly distributed over $\mathbb{S}^3$.

julia> using Random; Random.seed!(42);

julia> using HeteroTomo3D;

julia> q = rand(UnitQuaternion);

julia> abs(q) ≈ 1.0
true

julia> p = rand(Xoshiro(3), UnitQuaternion)
UnitQuaternion{Float64}(-0.3668215705606623, 0.7533620475506815, 0.39646901176823995, 0.3750998312305261)
source
HeteroTomo3D.rotateFunction
rotate(q::UnitQuaternion{T}, v::NTuple{3, T}) where {T<:Real} -> NTuple{3, T}

Rotates a 3D vector v by the unit quaternion q, i.e., computes

\[\mathbf{R}_{\mathbf{q}} \mathbf{v} = \mathbf{q} (0, \mathbf{v}) \mathbf{q}^{-1}.\]

julia> using Random; 

julia> using HeteroTomo3D;

julia> q = rand(Xoshiro(123), UnitQuaternion);

julia> v = (1.0, 0.0, 0.0);

julia> v_rot = rotate(q, v)
(-0.023923221913278114, 0.9601351868146877, -0.2785105069716637)

julia> length(v_rot) == 3
true
source
HeteroTomo3D.projection_axisFunction
projection_axis(q::UnitQuaternion{T}) -> NTuple{3, T}

Extracts the projection axis $\mathbf{r}_{\mathbf{q}} = \mathbf{R}_{\mathbf{q}}^{-1} \mathbf{e}_3 \in \mathbb{S}^2$.

julia> using Random; Random.seed!(42);

julia> using HeteroTomo3D;

julia> q = rand(UnitQuaternion);

julia> e3 = (0.0, 0.0, 1.0);

julia> r3 = rotate(inv(q), e3);

julia> all(isapprox.(projection_axis(q), r3, atol=1e-14))
true
source
HeteroTomo3D.shortest_arcFunction
shortest_arc(ux::Real, uy::Real, uz::Real) -> UnitQuaternion{T}

Computes the shortest-arc rotation $\mathbf{E}(\mathbf{u}) \in SO(3)$ mapping $\mathbf{e}_3$ to $\mathbf{u} \in \mathbb{S}^2$. Requires $\mathbf{u} \in \mathbb{S}^2 \backslash \{-\mathbf{e}_3\}$ for uniqueness.

julia> using HeteroTomo3D;

julia> using Random; Random.seed!(42);

julia> u = randn(3);

julia> u /= sqrt(sum(u .* u));

julia> q = shortest_arc(u...);

julia> e3 = rotate(q, Tuple(u));

julia> all(isapprox.(e3, (0.0, 0.0, 1.0), atol=1e-14))
true
source
HeteroTomo3D.planar_rotationFunction
planar_rotation(q::UnitQuaternion) -> NTuple{2, T}

Extracts the first column of the 2D in-plane rotation matrix, i.e., it returns a Tuple (c, s) representing $\begin{bmatrix} c & -s \\ s & c \end{bmatrix} \in SO(2)$ that rotates the detector plane around the projection axis.

julia> using HeteroTomo3D;

julia> using Random; Random.seed!(42);

julia> q = rand(UnitQuaternion);

julia> u = planar_rotation(q)
(0.07232925278690606, -0.997380809516249)

julia> u[1]^2 + u[2]^2 ≈ 1.0
true
source