Giter VIP home page Giter VIP logo

rotations.jl's Introduction

Rotations.jl

3D rotations made easy in Julia

Stable Dev Build Status Coverage Aqua QA DOI

This package implements various 3D rotation parameterizations and defines conversions between them. At their heart, each rotation parameterization is a 3×3 unitary (orthogonal) matrix (based on the StaticArrays.jl package), and acts to rotate a 3-vector about the origin through matrix-vector multiplication.

While the RotMatrix type is a dense representation of a 3×3 matrix, we also have sparse (or computed, rather) representations such as quaternions, angle-axis parameterizations, and Euler angles.

For composing rotations about the origin with other transformations, see the CoordinateTransformations.jl package.

Interface

The following operations are supported by all of the implemented rotation parameterizations.

Composition

Any two rotations of the same type can be composed with simple multiplication:

q3 = q2*q1

Rotations can be composed with the opposite (or inverse) rotation with the appropriate division operation

q1 = q2\q3
q2 = q3/q1

Rotation

Any rotation can operate on a 3D vector (represented as a SVector{3}), again through simple multiplication:

r2 = q*r

which also supports multiplication by the opposite rotation

r = q\r2

Rotation Angle / Axis

The rotation angle and axis can be obtained for any rotation parameterization using

rotation_axis(r::Rotation)
rotation_angle(r::Rotation)

Initialization

All rotation types support one(R) to construct the identity rotation for the desired parameterization. A random rotation, uniformly sampled over the space of rotations, can be sampled using rand(R). For example:

r = one(QuatRotation)  # equivalent to QuatRotation(1.0, 0.0, 0.0, 0.0)
q = rand(QuatRotation)
p = rand(MRP{Float32})

Conversion

All rotatations can be converted to another parameterization by simply calling the constructor for the desired parameterization. For example:

q = rand(QuatRotation)
aa = AngleAxis(q)
r = RotMatrix(aa)

Example Usage

using Rotations, StaticArrays

# create the identity rotation (identity matrix)
id = one(RotMatrix{3, Float64})

# create a random rotation matrix (uniformly distributed over all 3D rotations)
r = rand(RotMatrix{3}) # uses Float64 by default

# create a point
p = SVector(1.0, 2.0, 3.0) # from StaticArrays.jl, but could use any AbstractVector...

# convert to a quaternion (QuatRotation) and rotate the point
q = QuatRotation(r)
p_rotated = q * p

# Compose rotations
q2 = rand(QuatRotation)
q3 = q * q2

# Take the inverse (equivalent to transpose)
q_inv = transpose(q)
q_inv == inv(q)
p  q_inv * (q * p)
q4 = q3 / q2  # q4 = q3 * inv(q2)
q5 = q3 \ q2  # q5 = inv(q3) * q2

# convert to a Modified Rodrigues Parameter (aka Stereographic quaternion projection, recommended for applications with differentiation)
spq = MRP(r)

# convert to the Angle-axis parameterization, or related Rotation vector
aa = AngleAxis(r)
rv = RotationVec(r)
ϕ = rotation_angle(r)
v = rotation_axis(r)

# convert to Euler angles, composed of X/Y/Z axis rotations (Z applied first)
# (all combinations of "RotABC" are defined)
r_xyz = RotXYZ(r)

# Rotation about the X axis by 0.1 radians
r_x = RotX(0.1)

# Composing axis rotations together automatically results in Euler parameterization
RotX(0.1) * RotY(0.2) * RotZ(0.3) === RotXYZ(0.1, 0.2, 0.3)

# Can calculate Jacobian - derivatives of rotations with respect to parameters
j1 = Rotations.jacobian(RotMatrix, q) # How does the matrix change w.r.t the 4 Quat parameters?
j2 = Rotations.jacobian(q, p) # How does the rotated point q*p change w.r.t. the 4 Quat parameters?
# ... all Jacobian's involving RotMatrix, MRP and Quat are implemented
# (MRP is ideal for optimization purposes - no constaints/singularities)

Rotation Parameterizations

  1. Rotation Matrix RotMatrix{N, T}

    An N×N rotation matrix storing the rotation. This is a simple wrapper for a StaticArrays SMatrix{N,N,T}. A rotation matrix R should have the property I = R * R', but this isn't enforced by the constructor. On the other hand, all the types below are guaranteed to be "proper" rotations for all input parameters (equivalently: parity conserving, in SO(3), det(r) = 1, or a rotation without reflection).

  2. Arbitrary Axis Rotation AngleAxis{T}

    A 3D rotation with fields theta, axis_x, axis_y, and axis_z to store the rotation angle and axis of the rotation. Like all other types in this package, once it is constructed it acts and behaves as a 3×3 AbstractMatrix. The axis will be automatically renormalized by the constructor to be a unit vector, so that theta always represents the rotation angle in radians.

  3. Quaternions QuatRotation{T}

    A 3D rotation parameterized by a unit quaternion. Note that the constructor will renormalize the quaternion to be a unit quaternion, and that although they follow the same multiplicative algebra as quaternions, it is better to think of QuatRotation as a 3×3 matrix rather than as a quaternion number.

    Previously Quat, UnitQuaternion.

  4. Rotation Vector RotationVec{T}

    A 3D rotation encoded by an angle-axis representation as angle * axis. This type is used in packages such as OpenCV.

    Note: If you're differentiating a Rodrigues Vector check the result is what you expect at theta = 0. The first derivative of the rotation should behave, but higher-order derivatives of it (as well as parameterization conversions) should be tested. The Stereographic Quaternion Projection (MRP) is the recommended three parameter format for differentiation.

    Previously RodriguesVec.

  5. Rodrigues Parameters RodriguesParam{T} A 3-parameter representation of 3D rotations that has a singularity at 180 degrees. They can be interpreted as a projection of the unit quaternion onto the plane tangent to the quaternion identity. They are computationally efficient and do not have a sign ambiguity.

  6. Modified Rodrigues Parameter MRP{T} A 3D rotation encoded by the stereographic projection of a unit quaternion. This projection can be visualized as a pin hole camera, with the pin hole matching the quaternion [-1,0,0,0] and the image plane containing the origin and having normal direction [1,0,0,0]. The "identity rotation" Quaternion(1.0,0,0,0) then maps to the MRP(0,0,0)

    These are similar to the Rodrigues vector in that the axis direction is stored in an unnormalized form, and the rotation angle is encoded in the length of the axis. This type has the nice property that the derivatives of the rotation matrix w.r.t. the MRP parameters are rational functions, making the MRP type a good choice for differentiation / optimization.

    They are frequently used in aerospace applications since they are a 3-parameter representation whose singularity happens at 360 degrees. In practice, the singularity can be avoided with some switching logic between one of two equivalent MRPs (obtained by projecting the negated quaternion).

    Previously SPQuat.

  7. Cardinal axis rotations RotX{T}, RotY{T}, RotZ{T}

    Sparse representations of 3D rotations about the X, Y, or Z axis, respectively.

  8. Two-axis rotations RotXY{T}, etc

    Conceptually, these are compositions of two of the cardinal axis rotations above, so that RotXY(x, y) == RotX(x) * RotY(y) (note that the order of application to a vector is right-to-left, as-in matrix-matrix-vector multiplication: RotXY(x, y) * v == RotX(x) * (RotY(y) * v)).

  9. Euler Angles - Three-axis rotations RotXYZ{T}, RotXYX{T}, etc

    A composition of 3 cardinal axis rotations is typically known as a Euler angle parameterization of a 3D rotation. The rotations with 3 unique axes, such as RotXYZ, are said to follow the Tait Bryan angle ordering, while those which repeat (e.g. EulerXYX) are said to use Proper Euler angle ordering.

    Like the two-angle versions, the order of application to a vector is right-to-left, so that RotXYZ(x, y, z) * v == RotX(x) * (RotY(y) * (RotZ(z) * v)). This may be interpreted as an "extrinsic" rotation about the Z, Y, and X axes or as an "intrinsic" rotation about the X, Y, and Z axes. Similarly, RotZYX(z, y, x) may be interpreted as an "extrinsic" rotation about the X, Y, and Z axes or an "intrinsic" rotation about the Z, Y, and X axes.

