3D Rotations (Quaternions)
This section provides the rotation geometry for the tomographic pipeline using quaternions robust to Gimbal-lock.
HeteroTomo3D.UnitQuaternion — Type
UnitQuaternion{T<:Real}Represents a unit quaternion $\mathbf{q} = (\omega, x, y, z) \in \mathbb{S}^{3}$ for singularity-free 3D rotations.
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
trueBase.abs — Method
abs(q::UnitQuaternion{T}) -> TComputes 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)
trueBase.rand — Method
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)HeteroTomo3D.rotate — Function
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
trueHeteroTomo3D.projection_axis — Function
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))
trueHeteroTomo3D.shortest_arc — Function
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))
trueHeteroTomo3D.planar_rotation — Function
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