diff --git a/README.md b/README.md index c328ce86..cdabebe0 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ q2 = rand(Quat) q3 = q * q2 # Take the inverse (equivalent to transpose) -q_inv = q' +q_inv = transpose(q) q_inv == inv(q) p ≈ q_inv * (q * p) q4 = q3 / q2 # q4 = q3 * inv(q2) @@ -102,7 +102,7 @@ j2 = Rotations.jacobian(q, p) # How does the rotated point q*p change w.r.t. the 4. **Rodrigues Vector** `RodriguesVec{T}` - A 3D rotation encoded by an angle-axis representation as `angle * axis`. + A 3D rotation encoded by an angle-axis representation as `angle * axis`. This type is used in packages such as [OpenCV](http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#void%20Rodrigues%28InputArray%20src,%20OutputArray%20dst,%20OutputArray%20jacobian%29). Note: If you're differentiating a Rodrigues Vector check the result is what diff --git a/REQUIRE b/REQUIRE index 615d67b6..d061b3d8 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,2 +1,3 @@ julia 0.6 StaticArrays 0.6.5 +Compat diff --git a/perf/runbenchmarks.jl b/perf/runbenchmarks.jl index be15d370..789936ab 100644 --- a/perf/runbenchmarks.jl +++ b/perf/runbenchmarks.jl @@ -1,6 +1,7 @@ using Rotations using BenchmarkTools import Base.Iterators: product +import Compat.Random: srand const T = Float64 @@ -11,16 +12,23 @@ suite["conversions"] = BenchmarkGroup() rotationtypes = (RotMatrix3{T}, Quat{T}, SPQuat{T}, AngleAxis{T}, RodriguesVec{T}) for (from, to) in product(rotationtypes, rotationtypes) if from != to - name = "$(string(from)) -> $(string(to))" + name = if VERSION < v"0.7.0-" + "$(string(from)) -> $(string(to))" + else + "Rotations.$(string(from)) -> Rotations.$(string(to))" + end # use eval here because of https://github.com/JuliaCI/BenchmarkTools.jl/issues/50#issuecomment-318673288 suite["conversions"][name] = eval(:(@benchmarkable convert($to, rot) setup = rot = rand($from))) end end suite["composition"] = BenchmarkGroup() -suite["composition"]["RotMatrix{3} * RotMatrix{3}"] = @benchmarkable r1 * r2 setup = (r1 = rand(RotMatrix3{T}); r2 = rand(RotMatrix3{T})) +suite["composition"]["RotMatrix{3} * RotMatrix{3}"] = @benchmarkable( + r1 * r2, + setup = (r1 = rand(RotMatrix3{T}); r2 = rand(RotMatrix3{T})) +) -paramspath = joinpath(dirname(@__FILE__), "benchmarkparams.json") +paramspath = joinpath(@__DIR__, "benchmarkparams.json") if isfile(paramspath) loadparams!(suite, BenchmarkTools.load(paramspath)[1], :evals, :samples); else diff --git a/src/Rotations.jl b/src/Rotations.jl index 0e33a87b..1ecdd716 100644 --- a/src/Rotations.jl +++ b/src/Rotations.jl @@ -3,9 +3,12 @@ __precompile__(true) module Rotations +using Compat +using Compat.LinearAlgebra using StaticArrays -import Base: convert, eltype, size, length, getindex, inv, *, Tuple, eye +import Base: convert, eltype, size, length, getindex, *, Tuple +import Compat.LinearAlgebra: inv, eye include("util.jl") include("core_types.jl") diff --git a/src/core_types.jl b/src/core_types.jl index 963467ba..599541d3 100644 --- a/src/core_types.jl +++ b/src/core_types.jl @@ -9,8 +9,8 @@ abstract type Rotation{N,T} <: StaticMatrix{N,N,T} end Base.@pure StaticArrays.Size(::Type{Rotation{N}}) where {N} = Size(N,N) Base.@pure StaticArrays.Size(::Type{Rotation{N,T}}) where {N,T} = Size(N,N) Base.@pure StaticArrays.Size(::Type{R}) where {R<:Rotation} = Size(supertype(R)) -Base.ctranspose(r::Rotation) = inv(r) -Base.transpose(r::Rotation{N,T}) where {N,T<:Real} = inv(r) +Compat.adjoint(r::Rotation) = inv(r) +Compat.transpose(r::Rotation{N,T}) where {N,T<:Real} = inv(r) # Rotation angles and axes can be obtained by converting to the AngleAxis type rotation_angle(r::Rotation) = rotation_angle(AngleAxis(r)) @@ -102,11 +102,11 @@ Base.@propagate_inbounds Base.getindex(r::RotMatrix, i::Int) = r.mat[i] @inline (::Type{RotMatrix{2,T}})(θ::Real) where {T} = RotMatrix(@SMatrix T[cos(θ) -sin(θ); sin(θ) cos(θ)]) # A rotation is more-or-less defined as being an orthogonal (or unitary) matrix -Base.inv(r::RotMatrix) = RotMatrix(r.mat') +inv(r::RotMatrix) = RotMatrix(r.mat') # A useful constructor for identity rotation (eye is already provided by StaticArrays, but needs an eltype) -@inline Base.eye(::Type{RotMatrix{N}}) where {N} = RotMatrix((eye(SMatrix{N,N,Float64}))) -@inline Base.eye(::Type{RotMatrix{N,T}}) where {N,T} = RotMatrix((eye(SMatrix{N,N,T}))) +@inline eye(::Type{RotMatrix{N}}) where {N} = RotMatrix((eye(SMatrix{N,N,Float64}))) +@inline eye(::Type{RotMatrix{N,T}}) where {N,T} = RotMatrix((eye(SMatrix{N,N,T}))) # By default, composition of rotations will go through RotMatrix, unless overridden @inline *(r1::Rotation, r2::Rotation) = RotMatrix(r1) * RotMatrix(r2) @@ -150,32 +150,18 @@ function isrotation(r::AbstractMatrix{T}, tol::Real = 1000 * eps(eltype(T))) whe return d < tol end - -# A simplification and specialization of the Base.showarray() function makes -# everything sensible at the REPL. -function Base.showarray(io::IO, X::Rotation, repr::Bool = true; header = true) - if !haskey(io, :compact) - io = IOContext(io, compact=true) - end - if repr - if isa(X, RotMatrix) - Base.print_matrix_repr(io, X) - else - print(io, typeof(X).name.name) - n_fields = length(fieldnames(typeof(X))) - print(io, "(") - for i = 1:n_fields - print(io, getfield(X, i)) - if i < n_fields - print(io, ", ") - end - end - print(io, ")") +@static if VERSION < v"0.7-" + # A simplification and specialization of the Base.showarray() function makes + # everything sensible at the REPL. + function Base.showarray(io::IO, X::Rotation, repr::Bool = true; header = true) + if !haskey(io, :compact) + io = IOContext(io, compact=true) end - else - if header - print(io, summary(X)) - if !isa(X, RotMatrix) + if repr + if isa(X, RotMatrix) + Base.print_matrix_repr(io, X) + else + print(io, typeof(X).name.name) n_fields = length(fieldnames(typeof(X))) print(io, "(") for i = 1:n_fields @@ -186,14 +172,55 @@ function Base.showarray(io::IO, X::Rotation, repr::Bool = true; header = true) end print(io, ")") end - println(io, ":") + else + if header + print(io, summary(X)) + if !isa(X, RotMatrix) + n_fields = length(fieldnames(typeof(X))) + print(io, "(") + for i = 1:n_fields + print(io, getfield(X, i)) + if i < n_fields + print(io, ", ") + end + end + print(io, ")") + end + println(io, ":") + end + punct = (" ", " ", "") + Base.print_matrix(io, X, punct...) + end + end +else + # A simplification and specialization of the Base.show function for AbstractArray makes + # everything sensible at the REPL. + function Base.show(io::IO, ::MIME"text/plain", X::Rotation) + if !haskey(io, :compact) + io = IOContext(io, :compact => true) + end + summary(io, X) + if !isa(X, RotMatrix) + n_fields = length(fieldnames(typeof(X))) + print(io, "(") + for i = 1:n_fields + print(io, getfield(X, i)) + if i < n_fields + print(io, ", ") + end + end + print(io, ")") end - punct = (" ", " ", "") - Base.print_matrix(io, X, punct...) + print(io, ":") + println(io) + io = IOContext(io, :typeinfo => eltype(X)) + Base.print_array(io, X) end end # Removes module name from output, to match other types function Base.summary(r::Rotation{N,T}) where {T,N} - "$N×$N $(typeof(r).name.name){$(eltype(r))}" + inds = indices(r) + typestring = last(split(string(typeof(r)), '.'; limit = 2)) + string(Base.dims2string(length.(inds)), " ", typestring) end diff --git a/src/derivatives.jl b/src/derivatives.jl index c5d91108..25da3f97 100644 --- a/src/derivatives.jl +++ b/src/derivatives.jl @@ -66,7 +66,7 @@ function jacobian(::Type{RotMatrix}, q::Quat) # = (dR(s*q)/dQ*s - s*R(q) * ds/dQ) / s^2 # = (dR(s*q)/dQ - R(q) * ds/dQ) / s - jac = dsRdQ - R * dsdQ.' + jac = dsRdQ - R * transpose(dsdQ) # now reformat for output. TODO: is the the best expression? # return Vec{4,Mat{3,3,T}}(ith_partial(jac, 1), ith_partial(jac, 2), ith_partial(jac, 3), ith_partial(jac, 4)) @@ -198,7 +198,7 @@ function jacobian(q::Quat, X::AbstractVector) # and finalize with the quotient rule Xo = q * X # N.B. g(x) = s * Xo, with dG/dx = dRdQs - Xom = Xo * dSdQ.' + Xom = Xo * transpose(dSdQ) return dRdQs - Xom end diff --git a/src/quaternion_types.jl b/src/quaternion_types.jl index 4d0eb9d8..00b6c2e1 100644 --- a/src/quaternion_types.jl +++ b/src/quaternion_types.jl @@ -53,7 +53,7 @@ function (::Type{Q})(t::NTuple{9}) where Q<:Quat not_orthogonal = randn(3,3) u,s,v = svd(not_orthogonal) - is_orthogoral = u * diagm([1, 1, sign(det(u * v.'))]) * v.' + is_orthogoral = u * diagm([1, 1, sign(det(u * transpose(v)))]) * transpose(v) =# a = 1 + t[1] + t[5] + t[9] @@ -188,12 +188,12 @@ function Base.:*(q1::Quat, q2::Quat) q1.w*q2.z + q1.x*q2.y - q1.y*q2.x + q1.z*q2.w) end -function Base.inv(q::Quat) +function inv(q::Quat) Quat(q.w, -q.x, -q.y, -q.z) end -@inline Base.eye(::Type{Quat}) = Quat(1.0, 0.0, 0.0, 0.0) -@inline Base.eye(::Type{Quat{T}}) where {T} = Quat{T}(one(T), zero(T), zero(T), zero(T)) +@inline eye(::Type{Quat}) = Quat(1.0, 0.0, 0.0, 0.0) +@inline eye(::Type{Quat{T}}) where {T} = Quat{T}(one(T), zero(T), zero(T), zero(T)) """ rotation_between(from, to) @@ -274,10 +274,10 @@ end @inline Base.:*(r::RotMatrix, spq::SPQuat) = r * Quat(spq) @inline Base.:*(spq1::SPQuat, spq2::SPQuat) = Quat(spq1) * Quat(spq2) -@inline Base.inv(spq::SPQuat) = SPQuat(-spq.x, -spq.y, -spq.z) +@inline inv(spq::SPQuat) = SPQuat(-spq.x, -spq.y, -spq.z) -@inline Base.eye(::Type{SPQuat}) = SPQuat(0.0, 0.0, 0.0) -@inline Base.eye(::Type{SPQuat{T}}) where {T} = SPQuat{T}(zero(T), zero(T), zero(T)) +@inline eye(::Type{SPQuat}) = SPQuat(0.0, 0.0, 0.0) +@inline eye(::Type{SPQuat{T}}) where {T} = SPQuat{T}(zero(T), zero(T), zero(T)) # rotation properties @inline rotation_angle(spq::SPQuat) = rotation_angle(Quat(spq)) diff --git a/src/util.jl b/src/util.jl index 89f38b40..967f0014 100644 --- a/src/util.jl +++ b/src/util.jl @@ -10,9 +10,9 @@ function perpendicular_vector(vec::SVector{3}) # find indices of the two elements of vec with the largest absolute values: absvec = abs.(vec) - ind1 = indmax(absvec) # index of largest element + ind1 = argmax(absvec) # index of largest element @inbounds absvec2 = @SVector [ifelse(i == ind1, typemin(T), absvec[i]) for i = 1 : 3] # set largest element to typemin(T) - ind2 = indmax(absvec2) # index of second-largest element + ind2 = argmax(absvec2) # index of second-largest element # perp[ind1] = -vec[ind2], perp[ind2] = vec[ind1], set remaining element to zero: @inbounds perpind1 = -vec[ind2] diff --git a/test/2d.jl b/test/2d.jl index c25c3465..0249b4e6 100644 --- a/test/2d.jl +++ b/test/2d.jl @@ -1,4 +1,4 @@ -using Rotations, StaticArrays, Base.Test +using Rotations, StaticArrays, Compat.Test @testset "2d Rotations" begin @@ -38,8 +38,8 @@ using Rotations, StaticArrays, Base.Test for i = 1:repeats r = rand(R) @test isrotation(r) - @test inv(r) == r' - @test inv(r) == r.' + @test inv(r) == adjoint(r) + @test inv(r) == transpose(r) @test inv(r)*r ≈ I @test r*inv(r) ≈ I end @@ -112,14 +112,6 @@ using Rotations, StaticArrays, Base.Test @test R * R ≈ T[0 -1; 1 0] end end - - @testset "show" begin - io = IOBuffer() - r = rand(RotMatrix{2}) - show(io, MIME("text/plain"), r) - str = String(take!(io)) - @test startswith(str, "2×2 RotMatrix{Float64}:") - end end nothing diff --git a/test/rotation_tests.jl b/test/rotation_tests.jl index 72df1857..de5518a5 100644 --- a/test/rotation_tests.jl +++ b/test/rotation_tests.jl @@ -108,8 +108,8 @@ all_types = (RotMatrix{3}, Quat, SPQuat, AngleAxis, RodriguesVec, srand(0) for i = 1:repeats r = rand(R) - @test inv(r) == r' - @test inv(r) == r.' + @test inv(r) == adjoint(r) + @test inv(r) == transpose(r) @test inv(r)*r ≈ I @test r*inv(r) ≈ I end @@ -183,7 +183,7 @@ all_types = (RotMatrix{3}, Quat, SPQuat, AngleAxis, RodriguesVec, ######################################################################### function nearest_orthonormal(not_orthonormal) u,s,v = svd(not_orthonormal) - return u * diagm([1, 1, sign(det(u * v.'))]) * v.' + return u * Diagonal([1, 1, sign(det(u * transpose(v)))]) * transpose(v) end @testset "DCM to Quat" begin @@ -302,4 +302,17 @@ all_types = (RotMatrix{3}, Quat, SPQuat, AngleAxis, RodriguesVec, rot = eye(RotMatrix{3, Float64}) @inferred Tuple(rot) end + + @testset "Testing show" begin + io = IOBuffer() + r = rand(RotMatrix{2}) + show(io, MIME("text/plain"), r) + str = String(take!(io)) + @test startswith(str, "2×2 RotMatrix{2,Float64,4}:") + + rxyz = RotXYZ(1.0, 2.0, 3.0) + show(io, MIME("text/plain"), rxyz) + str = String(take!(io)) + @test startswith(str, "3×3 RotXYZ{Float64}(1.0, 2.0, 3.0):") + end end diff --git a/test/runtests.jl b/test/runtests.jl index 9b7dde53..169a0c3d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,7 +1,11 @@ -using Base.Test +using Compat +using Compat.Test +using Compat.LinearAlgebra using Rotations using StaticArrays +import Compat.Random: srand + # Check that there are no ambiguities beyond those present in StaticArrays ramb = detect_ambiguities(Rotations, Base, Core) samb = detect_ambiguities(StaticArrays, Base, Core) @@ -14,4 +18,4 @@ include("2d.jl") include("rotation_tests.jl") include("derivative_tests.jl") -include(joinpath("..", "perf", "runbenchmarks.jl")) +include(joinpath(@__DIR__, "..", "perf", "runbenchmarks.jl"))