The Rotation Error state and Linearization

It is often convenient to consider perturbations or errors about a particular 3D rotation, such as applications in state estimation or optimization-based control. Intuitively, we expect these errors to live in three-dimensional space. For globally non-singular parameterizations such as unit quaternions, we need a way to map between the four parameters of the quaternion to this three-dimensional plane tangent to the four-dimensional hypersphere on which quaternions live.

There are several of these maps provided by Rotations.jl:

  • ExponentialMap: A very common mapping that uses the quaternion exponential and the quaternion logarithm. The quaternion logarithm converts a 3D rotation vector (i.e. axis-angle vector) to a unit quaternion. It tends to be the most computationally expensive mapping.

  • CayleyMap: Represents the differential quaternion using Rodrigues parameters. This parameterization goes singular at 180° but does not inherit the sign ambiguities of the unit quaternion. It offers an excellent combination of cheap computation and good behavior.

  • MRPMap: Uses Modified Rodrigues Parameters (MRPs) to represent the differential unit quaternion. This mapping goes singular at 360°.

  • QuatVecMap: Uses the vector part of the unit quaternion as the differential unit quaternion. This mapping also goes singular at 180° but is the computationally cheapest map and often performs well.

Rotations.jl provides the RotationError type for representing rotation errors, that act just like a SVector{3} but carry the nonlinear map used to compute the error, which can also be used to convert the error back to a QuatRotation (and, by extention, any other 3D rotation parameterization). The following methods are useful for computing RotationErrors and adding them back to the nominal rotation:

rotation_error(R1::Rotation, R2::Rotation, error_map::ErrorMap)  # compute the error between `R1` and `R2` using `error_map`
add_error(R::Rotation, err::RotationError)  # "adds" the error to `R` by converting back a `UnitQuaterion` and composing with `R`

or their aliases

R1  R2   # caclulate the error using the default error map
R1  err  # alias for `add_error(R1, err)`

For a practical application of these ideas, see the quatenrion-multiplicative Extended Kalman Filter (MEKF). This article provides a good description.

When taking derivatives with respect to quaternions we need to account both for these mappings and the fact that local perturbations to a rotation act through composition instead of addition, as they do in vector space (e.g. q * dq vs x + dx). The following methods are useful for computing these Jacobians for QuatRotation, RodriguesParam or MRP

  • ∇rotate(q,r): Jacobian of the q*r with respect to the rotation
  • ∇composition1(q2,q1): Jacobian of q2*q1 with respect to q1
  • ∇composition2(q2,q1,b): Jacobian of q2*q1 with respect to q2
  • ∇²composition1(q2,q1): Jacobian of ∇composition1(q2,q2)'b where b is an arbitrary vector
  • ∇differential(q): Jacobian of composing the rotation with an infinitesimal rotation, with respect to the infinitesimal rotation. For unit quaternions, this is a 4x3 matrix.
  • ∇²differential(q,b): Jacobian of ∇differential(q)'b for some vector b.

Import / Export

All parameterizations can be converted to and from (mutable or immutable) 3×3 matrices, e.g.

using StaticArrays, Rotations

# export
q = QuatRotation(1.0,0,0,0)
matrix_mutable = Array(q)
matrix_immutable = SMatrix{3,3}(q)

# import
q2 = Quaternion(matrix_mutable)
q3 = Quaternion(matrix_immutable)

Notes

This package assumes active (right handed) rotations where applicable.

Why use immutables / StaticArrays?

They're faster (Julia's Array and BLAS aren't great for 3×3 matrices) and don't need preallocating or garbage collection. For example, see this benchmark case where we get a 20× speedup:

julia> cd(Pkg.dir("Rotations") * "/test")

julia> include("benchmark.jl")

julia > BenchMarkRotations.benchmark_mutable()
Rotating using mutables (Base.Matrix and Base.Vector):
  0.124035 seconds (2 allocations: 224 bytes)
Rotating using immutables (Rotations.RotMatrix and StaticArrays.SVector):
  0.006006 seconds

Acknowledgements

FugroRoames

rotations.jl's People

Contributors

andyferris avatar ararslan avatar awbsmith avatar bicycle1885 avatar bjack205 avatar c42f avatar danielmatz avatar femtocleaner[bot] avatar github-actions[bot] avatar hyrodium avatar jipolanco avatar juliatagbot avatar jw3126 avatar kevin-mattheus-moerman avatar kristofferc avatar miguelraz avatar n5n3 avatar nhz2 avatar oliverevans96 avatar pbouffard avatar rdeits avatar ryanelandt avatar sethaxen avatar simonbyrne avatar sjkelly avatar timholy avatar tkelman avatar tkoolen avatar traversaro avatar twadleigh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rotations.jl's Issues

