diff --git a/src/GeoStatsBase.jl b/src/GeoStatsBase.jl index d8bdd0d9..13c03099 100644 --- a/src/GeoStatsBase.jl +++ b/src/GeoStatsBase.jl @@ -82,6 +82,7 @@ export DatamineAngles, GslibAngles, VulcanAngles, + MinesightAngles, # plotting hscatter, diff --git a/src/rotations.jl b/src/rotations.jl index cd534f3b..4630d221 100644 --- a/src/rotations.jl +++ b/src/rotations.jl @@ -4,17 +4,24 @@ abstract type IndustryRotation{T} <: Rotation{3,T} end +""" + rottype(R::Type{<:IndustryRotation}) + +Returns the equivalent rotation type of the industry rotation `R`. +""" +rottype(::Type{<:IndustryRotation}) = RotZYX + Rotations.params(r::IndustryRotation) = SVector(r.θ₁, r.θ₂, r.θ₃) -(::Type{R})(t::NTuple{9}) where {R<:IndustryRotation} = convert(R, RotZYX(t)) +(::Type{R})(t::NTuple{9}) where {R<:IndustryRotation} = convert(R, rottype(R)(t)) -Base.Tuple(r::IndustryRotation) = Tuple(convert(RotZYX, r)) +Base.Tuple(r::R) where {R<:IndustryRotation} = Tuple(convert(rottype(R), r)) -function Base.:*(r::IndustryRotation, v::StaticVector) +function Base.:*(r::R, v::StaticVector) where {R<:IndustryRotation} if length(v) != 3 throw("Dimension mismatch: cannot rotate a vector of length $(length(v))") end - rot = convert(RotZYX, r) + rot = convert(rottype(R), r) rot * v end @@ -105,3 +112,41 @@ function Base.convert(::Type{R}, rot::GslibAngles) where {R<:RotZYX} (; θ₁, θ₂, θ₃) = rot R(-deg2rad(θ₃), deg2rad(θ₂), deg2rad(θ₁ - 90)) end + +""" + MinesightAngles(θ₁, θ₂, θ₃) + +MineSight YZX rotation convention following the right-hand rule. +All angles are in degrees and the sign convention is CW, CCW, CW positive. + +The first rotation is a horizontal rotation around the Z-axis, with positive being clockwise. +The second rotation is a rotation around the new X-axis, which moves the Y-axis into the +desired position, with positive direction of rotation is up. +The third rotation is a rotation around the new Y-axis, which moves the X-axis into the +desired position, with positive direction of rotation is up. + +## References + +* Sanchez, J. [MINESIGHT® TUTORIALS](https://pdfcoffee.com/manual-minesight-6-pdf-free.html) +""" +struct MinesightAngles{T} <: IndustryRotation{T} + θ₁::T + θ₂::T + θ₃::T + MinesightAngles{T}(θ₁, θ₂, θ₃) where {T} = new{rot_eltype(T)}(θ₁, θ₂, θ₃) +end + +MinesightAngles(θ₁::T, θ₂::T, θ₃::T) where {T} = MinesightAngles{T}(θ₁, θ₂, θ₃) +MinesightAngles(θ₁, θ₂, θ₃) = MinesightAngles(promote(θ₁, θ₂, θ₃)...) + +rottype(::Type{<:MinesightAngles}) = RotYXZ + +function Base.convert(::Type{R}, rot::RotYXZ) where {R<:MinesightAngles} + θ₁, θ₂, θ₃ = Rotations.params(rot) + R(-rad2deg(θ₃), rad2deg(θ₂), -rad2deg(θ₁)) +end + +function Base.convert(::Type{R}, rot::MinesightAngles) where {R<:RotYXZ} + (; θ₁, θ₂, θ₃) = rot + R(-deg2rad(θ₃), deg2rad(θ₂), -deg2rad(θ₁)) +end diff --git a/test/rotations.jl b/test/rotations.jl index 164fd9c8..d2a43959 100644 --- a/test/rotations.jl +++ b/test/rotations.jl @@ -20,6 +20,12 @@ @test vulcan * v₁ ≈ [3.0810411262386967, 1.2425629737653001, 1.7214014158973259] @test vulcan * v₂ ≈ [0.7601180732526871, 0.5597366702348654, 0.09442126195411826] + # MineSight convention + minisight = MinesightAngles(30, 15, -15) + + @test minisight * v₁ ≈ [3.7410191941252595, -0.034675177060507156, 0.059774754555877996] + @test minisight * v₂ ≈ [0.8592918193713616, 0.4018496819077273, -0.011593201115905286] + # comparing conventions @test datamine ≈ gslib @test datamine ≈ vulcan @@ -41,4 +47,10 @@ rot = RotZYX(-deg2rad(θ₃), deg2rad(θ₂), deg2rad(θ₁ - 90)) gslib = GslibAngles(rot) @test Rotations.params(gslib) ≈ [θ₁, θ₂, θ₃] + + θ₁, θ₂, θ₃ = 30, 15, -15 + rot = RotYXZ(-deg2rad(θ₃), deg2rad(θ₂), -deg2rad(θ₁)) + minisight = MinesightAngles(rot) + @test minisight ≈ rot + @test Rotations.params(minisight) ≈ [θ₁, θ₂, θ₃] end