JuliaGeometry / Rotations.jl

Julia implementations for different rotation parameterizations
https://juliageometry.github.io/Rotations.jl
MIT License
176 stars 44 forks source link

Problems with using subtypes of StaticArrays.FieldVector in perpendicular_vector() #265

Closed tamasgal closed 11 months ago

tamasgal commented 11 months ago

Sometimes my code crashes because of a missing method of Rotations. perpendicular_vector while every other method in in Rotations.jl happily takes my custom StaticArrays.FieldVectors.

Here is what I mean, given some simple subtyping as recommended in the StaticArray.jl docs, we define Dir which works fine and is taken as FieldVector{3, Float64} and indices of SOneTo(3), so all fine:

julia> struct Dir <: FieldVector{3, Float64}
           x::Float64
           y::Float64
           z::Float64
       end

julia> d = Dir(1, 0, 0)
3-element Dir with indices SOneTo(3):
 1.0
 0.0
 0.0

julia> R = rand(RotMatrix{3})
3×3 RotMatrix3{Float64} with indices SOneTo(3)×SOneTo(3):
 -0.9449     0.324235  -0.0451206
 -0.254104  -0.639555   0.725535
  0.206387   0.697023   0.686705

julia> R * d
3-element Dir with indices SOneTo(3):
 -0.9448998601049303
 -0.2541038561489163
  0.2063867356782747

Now the problem: in some rotations, the perpendicular_vector is called (I have not traced down when this happens but it does not happen that much in my code, so it's something which is triggered by specific constellations of the vectors) and that method only accepts subtypes of SVector{3}:

julia> Rotations.perpendicular_vector(d)
ERROR: MethodError: no method matching perpendicular_vector(::Dir)

Closest candidates are:
  perpendicular_vector(::SVector{3})
   @ Rotations ~/.julia/packages/Rotations/vUzHx/src/util.jl:8

Stacktrace:
 [1] top-level scope
   @ REPL[17]:1

Can we widen the input type domain or is there a specific reason why SVector{3} has been chosen?

Btw. I aways assumed that FieldVector{3, Float64} is a subtype of SVector{3} but apparently it's not:

julia> FieldVector{3, Float64} <: SVector{3}
false

Edit: btw. the current workaround is wrapping the custom struct into SVector{3}. The problem is that I need to do this at the beginning of the chain (when calling the rotation functions) and then I also get SVector{3} back, which introduces type instabilities since there are other methods in my code which expect Dir.

Calling the "private" method at least works:

julia> Rotations.perpendicular_vector(SVector{3}(d))
3-element SVector{3, Float64} with indices SOneTo(3):
 -0.0
  1.0
  0.0
hyrodium commented 11 months ago

Thank you for creating the issue! Here's a MWE:

julia> using Rotations, StaticArrays

julia> u = MVector(1,0,0)
3-element MVector{3, Int64} with indices SOneTo(3):
 1
 0
 0

julia> v = MVector(-1,0,0)
3-element MVector{3, Int64} with indices SOneTo(3):
 -1
  0
  0

julia> rotation_between(u,v)
ERROR: MethodError: no method matching perpendicular_vector(::MVector{3, Int64})

Closest candidates are:
  perpendicular_vector(::SVector{3})
   @ Rotations ~/.julia/dev/Rotations/src/util.jl:8

Stacktrace:
 [1] rotation_between(u::MVector{3, Int64}, v::MVector{3, Int64})
   @ Rotations ~/.julia/dev/Rotations/src/rotation_between.jl:23
 [2] top-level scope
   @ REPL[4]:1
tamasgal commented 11 months ago

Awesome, many thanks!