RotationVec(angle, 0.0, 0.0) does not return a Rotation Matrix for angle > 1e15

I am on Windows, with this Julia&Rotations versions:

julia> VERSION
v"1.5.3"

julia> Pkg.status()
Status `C:\Users\STraversaro\.julia\environments\v1.5\Project.toml`
  [91a5bcdd] Plots v1.10.1
  [6038ab10] Rotations v1.0.2
  [37e2e46d] LinearAlgebra

If I try to pass to RotationVec a value higher then 1e15, it does not return anymore a Rotation matrix, see:

julia> RotationVec(1e7, 0.0, 0.0)
3×3 RotationVec{Float64} with indices SOneTo(3)×SOneTo(3)(1.0e7, 0.0, 0.0):
 1.0   0.0        0.0
 0.0  -0.90727   -0.420548
 0.0   0.420548  -0.90727

julia> RotationVec(1e19, 0.0, 0.0)
3×3 RotationVec{Float64} with indices SOneTo(3)×SOneTo(3)(1.0e19, 0.0, 0.0):
 0.312547  0.0       0.0
 0.0       0.312547  0.0
 0.0       0.0       0.312547

Interestingly, passing similar values to AngleAxis seems to be working fine:

julia> AngleAxis(1e7, 1.0, 0.0, 0.0)
3×3 AngleAxis{Float64} with indices SOneTo(3)×SOneTo(3)(1.0e7, 1.0, 0.0, 0.0):
 1.0   0.0        0.0
 0.0  -0.90727   -0.420548
 0.0   0.420548  -0.90727

julia> AngleAxis(1e19, 1.0, 0.0, 0.0)
3×3 AngleAxis{Float64} with indices SOneTo(3)×SOneTo(3)(1.0e19, 1.0, 0.0, 0.0):
 1.0   0.0        0.0
 0.0  -0.374905   0.927063
 0.0  -0.927063  -0.374905

To understand more about this, I tried to plot the determinant of the matrix for various values of angle with this script:

using Rotations
using LinearAlgebra

function myRotationVec(x,y,z)
    angle = norm([x y z]);
    if(angle > 0)
      axis = [x y z]/angle;
    else
      axis = [1.0 0.0 0.0];
    end
    return AngleAxis(angle, axis[1], axis[2], axis[3])
end

using Plots
angleRange = 10.0 .^(range(0, step=0.01,stop=150));
detRotVec = copy(angleRange);
for i=1:length(angleRange)
    detRotVec[i] = det(RotationVec(angleRange[i],0.0,0.0))
end


detMyRotVec = copy(angleRange);
for i=1:length(angleRange)
    detMyRotVec[i] = det(myRotationVec(angleRange[i],0.0,0.0))
end

plot(angleRange, detRotVec, xaxis=:log)

plot(angleRange, detMyRotVec, ylims=(0,2), xaxis=:log)

And from this I see that the determinant seems to be different from 1.0 around ~1e15 :
det_rotvector

While the naive myRotationVec that uses AngleAxis seems to always return a Rotation matrix:
det_myrotvector

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

If you'd like for me to do this for you, comment TagBot fix on this issue.
I'll open a PR within a few hours, please be patient!

Weird interactions with Unitful

I have been trying to use Rotations and Unitful together. At first, it seems like it works well:

julia> RotX(1.0u"°")
3×3 RotX{Unitful.Quantity{Float64,Unitful.Dimensions{()},Unitful.FreeUnits{(Unitful.Unit{:Degree,Unitful.Dimensions{()}}(0, 1//1),),Unitful.Dimensions{()}}}}(1.0°):
 1.0  0.0         0.0      
 0.0  0.999848   -0.0174524
 0.0  0.0174524   0.999848

But then I found that the units propagate incorrectly:

julia> RotX(1.0u"°") * [0, 0, 1]
3-element StaticArrays.SArray{Tuple{3},Unitful.Quantity{Float64,Unitful.Dimensions{()},Unitful.FreeUnits{(Unitful.Unit{:Degree,Unitful.Dimensions{()}}(0, 1//1),),Unitful.Dimensions{()}}},1,3}:
       0.0°
 -0.999949°
   57.2871°

NaNs when using ForwardDiff with RodriguesVec

For example:

julia> using ForwardDiff, Rotations

julia> ForwardDiff.derivative(0) do x
           rotation_angle(RodriguesVec(x, 0, 0))
       end
NaN

This comes out of the way the rotation angle is computed, which involves sqrt(rv.sx * rv.sx + ...), and which fails because the derivative of sqrt at 0 is inf (from above).

However, there is a correct answer for this derivative (it's 0), so I wonder if we can make the computation a bit more robust. For example, the generic LinearAlgebra.norm does the right thing:

julia> ForwardDiff.derivative(0.0) do x
           rv = RodriguesVec(x, 0, 0)
           norm([rv.sx, rv.sy, rv.sz])
       end
0.0

Skew-symmetric matrices as generators for rotations

See #29.

I think it would be great to have a skew-symmetric matrix type that can be exponentiated into a Rotation, and vice-versa via log. These would form a Lie algebra to complement the Lie group of rotations.

Similarly to the rotations, these could have simplified parameterizations that are similar to AngleAxis and RodriguesVec.

exp(Rotations.UnitQuaternion(0, π, 0, 0)) gives incorrect result

julia> exp(Rotations.UnitQuaternion(0, π, 0, 0))
3×3 UnitQuaternion{Float64} with indices SOneTo(3)×SOneTo(3)(0.540302, 0.841471, 0.0, 0.0):
 1.0   0.0        0.0     
 0.0  -0.416147  -0.909297
 0.0   0.909297  -0.416147

I would expect the result UnitQuaternion(-1.0, 0, 0, 0).

This issue might end up being superseded by a fix to #30.

Rotations.RodriguesVec

julia : Rotations.RodriguesVec and python: rvecs_matrix = cv2.Rodrigues(rvecs, None) answer is different

julia:

using Rotations, StaticArrays
rot_1 = [0.999788  0.003629  -0.020254;  23.244938  -0.003697  0.999988 ; -0.003359  -0.077094  0.020242]
rot_1 = RotMatrix{3, Float64}(rot_1)
rv = RodriguesVec(rot_1)      # -0.137432, -0.00215575, 2.96551

python:

import cv2
import numpy as np
rot_1 = np.array([[0.999788, 0.003629, -0.020254],
                      [23.244938, -0.003697, 0.999988],
                      [-0.003359, -0.077094, 0.020242]])
rv= cv2.Rodrigues(rot_1 , None)   # [[-1.16185012], [-1.06024884], [1.1998094]]

can you help me, check it out . thank you!!

Tag?

I'd like to tag a new version; is that OK?

Proposed release notes:

  • Require Julia 0.6.0-pre (#35)
  • add RotMatrix2, RotMatrix3 type aliases (#36)
  • add option to skip renormalization of AngleAxis and Quat (#37)
  • Fix performance issues introduced in #32 due to missing Tuple constructor specializations (#38, #39)

rotation_angle(::Quaternion)

There's currently an issue that
rotation_angle(q::Quaternion) = 2*acos(q.s)
doesn't check / force q.s into the [-1, 1] range. This broke another package with rounding errors putting q.s out of range, causing acos to throw. Some options:

  1. always renormalize the quaternion (but it'll cause more rounding)
  2. use max / min (but this is gammy for non-unit quaternions)
  3. leave it to the user (but this sucks)
  4. voodoo loosening of the acos range ([-1-1e9, 1+1e-9] or something?)

I don't really like any of these options. The Quaternions.jl implementation uses the renormalize option (but its a bit broken)

Thoughts? @andyferris @c42f

Handling the `eye` change

What should we do with stuff like

@inline eye(::Type{AngleAxis}) = AngleAxis(0.0, 1.0, 0.0, 0.0)
@inline eye(::Type{AngleAxis{T}}) where {T} = AngleAxis{T}(zero(T), one(T), zero(T), zero(T))

?

The annoying thing is that UniformScaling is 'too general': what should we do with AngleAxis(3.0I)?

New ambiguities on upcoming 1.6

PkgEval for upcoming 1.6 detected the following ambiguity, breaking the package test:

  Expression: isempty(setdiff(ramb, samb))
   Evaluated: isempty(Tuple{Method, Method}[(RotZ{T}(theta) where T in Rotations at /home/kc/JuliaPkgs/Rotations.jl/src/euler_types.jl:21,
(::Type{R})(t::NTuple{9, T} where T) where R<:RotZ in Rotations at /home/kc/JuliaPkgs/Rotations.jl/src/euler_types.jl:30),
(RotY{T}(theta) where T in Rotations at /home/kc/JuliaPkgs/Rotations.jl/src/euler_types.jl:21,
(::Type{R})(t::NTuple{9, T} where T) where R<:RotY in Rotations at /home/kc/JuliaPkgs/Rotations.jl/src/euler_types.jl:30),
(RotX{T}(theta) where T in Rotations at /home/kc/JuliaPkgs/Rotations.jl/src/euler_types.jl:21,
(::Type{R})(t::NTuple{9, T} where T) where R<:RotX in Rotations at /home/kc/JuliaPkgs/Rotations.jl/src/euler_types.jl:30),
(RotZ(theta) in Rotations at /home/kc/JuliaPkgs/Rotations.jl/src/euler_types.jl:25,
(::Type{R})(t::NTuple{9, T} where T) where R<:RotZ in Rotations at /home/kc/JuliaPkgs/Rotations.jl/src/euler_types.jl:30),
(RotX(theta) in Rotations at /home/kc/JuliaPkgs/Rotations.jl/src/euler_types.jl:25,
(::Type{R})(t::NTuple{9, T} where T) where R<:RotX in Rotations at /home/kc/JuliaPkgs/Rotations.jl/src/euler_types.jl:30), 
(RotY(theta) in Rotations at /home/kc/JuliaPkgs/Rotations.jl/src/euler_types.jl:25,
(::Type{R})(t::NTuple{9, T} where T) where R<:RotY in Rotations at /home/kc/JuliaPkgs/Rotations.jl/src/euler_types.jl:30)])

Also, I notice this package only tests on 1.0 and 1.3. Might make sense to also include the latest release in that.

Help understanding performance discrepancy

I'm a little confused as to what's causing the performance discrepancy between the following two pieces of code:

immutable T3DRotMatrix{T}
    rot::RotMatrix{3, T, 9}
    trans::SVector{3, T}
end

@inline *(t1::T3DRotMatrix, t2::T3DRotMatrix) = T3DRotMatrix(t1.rot * t2.rot, t1.trans + t1.rot * t2.trans)
immutable T3DSMatrix{T}
    rot::SMatrix{3, 3, T, 9}
    trans::SVector{3, T}
end

@inline *(t1::T3DSMatrix, t2::T3DSMatrix) = T3DSMatrix(t1.rot * t2.rot, t1.trans + t1.rot * t2.trans)

BenchmarkTools shows (with bounds checks turned off and with -O3) that * for T3DRotMatrix is about twice as fast on my machine as for T3DRotMatrix.

Here's the code_llvm for T3DRotMatrix:

define void @julia_Type_71814(%T3DRotMatrix* noalias sret, %jl_value_t*, %RotMatrix*, %SVector*) #0 {
top:
  %4 = alloca %T3DRotMatrix, align 8
  %5 = alloca [9 x double], align 8
  call void @julia_Type_71534([9 x double]* noalias nonnull sret %5, %jl_value_t* inttoptr (i64 4445652016 to %jl_value_t*), %RotMatrix* %2) #0
  %6 = bitcast [9 x double]* %5 to i8*
  %7 = bitcast %T3DRotMatrix* %4 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %7, i8* %6, i64 72, i32 8, i1 false)
  %8 = bitcast %T3DRotMatrix* %4 to i8*
  %9 = bitcast %SVector* %3 to i8*
  %10 = getelementptr inbounds %T3DRotMatrix, %T3DRotMatrix* %4, i64 0, i32 1
  %11 = bitcast %SVector* %10 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %11, i8* %9, i64 24, i32 1, i1 false)
  %12 = bitcast %T3DRotMatrix* %0 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %12, i8* %8, i64 96, i32 8, i1 false)
  ret void
}

and for T3DSMatrix:

define void @julia_Type_71869(%T3DSMatrix* noalias sret, %jl_value_t*, %SMatrix*, %SVector*) #0 {
top:
  %4 = alloca %T3DSMatrix, align 8
  %5 = bitcast %SMatrix* %2 to i8*
  %6 = bitcast %T3DSMatrix* %4 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %6, i8* %5, i64 72, i32 1, i1 false)
  %7 = bitcast %T3DSMatrix* %4 to i8*
  %8 = bitcast %SVector* %3 to i8*
  %9 = getelementptr inbounds %T3DSMatrix, %T3DSMatrix* %4, i64 0, i32 1
  %10 = bitcast %SVector* %9 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %10, i8* %8, i64 24, i32 1, i1 false)
  %11 = bitcast %T3DSMatrix* %0 to i8*
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %11, i8* %7, i64 96, i32 8, i1 false)
  ret void
}

so the difference is

  %5 = alloca [9 x double], align 8
  call void @julia_Type_71534([9 x double]* noalias nonnull sret %5, %jl_value_t* inttoptr (i64 4445652016 to %jl_value_t*), %RotMatrix* %2) #0

This is despite the fact that the code_llvm for RotMatrix * RotMatrix is identical to that for SMatrix * SMatrix, and likewise for RotMatrix * SVector and SMatrix * SVector.

Could you help me understand what's going on? Could it be related to this TODO?

Support automatic differentiation with Zygote

Supporting reverse-mode autodiff with Zygote requires two things:

  • custom pullbacks (reverse-mode differentiation rules, AKA adjoints) for functions that internally mutate arrays (Zygote does not support array mutation)
  • custom pullbacks for constructors

I think the latter is the main thing missing for Zygote support. Supporting it would require adding custom rrules for constructors using ChainRulesCore, which has essentially no dependencies.

Here's an example that fails:

julia> using Rotations, Zygote

julia> foo(ω, v) = (RotationVec...) * v)[1];

julia> ω, v = randn(3), randn(3)
([-0.5124874613220701, 0.27274002772526423, 1.0593705312514463], [-0.2329525748114141, -1.1183670007323072, -0.4878065893106537])

julia> foo(ω, v)
0.8904209135865043

julia> Zygote.gradient(foo, ω, v) # expected from finite differences: ([-0.18142454901446126, -0.16476417425397563, 0.8182962760715483], [0.47098619925782836, -0.8816660765248943, -0.028929735807022527])
ERROR: Need an adjoint for constructor RotationVec{Float64}. Gradient is of type Array{Float64,2}

In this case mathematically the missing pullback is for the exponential map exp: so(3) → SO(3).

Promotion behavior under Rot multiplication

Multiplying by Rot... instances has unexpected promotion behavior:

julia> using Unitful: °, rad

julia> using Rotations

# Works because the RotZYX instance has integer elements
julia> RotZYX(0, 0, 0) * [1, 0, 0]
3-element SArray{Tuple{3},Int64,1,3} with indices SOneTo(3):
 1
 0
 0

# Doesn't work because the RotZYX instance has non-integer elements
julia> RotZYX(5, 0, 0) * [1, 0, 0]
ERROR: InexactError: Int64(0.28366218546322625)
...

# Works now
julia> RotZYX(5, 0, 0) * Float64[1, 0, 0]
3-element SArray{Tuple{3},Float64,1,3} with indices SOneTo(3):
  0.28366218546322625
 -0.9589242746631385
  0.0

Things get a little weirder when specifying rotations in degrees using Unitful quantities:

# Doesn't work because multiplying by integer vector
julia> RotZYX(90°, 0°, 0°) * [1, 0, 0]
ERROR: InexactError: Int64(57.29577951308232)
...

# This works, though, as it converts degrees to non-Unitful, non-integer radians
julia> RotZYX(90°, 0, 0) * [1, 0, 0]
3-element SArray{Tuple{3},Float64,1,3} with indices SOneTo(3):
 6.123233995736766e-17
 1.0
 0.0

# This one "works" but produces an incorrect output
julia> RotZYX(90°,0°,0°) * Float64[1, 0, 0]
3-element SArray{Tuple{3},Quantity{Float64,NoDims,Unitful.FreeUnits{(°,),NoDims,nothing}},1,3} with indices SOneTo(3):
               0.0°
 57.29577951308232°
               0.0°

# Converting to radians and de-unitizing the output gives the desired result
julia> rad.(RotZYX(90°,0°,0°) * Float64[1,0,0]) ./ rad
3-element SArray{Tuple{3},Float64,1,3} with indices SOneTo(3):
 0.0
 1.0
 0.0

On Julia v0.6 the size of rotations are not known (due to StaticArrays)

I still get a size error, although I checked out the latest Rotations.jl

julia> Pkg.status()
- Rotations                     0.3.5              master

The error is the following:

ERROR: The size of type `Rotations.RotXYZ{Float64}` is not known.

If you were trying to construct (or `convert` to) a `StaticArray` you
may need to add the size explicitly as a type parameter so its size is
inferrable to the Julia compiler (or performance would be terrible). For
example, you might try

    m = zeros(3,3)
    SMatrix(m)      # this error
    SMatrix{3,3}(m) # correct - size is inferrable

Stacktrace:
 [1] StaticArrays.Size(::Type{Rotations.RotXYZ{Float64}}) at /home/zorn/.julia/v0.6/StaticArrays/src/traits.jl:42
 [2] size(::Type{Rotations.RotXYZ{Float64}}) at /home/zorn/.julia/v0.6/StaticArrays/src/abstractarray.jl:7
 [3] *(...) at /home/zorn/.julia/v0.6/StaticArrays/src/matrix_multiply.jl:199

Warnings on Julia v0.6

WARNING: deprecated syntax "abstract Rotation{N,T}<:StaticMatrix{T}" at /home/juliohm/.julia/v0.6/Rotations/src/core_types.jl:8.
Use "abstract type Rotation{N,T}<:StaticMatrix{T} end" instead.

WARNING: deprecated syntax "inner constructor Quat(...) around /home/juliohm/.julia/v0.6/Rotations/src/quaternion_types.jl:21".
Use "Quat{T}(...) where T" instead.

WARNING: deprecated syntax "inner constructor AngleAxis(...) around /home/juliohm/.julia/v0.6/Rotations/src/angleaxis_types.jl:22".
Use "AngleAxis{T}(...) where T" instead.

New tag?

Hey guys, would you mind tagging a new version that includes #17? Thanks in advance!

Identity `RodriguesVec` can't multipy a `SVector{3}`.

The following:

using StaticArrays, Rotations
const V3 = SVector{3, Float64}
rot = RodriguesVec(zero(V3)...)
x1 = rot * rand(3)  # OK
x2 = rot * rand(V3)  # fail

gives:

ERROR: MethodError: no method matching promote_type(::Type{Float64}, ::Int64)
Closest candidates are:
  promote_type(::Any, ::Any, ::Any, ::Any...) at promotion.jl:207
  promote_type(::Type{T}, ::Type{Union{}}) where T at promotion.jl:211
  promote_type(::Type{T}, ::Type{T}) where T at promotion.jl:210
  ...
Stacktrace:
 [1] *(::RodriguesVec{Float64}, ::SArray{Tuple{3},Float64,1,3}) at /home/tracy/.julia/packages/Rotations/VKCTi/src/angleaxis_types.jl:191
 [2] top-level scope at none:0

Testing for proper rotation.

Should isrotation test if the rotation is proper? There is an assumption that rotations can be converted between many reference frames, some of which cannot handle reflection.

The change is simple: test if the determinate is positive.

ANN: Move of Rotations.jl to a community-maintained location

Hi all, I am planning to move Rotations.jl to @JuliaGeometry because it has been community maintained for some time now and having it in a community-owned location will make some project admin tasks — such as adding new maintainers and github bots — easier.

To mention some people who have maintained or contributed here in the past and may be interested in the move: @tkoolen @andyferris @awbsmith @ryanelandt @jw3126 @rdeits @timholy. I hope this makes sense to everyone? I'm happy to add current maintainers in the new location, just let me know if you'd like that or have other concerns about the move.

I also plan to do the same for CoordinateTransformations.jl which has much the same group of contributors. (And PlyIO.jl, but I won't make a separate announcement about that because it's fairly obscure.)

I've already discussed the move with current @FugroRoames people @draffelt and @bertrand-caron and I'll be adding a small acknowledgement section to the READMEs - see #109)

isrotation and non Float eltype

julia> using Rotations, LinearAlgebra

julia> isrotation(Matrix{Float64}(I,3,3)) # works
true

julia> isrotation(Matrix(I,3,3)) #throws
ERROR: MethodError: no method matching eps(::Type{Bool})

isrotation appears broken for integer types again

isrotation([1 0; 0 1])
ERROR: MethodError: no method matching eps(::Type{Int64})
Closest candidates are:
  eps(::Dates.Time) at /usr/local/julia/julia-1/usr/share/julia/stdlib/v1.2/Dates/src/types.jl:362
  eps(::Dates.Date) at /usr/local/julia/julia-1/usr/share/julia/stdlib/v1.2/Dates/src/types.jl:361
  eps(::Dates.DateTime) at /usr/local/julia/julia-1/usr/share/julia/stdlib/v1.2/Dates/src/types.jl:360
  ...
Stacktrace:
 [1] isrotation(::Array{Int64,2}) at /home/chantal/.julia/packages/Rotations/RtNzW/src/core_types.jl:194
 [2] top-level scope at none:0

julia> isrotation([1 5; 6 8])
ERROR: MethodError: no method matching eps(::Type{Int64})
Closest candidates are:
  eps(::Dates.Time) at /usr/local/julia/julia-1/usr/share/julia/stdlib/v1.2/Dates/src/types.jl:362
  eps(::Dates.Date) at /usr/local/julia/julia-1/usr/share/julia/stdlib/v1.2/Dates/src/types.jl:361
  eps(::Dates.DateTime) at /usr/local/julia/julia-1/usr/share/julia/stdlib/v1.2/Dates/src/types.jl:360
  ...
Stacktrace:
 [1] isrotation(::Array{Int64,2}) at /home/chantal/.julia/packages/Rotations/RtNzW/src/core_types.jl:194
 [2] top-level scope at none:0
``` #95 

A "lazy" 2D rotation type

A 2D rotation parameterized by an angle would be welcome. This would go in the "angle-axis" family.

Internally the new type could store the angle, or it could optionally also store the cos and sin of the angle for efficiency in application to multiple points. Would only save one value compared to RotMatrix{2} and be marginally faster, but it would be guaranteed to be a parity-preserving rotation.

error when calling varinfo()

I cannot list the memory footprint from my current workspace when declaring some rotations matrices from Rotations:

julia> using Rotations

julia> # % rotmZYX = eul2rotm(eul);
       
       angX          = -pi/4 # Angle of rotation of contact surface
-0.7853981633974483

julia> rotmat_node = RotX(angX)
3×3 RotX{Float64} with indices SOneTo(3)×SOneTo(3)(-0.785398):
 1.0   0.0       0.0
 0.0   0.707107  0.707107
 0.0  -0.707107  0.707107

julia> varinfo()
ERROR: UndefVarError: indices not defined
Stacktrace:
 [1] summary(::RotX{Float64}) at /home/carlo/.julia/packages/Rotations/Hd1VA/src/core_types.jl:240
 [2] #45 at /home/carlo/Software/julia/julia/usr/share/julia/stdlib/v1.4/InteractiveUtils/src/InteractiveUtils.jl:32 [inlined]
 [3] (::InteractiveUtils.var"#45#47"{Module})(::Symbol) at ./none:0
 [4] iterate at ./generator.jl:47 [inlined]
 [5] _collect(::Type{Any}, ::Base.Generator{Base.Iterators.Filter{InteractiveUtils.var"#46#48"{Module,Regex},Array{Symbol,1}},InteractiveUtils.var"#45#47"{Module}}, ::Base.SizeUnknown) at ./array.jl:569
 [6] collect at ./array.jl:562 [inlined]
 [7] varinfo(::Module, ::Regex) at /home/carlo/Software/julia/julia/usr/share/julia/stdlib/v1.4/InteractiveUtils/src/InteractiveUtils.jl:30 (repeats 2 times)
 [8] top-level scope at REPL[4]:1


RodriguesVec -> RotMatrix problems

The conversion from RodriguresVec to RotMatrix seems a bit off when wrapping past . Demo - the following are the same:

julia> RotMatrix(RodriguesVec(2π+0.1,0,0))
FixedSizeArrays.Mat{3,3,Float64}(
    1.0 0.0 0.0
    0.0 0.9950041652780258 0.09983341664682784
    0.0 -0.09983341664682784 0.9950041652780258
)

julia> RotMatrix(RodriguesVec(2π-0.1,0,0))
FixedSizeArrays.Mat{3,3,Float64}(
    1.0 0.0 0.0
    0.0 0.9950041652780258 0.09983341664682782
    0.0 -0.09983341664682782 0.9950041652780258
)

Version 1.0

What do we need to work toward version 1.0?

We could do this right away (this package has been quite stable for a long time), though it might be an opportunity to look at integrating some of the great stuff from DifferentialRotations.jl

@bjack205 what are your thoughts? Do you think you'd have time for such a project?

XRef: RoboticExplorationLab/DifferentialRotations.jl#1 (comment)

Define exp(UnitQuaternion{T}(0, 0, 0, 0)) = one(UnitQuaternion{T})

As currently written, exp(UnitQuaternion{Float64}(0, 0, 0, 0)) evaluates to UnitQuaternion(NaN, NaN, NaN, NaN). Upon initial thought, I think it's mathematically "correct" to special-case this behavior (i.e. when the argument is exactly zero) and define the result to be the identity quaternion. We're applying the exponential map to zero, viewed as an element of the Lie algebra of unit quaternions, and the result should be the identity element of the Lie group. WDYT?

Implement `one` for `R<:Rotation` to respect the algebra of SO(n)

As described at JuliaArrays/StaticArrays.jl#763, Rotations relies on a particular buggy definition in StaticArrays for zero and one where the constructor of the static matrix type is called.

We should implement one(::Type{R<:Rotation}) here so we don't rely on that bug and so we can return something which isa R.

For zero we currently we have the following oddity (should not return a RotMatrix!). This is a bug which should be fixed in StaticArrays:

julia> zero(RotMatrix{2})
2×2 RotMatrix{2,Float64,4} with indices SOneTo(2)×SOneTo(2):
 0.0  0.0
 0.0  0.0

RotMatrix brocken with latest StaticArray

With StaticArrays.jl v0.6.5 this error occurs:

   _       _ _(_)_     |  A fresh approach to technical computing
  (_)     | (_) (_)    |  Documentation: https://docs.julialang.org
   _ _   _| |_  __ _   |  Type "?help" for help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 0.6.1 (2017-10-24 22:15 UTC)
 _/ |\__'_|_|_|\__'_|  |  
|__/                   |  x86_64-pc-linux-gnu

julia> using Rotations                                                                                                                                                   
                                                                                                                                                                        
julia> RotMatrix(-4.5)                                                                                                                                                   
ERROR: MethodError: Rotations.RotMatrix{2,Float64,4}(::StaticArrays.SArray{Tuple{2,2},Float64,2,4}) is ambiguous. Candidates:                                            
  (::Type{Rotations.RotMatrix{N,T,L}})(x::AbstractArray) where {N, T, L} in Rotations at /home/mauro/.julia/v0.6/Rotations/src/core_types.jl:78
  (::Type{SA})(a::StaticArrays.StaticArray) where SA<:StaticArrays.StaticArray in StaticArrays at /home/mauro/.julia/v0.6/StaticArrays/src/convert.jl:4
Possible fix, define
  (::Type{Rotations.RotMatrix{N,T,L}})(::StaticArrays.StaticArray)                                                                                                       
Stacktrace:                                                                                                                                                              
 [1] Rotations.RotMatrix(::Float64) at /home/mauro/.julia/v0.6/Rotations/src/core_types.jl:98                                                                            

Caused by commit JuliaArrays/StaticArrays.jl@025a991

UnitQuaternions and ForwardDiff

When differentiating a function that takes a quaternion as an input, I get several different solutions for different setups.
I know it's not really correct to treat a quaternion as a regular 4D vector when differentiating, but I'm a bit confused about the dimensions and entries of some of the results.

Code:

using LinearAlgebra
using ForwardDiff
using Rotations

function testfoo1(q)
    q = UnitQuaternion(q) 
    Rotations.params(q)
end

function testfoo2(q)
    q
end

q1 = rand(4)
q1 = q1/norm(q1)
q2 = UnitQuaternion(q1)

ForwardDiff.jacobian(testfoo1,q1) # 4x4 "random" matrix
ForwardDiff.jacobian(testfoo1,q2) # 4x9 "random" matrix
ForwardDiff.jacobian(testfoo2,q1) # 4x4 identity matrix
ForwardDiff.jacobian(testfoo2,q2) # 9x9 identity matrix

What I'd expect (but that's definitely debatable):

ForwardDiff.jacobian(testfoo1,q1) # 4x4 identity matrix
ForwardDiff.jacobian(testfoo1,q2) # 4x4 identity matrix or 4x3 "proper" quaternion derivative
ForwardDiff.jacobian(testfoo2,q1) # 4x4 identity matrix
ForwardDiff.jacobian(testfoo2,q2) # 4x4 identity matrix or 4x3 "proper" quaternion derivative

Especially the first one ( ForwardDiff.jacobian(testfoo1,q1) ) where I pass in a regular vector and return a regular vector seems off to me. And dimension 9 for some of the matrices also seems off; does that come from writing the quaternions as rotation matrices?

Rationale for `copysign` in normalized Quat constructor?

@tkoolen and I noticed that the copysign in the normalized Quat constructor was causing issues for integration (since it makes the individual components of the quaternion non-differentiable). Removing the copysign entirely fixed my issue, but I'm wondering if there's some other reason for why it should be there? If not, then removing it would make my life easier.

Cannot take the mean of single or two axis euler rotations

Note that mean() is fully broken on master. To reproduce this you'll have to checkout the fix/mean branch on my fork (will be merged after I write a test) and do the following.

julia> r=[RotZ(pi/2) for _ in 1:5000];

julia> mean(r)
ERROR: MethodError: RotZ{Float64}(::Quat{Float64}) is ambiguous. Candidates:
  (::Type{RotZ{T}})(theta) where T in Rotations at /home/colinxs/workspace/Rotations.jl/src/euler_types.jl:17
  (::Type{SA})(a::StaticArrays.StaticArray) where SA<:StaticArrays.StaticArray in StaticArrays at /home/colinxs/.julia/packages/StaticArrays/mcf7t/src/convert.jl:5
Possible fix, define
  (::Type{RotZ{T}})(::StaticArrays.StaticArray)
Stacktrace:
 [1] mean(::Array{RotZ{Float64},1}) at /home/colinxs/workspace/Rotations.jl/src/mean.jl:50
 [2] top-level scope at none:0

julia> r2=[RotXY(0,pi/2) for _ in 1:5000];

julia> mean(r2)
ERROR: Cannot construct a two-axis rotation from a matrix
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] Type at /home/colinxs/workspace/Rotations.jl/src/euler_types.jl:232 [inlined]
 [3] Type at /home/colinxs/.julia/packages/StaticArrays/mcf7t/src/convert.jl:5 [inlined]
 [4] mean(::Array{RotXY{Float64},1}) at /home/colinxs/workspace/Rotations.jl/src/mean.jl:50
 [5] top-level scope at none:0

julia> r3=[RotXYZ(0,0,pi/2) for _ in 1:5000];

julia> mean(r3)
3×3 RotXYZ{Float64}(3.14159, 0.0, 3.14159):
 -1.0          -1.22465e-16   0.0        
 -1.22465e-16   1.0          -1.22465e-16
  1.49976e-32  -1.22465e-16  -1.0        

Can't display RotX(pi)

If I create a rotation:

julia> R = RotX(pi);

And then I display it:

display(R)

I get an error:

ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type Irrational{:π}
This may have arisen from a call to the constructor Irrational{:π}(...),
since type constructors fall back to convert methods.
 in getindex at /Users/dmatz/.julia/v0.5/Rotations/src/euler_types.jl:157 [inlined]
 in macro expansion at /Users/dmatz/.julia/v0.5/StaticArrays/src/indexing.jl:142 [inlined]
 in getindex at /Users/dmatz/.julia/v0.5/StaticArrays/src/indexing.jl:136 [inlined]
 in alignment(::IOContext{Base.Terminals.TTYTerminal}, ::Rotations.RotZ{Irrational{:π}}, ::Array{Int64,1}, ::Array{Int64,1}, ::Int64, ::Int64, ::Int64) at ./show.jl:1278
 in print_matrix(::IOContext{Base.Terminals.TTYTerminal}, ::Rotations.RotZ{Irrational{:π}}, ::String, ::String, ::String, ::String, ::String, ::String, ::Int64, ::Int64) at ./show.jl:1407
 in #showarray#1(::Bool, ::Function, ::IOContext{Base.Terminals.TTYTerminal}, ::Rotations.RotZ{Irrational{:π}}, ::Bool) at /Users/dmatz/.julia/v0.5/Rotations/src/core_types.jl:149
 in display(::Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}, ::MIME{Symbol("text/plain")}, ::Rotations.RotZ{Irrational{:π}}) at ./REPL.jl:132
 in display(::Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}, ::Rotations.RotZ{Irrational{:π}}) at ./REPL.jl:135
 in display(::Rotations.RotZ{Irrational{:π}}) at ./multimedia.jl:143

If you look at the getindex call in euler_types.jl, line 157, it calls zero(T). So this seems to stem from the fact that the following produces an error:

zero(typeof(pi))

Branch and tag for 0.5

@awbsmith, @c42f

I've been slowly updating a bunch of other packages to work with StaticArrays and the related branch of Rotations. I'm thinking of tagging a (Julia 0.5+ only) release of Rotations, say version 0.3.0, that will go on that branch. We can continue tagging 0.2.x releases for Julia 0.4 using the master branch and when we are all using Julia 0.5, we can merge the static_arrays branch with master.

Is everyone OK with this?

Singularity for Rodrigues Parameters at 180 degree

README.md says that the Rodrigues Parameters would have a singularity at 180 degree. What does that mean? To my understanding, the Rodrigues Parameters are a 3-vector p that parametrize the rotation R = exp([p]_x) where [.]_x is the skew symmetric cross-product matrix and exp is the matrix exponential function. This function is continous differentiable. To me, a singularity would mean that the differential of p -> exp([p]_x) would not have full rank at some point p. So I tried the point p = (pi, 0, 0) and tried to visually picture the exponential map and their derivative and it looks to me that it is of full rank (i.e. rank 3). Obviously, (pi+dx,0,0) turns further around x but (pi,dy,0) and (pi,0,dz) don't, and for symmetry reason the directions where (pi,dy,0) and (pi,0,dz) turn around cannot fall together (except if they would both only turn around x). So, am I wrong or do you mean something different by "singularity"?

Use sincos on 0.7

Just a reminder to switch to using sincos instead of separate sin and cos where appropriate support is dropped for 0.6.

Error when using Int instead of Float as a rotation angle

using Rotations

# This works
RotX(1.0)*rand(3,5)

# This does not
RotX(1)*rand(3,5)

I get this error

ERROR: InexactError: Int64(0.5403023058681398)
Stacktrace:
 [1] Int64 at ./float.jl:709 [inlined]
 [2] convert at ./number.jl:7 [inlined]
 [3] setindex! at ./array.jl:784 [inlined]
 [4] copy_transpose!(::Array{Int64,2}, ::UnitRange{Int64}, ::UnitRange{Int64}, ::RotX{Int64}, ::UnitRange{Int64}, ::UnitRange{Int64}) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/LinearAlgebra/src/transpose.jl:197
 [5] copy_transpose! at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/LinearAlgebra/src/matmul.jl:589 [inlined]
 [6] _generic_matmatmul!(::Array{Float64,2}, ::Char, ::Char, ::RotX{Int64}, ::Array{Float64,2}, ::LinearAlgebra.MulAddMul{true,true,Bool,Bool}) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/LinearAlgebra/src/matmul.jl:730
 [7] generic_matmatmul!(::Array{Float64,2}, ::Char, ::Char, ::RotX{Int64}, ::Array{Float64,2}, ::LinearAlgebra.MulAddMul{true,true,Bool,Bool}) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/LinearAlgebra/src/matmul.jl:694
 [8] mul! at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/LinearAlgebra/src/matmul.jl:230 [inlined]
 [9] mul! at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/LinearAlgebra/src/matmul.jl:203 [inlined]
 [10] *(::RotX{Int64}, ::Array{Float64,2}) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/LinearAlgebra/src/matmul.jl:153
 [11] top-level scope at REPL[2]:1
julia> versioninfo()
Julia Version 1.3.1
Commit 2d5741174c (2019-12-30 21:36 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: AMD Ryzen Threadripper 1950X 16-Core Processor
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, znver1)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.