Skip to content

Commit c5d24f9

Browse files
authoredJun 18, 2024··
Merge pull request #95 from abhro/doc-deploy
Deploy documentation
2 parents a0b7026 + 73f365c commit c5d24f9

File tree

9 files changed

+343
-44
lines changed

9 files changed

+343
-44
lines changed
 

‎.github/workflows/ci.yml

+17-18
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,20 @@ jobs:
3333
env:
3434
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
3535

36-
# Enable the below for Documenter build
37-
# docs:
38-
# name: Documentation
39-
# runs-on: ubuntu-latest
40-
# steps:
41-
# - uses: actions/checkout@v1
42-
# - uses: julia-actions/setup-julia@latest
43-
# with:
44-
# version: '1.3'
45-
# - run: julia --project=docs -e '
46-
# using Pkg;
47-
# Pkg.develop(PackageSpec(; path=pwd()));
48-
# Pkg.instantiate();'
49-
# - run: julia --project=docs docs/make.jl
50-
# env:
51-
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52-
# # Needed due to https://github.com/JuliaDocs/Documenter.jl/issues/1177
53-
# DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}
36+
docs:
37+
name: Documentation
38+
runs-on: ubuntu-latest
39+
steps:
40+
- uses: actions/checkout@v1
41+
- uses: julia-actions/setup-julia@latest
42+
with:
43+
version: '1'
44+
- uses: julia-actions/cache@v1
45+
- name: Install dependencies
46+
run: julia --project=docs -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate();'
47+
- name: Build and deploy
48+
env:
49+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50+
# Needed due to https://github.com/JuliaDocs/Documenter.jl/issues/1177
51+
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}
52+
run: julia --project=docs docs/make.jl

‎docs/make.jl

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
1-
using Documenter
21
using CoordinateTransformations
2+
using Documenter
3+
using Documenter.Remotes: GitHub
4+
5+
DocMeta.setdocmeta!(
6+
CoordinateTransformations,
7+
:DocTestSetup,
8+
:(using CoordinateTransformations),
9+
recursive = true,
10+
)
311

412
makedocs(
513
sitename = "CoordinateTransformations.jl",
14+
modules = [CoordinateTransformations],
15+
repo = GitHub("JuliaGeometry/CoordinateTransformations.jl"),
616
pages = [
717
"Introduction" => "index.md",
818
"API" => "api.md",
919
],
1020
)
21+
22+
deploydocs(
23+
repo = "github.com/JuliaGeometry/CoordinateTransformations.jl.git"
24+
)

‎docs/src/api.md

+44-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,47 @@
1-
# CoordinateTransformations.jl documentation
1+
# API Reference
22

3-
## API Reference
3+
## Transformations
4+
```@docs
5+
Transformation
6+
CoordinateTransformations.ComposedTransformation
7+
IdentityTransformation
8+
PerspectiveMap
9+
inv
10+
cameramap
11+
compose
12+
recenter
13+
transform_deriv
14+
transform_deriv_params
15+
```
16+
17+
## Affine maps
18+
```@docs
19+
AbstractAffineMap
20+
AffineMap
21+
AffineMap(::Transformation, ::Any)
22+
AffineMap(::Pair)
23+
LinearMap
24+
Translation
25+
```
26+
27+
## 2D Coordinates
28+
```@docs
29+
Polar
30+
PolarFromCartesian
31+
CartesianFromPolar
32+
```
33+
34+
## 3D Coordinates
35+
```@docs
36+
Cylindrical
37+
Spherical
38+
```
439

5-
```@autodocs
6-
Modules = [CoordinateTransformations]
40+
```@docs
41+
CartesianFromCylindrical
42+
CartesianFromSpherical
43+
CylindricalFromCartesian
44+
CylindricalFromSpherical
45+
SphericalFromCartesian
46+
SphericalFromCylindrical
747
```

‎docs/src/index.md

+219-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,219 @@
1-
# CoordinateTransformations.jl
1+
```@meta
2+
DocTestSetup = quote
3+
using CoordinateTransformations
4+
end
5+
```
6+
# CoordinateTransformations
7+
8+
[![Build Status](https://github.com/JuliaGeometry/CoordinateTransformations.jl/workflows/CI/badge.svg)](https://github.com/JuliaGeometry/CoordinateTransformations.jl/actions?query=workflow%3ACI)
9+
10+
**CoordinateTransformations** is a Julia package to manage simple or complex
11+
networks of coordinate system transformations. Transformations can be easily
12+
applied, inverted, composed, and differentiated (both with respect to the
13+
input coordinates and with respect to transformation parameters such as rotation
14+
angle). Transformations are designed to be light-weight and efficient enough
15+
for, e.g., real-time graphical applications, while support for both explicit
16+
and automatic differentiation makes it easy to perform optimization and
17+
therefore ideal for computer vision applications such as SLAM (simultaneous
18+
localization and mapping).
19+
20+
The package provide two main pieces of functionality
21+
22+
1. Primarily, an interface for defining `Transformation`s and applying
23+
(by calling), inverting (`inv()`), composing (`` or `compose()`) and
24+
differentiating (`transform_deriv()` and `transform_deriv_params()`) them.
25+
26+
2. A small set of built-in, composable, primitive transformations for
27+
transforming 2D and 3D points (optionally leveraging the *StaticArrays*
28+
and *Rotations* packages).
29+
30+
## Quick start
31+
32+
Let's translate a 3D point:
33+
```julia
34+
using CoordinateTransformations, Rotations, StaticArrays
35+
36+
x = SVector(1.0, 2.0, 3.0) # SVector is provided by StaticArrays.jl
37+
trans = Translation(3.5, 1.5, 0.0)
38+
39+
y = trans(x)
40+
```
41+
42+
We can either apply different transformations in turn,
43+
```julia
44+
rot = LinearMap(RotX(0.3)) # Rotate 0.3 radians about X-axis, from Rotations.jl
45+
46+
z = trans(rot(x))
47+
```
48+
or build a composed transformation using the `` operator (accessible at the
49+
REPL by typing `\circ` then tab):
50+
```julia
51+
composed = trans rot # alternatively, use compose(trans, rot)
52+
53+
composed(x) == z
54+
```
55+
A composition of a `Translation` and a `LinearMap` results in an `AffineMap`.
56+
57+
We can invert the transformation:
58+
```julia
59+
composed_inv = inv(composed)
60+
61+
composed_inv(z) == x
62+
```
63+
64+
For any transformation, we can shift the origin to a new point using `recenter`:
65+
```julia
66+
rot_around_x = recenter(rot, x)
67+
```
68+
Now `rot_around_x` is a rotation around the point `x = SVector(1.0, 2.0, 3.0)`.
69+
70+
71+
Finally, we can construct a matrix describing how the components of `z`
72+
differentiates with respect to components of `x`:
73+
```julia
74+
# In general, the transform may be non-linear, and thus we require
75+
# the value of x to compute the derivative
76+
∂z_∂x = transform_deriv(composed, x)
77+
```
78+
79+
Or perhaps we want to know how `y` will change with respect to changes of
80+
to the translation parameters:
81+
```julia
82+
∂y_∂θ = transform_deriv_params(trans, x)
83+
```
84+
85+
## Interface
86+
87+
Transformations are derived from `Transformation`. As an example, we have
88+
`Translation{T} <: Transformation`. A `Translation` will accept and translate
89+
points in a variety of formats, such as `Vector` or `SVector`, but in general
90+
your custom-defined `Transformation`s could transform any Julia object.
91+
92+
Transformations can be reversed using `inv(trans)`. They can be chained
93+
together using the `` operator (`trans1 ∘ trans2`) or `compose` function (`compose(trans1, trans2)`).
94+
In this case, `trans2` is applied first to the data, before `trans1`.
95+
Composition may be intelligent, for instance by precomputing a new `Translation`
96+
by summing the elements of two existing `Translation`s, and yet other
97+
transformations may compose to the `IdentityTransformation`. But by default,
98+
composition will result in a `ComposedTransformation` object which simply
99+
dispatches to apply the transformations in the correct order.
100+
101+
Finally, the matrix describing how differentials propagate through a transform
102+
can be calculated with the `transform_deriv(trans, x)` method. The derivatives
103+
of how the output depends on the transformation parameters is accessed via
104+
`transform_deriv_params(trans, x)`. Users currently have to overload these methods,
105+
as no fall-back automatic differentiation is currently included. Alternatively,
106+
all the built-in types and transformations are compatible with automatic differentiation
107+
techniques, and can be parameterized by *DualNumbers*' `DualNumber` or *ForwardDiff*'s `Dual`.
108+
109+
## Built-in transformations
110+
111+
A small number of 2D and 3D coordinate systems and transformations are included.
112+
We also have `IdentityTransformation` and `ComposedTransformation`, which allows us
113+
to nest together arbitrary transformations to create a complex yet efficient
114+
transformation chain.
115+
116+
### Coordinate types
117+
118+
The package accepts any `AbstractVector` type for Cartesian coordinates. For speed, we recommend
119+
using a statically-sized container such as `SVector{N}` from *StaticArrays*.
120+
121+
We do provide a few specialist coordinate types. The `Polar(r, θ)` type is a 2D
122+
polar representation of a point, and similarly in 3D we have defined
123+
`Spherical(r, θ, ϕ)` and `Cylindrical(r, θ, z)`.
124+
125+
### Coordinate system transformations
126+
127+
Two-dimensional coordinates may be converted using these parameterless (singleton)
128+
transformations:
129+
130+
1. [`PolarFromCartesian()`](@ref)
131+
2. [`CartesianFromPolar()`](@ref)
132+
133+
Three-dimensional coordinates may be converted using these parameterless
134+
transformations:
135+
136+
1. [`SphericalFromCartesian()`](@ref)
137+
2. [`CartesianFromSpherical()`](@ref)
138+
3. [`SphericalFromCylindrical()`](@ref)
139+
4. [`CylindricalFromSpherical()`](@ref)
140+
5. [`CartesianFromCylindrical()`](@ref)
141+
6. [`CylindricalFromCartesian()`](@ref)
142+
143+
However, you may find it simpler to use the convenience constructors like
144+
`Polar(SVector(1.0, 2.0))`.
145+
146+
### Translations
147+
148+
Translations can be be applied to Cartesian coordinates in arbitrary dimensions,
149+
by e.g. `Translation(Δx, Δy)` or `Translation(Δx, Δy, Δz)` in 2D/3D, or by
150+
`Translation(Δv)` in general (with `Δv` an `AbstractVector`). Compositions of
151+
two `Translation`s will intelligently create a new `Translation` by adding the
152+
translation vectors.
153+
154+
### Linear transformations
155+
156+
Linear transformations (a.k.a. linear maps), including rotations, can be
157+
encapsulated in the `LinearMap` type, which is a simple wrapper of an
158+
`AbstractMatrix`.
159+
160+
You are able to provide any matrix of your choosing, but your choice of type
161+
will have a large effect on speed. For instance, if you know the dimensionality
162+
of your points (e.g. 2D or 3D) you might consider a statically sized matrix
163+
like `SMatrix` from *StaticArrays.jl*. We recommend performing 3D rotations
164+
using those from *Rotations.jl* for their speed and flexibility. Scaling will
165+
be efficient with Julia's built-in `UniformScaling`. Also note that compositions
166+
of two `LinearMap`s will intelligently create a new `LinearMap` by multiplying
167+
the transformation matrices.
168+
169+
### Affine maps
170+
171+
An Affine map encapsulates a more general set of transformation which are
172+
defined by a composition of a translation and a linear transformation. An
173+
`AffineMap` is constructed from an `AbstractVector` translation `v` and an
174+
`AbstractMatrix` linear transformation `M`. It will perform the mapping
175+
`x -> M*x + v`, but the order of addition and multiplication will be more obvious
176+
(and controllable) if you construct it from a composition of a linear map
177+
and a translation, e.g. `Translation(v) ∘ LinearMap(v)` (or any combination of
178+
`LinearMap`, `Translation` and `AffineMap`).
179+
180+
`AffineMap`s can be constructed to fit point pairs `from_points => to_points`:
181+
182+
```jldoctest
183+
julia> from_points = [[0, 0], [1, 0], [0, 1]];
184+
185+
julia> to_points = [[1, 1], [3, 1], [1.5, 3]];
186+
187+
julia> AffineMap(from_points => to_points)
188+
AffineMap([1.9999999999999996 0.5; -5.551115123125783e-16 2.0], [0.9999999999999999, 1.0000000000000002])
189+
```
190+
191+
The points can be supplied as a collection of vectors or as a matrix with points as columns.
192+
193+
### Perspective transformations
194+
195+
The perspective transformation maps real-space coordinates to those on a virtual
196+
"screen" of one lesser dimension. For instance, this process is used to render
197+
3D scenes to 2D images in computer generated graphics and games. It is an ideal
198+
model of how a pinhole camera operates and is a good approximation of the modern
199+
photography process.
200+
201+
The `PerspectiveMap()` command creates a `Transformation` to perform the
202+
projective mapping. It can be applied individually, but is particularly
203+
powerful when composed with an `AffineMap` containing the position and
204+
orientation of the camera in your scene. For example, to transfer `points` in 3D
205+
space to 2D `screen_points` giving their projected locations on a virtual camera
206+
image, you might use the following code:
207+
208+
```julia
209+
cam_transform = PerspectiveMap() inv(AffineMap(cam_rotation, cam_position))
210+
screen_points = map(cam_transform, points)
211+
```
212+
213+
There is also a `cameramap()` convenience function that can create a composed
214+
transformation that includes the intrinsic scaling (e.g. focal length and pixel
215+
size) and offset (defining which pixel is labeled `(0,0)`) of an imaging system.
216+
217+
## Acknowledgements
218+
219+
- The [Fugro Roames organization](https://github.com/FugroRoames)

‎src/affine.jl

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
"""
2+
abstract type AbstractAffineMap <: Transformation
3+
4+
`AbstractAffineMap` is the supertype of all affine transformations,
5+
including linear transformations.
6+
"""
17
abstract type AbstractAffineMap <: Transformation end
28

39
"""
@@ -114,7 +120,7 @@ end
114120
"""
115121
AffineMap(trans::Transformation, x0)
116122
117-
Create an Affine transformation corresponding to the differential transformation
123+
Create an affine transformation corresponding to the differential transformation
118124
of `x0 + dx` according to `trans`, i.e. the Affine transformation that is
119125
locally most accurate in the vicinity of `x0`.
120126
"""

‎src/coordinatesystems.jl

+16-10
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,6 @@ orthogonal projection of `v` on the `xy` plane.
104104
* `ϕ` is the latitude. It is the angle from `v_xy` to `v`.
105105
106106
```jldoctest
107-
julia> using CoordinateTransformations
108-
109107
julia> v = randn(3);
110108
111109
julia> sph = SphericalFromCartesian()(v);
@@ -131,7 +129,8 @@ function Spherical(r, θ, ϕ)
131129
end
132130

133131
Base.show(io::IO, x::Spherical) = print(io, "Spherical(r=$(x.r), θ=$(x.θ) rad, ϕ=$(x.ϕ) rad)")
134-
Base.isapprox(p1::Spherical, p2::Spherical; kwargs...) = isapprox(p1.r, p2.r; kwargs...) && isapprox(p1.θ, p2.θ; kwargs...) && isapprox(p1.ϕ, p2.ϕ; kwargs...)
132+
Base.isapprox(p1::Spherical, p2::Spherical; kwargs...) =
133+
isapprox(p1.r, p2.r; kwargs...) && isapprox(p1.θ, p2.θ; kwargs...) && isapprox(p1.ϕ, p2.ϕ; kwargs...)
135134

136135
"""
137136
Cylindrical(r, θ, z)
@@ -153,7 +152,8 @@ function Cylindrical(r, θ, z)
153152
end
154153

155154
Base.show(io::IO, x::Cylindrical) = print(io, "Cylindrical(r=$(x.r), θ=$(x.θ) rad, z=$(x.z))")
156-
Base.isapprox(p1::Cylindrical, p2::Cylindrical; kwargs...) = isapprox(p1.r, p2.r; kwargs...) && isapprox(p1.θ, p2.θ; kwargs...) && isapprox(p1.z, p2.z; kwargs...)
155+
Base.isapprox(p1::Cylindrical, p2::Cylindrical; kwargs...) =
156+
isapprox(p1.r, p2.r; kwargs...) && isapprox(p1.θ, p2.θ; kwargs...) && isapprox(p1.z, p2.z; kwargs...)
157157

158158
"""
159159
SphericalFromCartesian()
@@ -219,7 +219,8 @@ function transform_deriv(::SphericalFromCartesian, x::AbstractVector)
219219
-fxy*cxy cxy zero(T);
220220
f*x[1] f*x[2] rxy/(r*r) ]
221221
end
222-
transform_deriv_params(::SphericalFromCartesian, x::AbstractVector) = error("SphericalFromCartesian has no parameters")
222+
transform_deriv_params(::SphericalFromCartesian, x::AbstractVector) =
223+
error("SphericalFromCartesian has no parameters")
223224

224225
function (::CartesianFromSpherical)(x::Spherical)
225226
sθ, cθ = sincos(x.θ)
@@ -233,7 +234,8 @@ function transform_deriv(::CartesianFromSpherical, x::Spherical{T}) where T
233234
*cϕ x.r**-x.r**sϕ ;
234235
zero(T) x.r * cϕ ]
235236
end
236-
transform_deriv_params(::CartesianFromSpherical, x::Spherical) = error("CartesianFromSpherical has no parameters")
237+
transform_deriv_params(::CartesianFromSpherical, x::Spherical) =
238+
error("CartesianFromSpherical has no parameters")
237239

238240
# Cartesian <-> Cylindrical
239241
function (::CylindricalFromCartesian)(x::AbstractVector)
@@ -253,7 +255,8 @@ function transform_deriv(::CylindricalFromCartesian, x::AbstractVector)
253255
-f*c c zero(T) ;
254256
zero(T) zero(T) one(T) ]
255257
end
256-
transform_deriv_params(::CylindricalFromCartesian, x::AbstractVector) = error("CylindricalFromCartesian has no parameters")
258+
transform_deriv_params(::CylindricalFromCartesian, x::AbstractVector) =
259+
error("CylindricalFromCartesian has no parameters")
257260

258261
function (::CartesianFromCylindrical)(x::Cylindrical)
259262
sθ, cθ = sincos(x.θ)
@@ -265,7 +268,8 @@ function transform_deriv(::CartesianFromCylindrical, x::Cylindrical{T}) where {T
265268
sθ x.r*zero(T) ;
266269
zero(T) zero(T) one(T) ]
267270
end
268-
transform_deriv_params(::CartesianFromPolar, x::Cylindrical) = error("CartesianFromCylindrical has no parameters")
271+
transform_deriv_params(::CartesianFromPolar, x::Cylindrical) =
272+
error("CartesianFromCylindrical has no parameters")
269273

270274
function (::CylindricalFromSpherical)(x::Spherical)
271275
sϕ, cϕ = sincos(x.ϕ)
@@ -276,7 +280,8 @@ function transform_deriv(::CylindricalFromSpherical, x::Spherical)
276280
M2 = transform_deriv(CartesianFromSpherical(), x)
277281
return M1*M2
278282
end
279-
transform_deriv_params(::CylindricalFromSpherical, x::Spherical) = error("CylindricalFromSpherical has no parameters")
283+
transform_deriv_params(::CylindricalFromSpherical, x::Spherical) =
284+
error("CylindricalFromSpherical has no parameters")
280285

281286
function (::SphericalFromCylindrical)(x::Cylindrical)
282287
Spherical(hypot(x.r,x.z),x.θ,atan(x.z,x.r))
@@ -286,7 +291,8 @@ function transform_deriv(::SphericalFromCylindrical, x::Cylindrical)
286291
M2 = transform_deriv(CartesianFromCylindrical(), x)
287292
return M1*M2
288293
end
289-
transform_deriv_params(::SphericalFromCylindrical, x::Cylindrical) = error("SphericalFromCylindrical has no parameters")
294+
transform_deriv_params(::SphericalFromCylindrical, x::Cylindrical) =
295+
error("SphericalFromCylindrical has no parameters")
290296

291297
Base.inv(::SphericalFromCartesian) = CartesianFromSpherical()
292298
Base.inv(::CartesianFromSpherical) = SphericalFromCartesian()

‎src/core.jl

+14-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
# transform_deriv() and transform_deriv_params()
77

88
"""
9+
abstract type Transformation
10+
911
The `Transformation` supertype defines a simple interface for performing
1012
transformations. Subtypes should be able to apply a coordinate system
1113
transformation on the correct data types by overloading the call method, and
@@ -15,6 +17,8 @@ Efficient compositions can optionally be defined by `compose()` (equivalently `
1517
abstract type Transformation end
1618

1719
"""
20+
IdentityTransformation <: Transformation
21+
1822
The `IdentityTransformation` is a singleton `Transformation` that returns the
1923
input unchanged, similar to `identity`.
2024
"""
@@ -23,8 +27,10 @@ struct IdentityTransformation <: Transformation; end
2327
@inline (::IdentityTransformation)(x) = x
2428

2529
"""
26-
A `ComposedTransformation` simply executes two transformations successively, and
27-
is the fallback output type of `compose()`.
30+
ComposedTransformation{T1, T2} <: Transformation
31+
32+
A `ComposedTransformation` simply executes two transformations `T2` and `T1`
33+
successively, and is the fallback output type of `compose()`.
2834
"""
2935
struct ComposedTransformation{T1 <: Transformation, T2 <: Transformation} <: Transformation
3036
t1::T1
@@ -106,7 +112,8 @@ recenter(trans::Transformation, origin::Tuple) = recenter(trans, SVector(origin)
106112
A matrix describing how differentials on the parameters of `x` flow through to
107113
the output of transformation `trans`.
108114
"""
109-
transform_deriv(trans::Transformation, x) = error("Differential matrix of transform $trans with input $x not defined")
115+
transform_deriv(trans::Transformation, x) =
116+
error("Differential matrix of transform $trans with input $x not defined")
110117

111118
transform_deriv(::IdentityTransformation, x) = I
112119

@@ -124,9 +131,11 @@ end
124131
A matrix describing how differentials on the parameters of `trans` flow through
125132
to the output of transformation `trans` given input `x`.
126133
"""
127-
transform_deriv_params(trans::Transformation, x) = error("Differential matrix of parameters of transform $trans with input $x not defined")
134+
transform_deriv_params(trans::Transformation, x) =
135+
error("Differential matrix of parameters of transform $trans with input $x not defined")
128136

129-
transform_deriv_params(::IdentityTransformation, x) = error("IdentityTransformation has no parameters")
137+
transform_deriv_params(::IdentityTransformation, x) =
138+
error("IdentityTransformation has no parameters")
130139

131140
function transform_deriv_params(trans::ComposedTransformation, x)
132141
x2 = trans.t2(x)

‎test/coordinatesystems.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@
473473
partials(rθϕ_gn.θ, 1) partials(rθϕ_gn.θ, 2) partials(rθϕ_gn.θ, 3);
474474
partials(rθϕ_gn.ϕ, 1) partials(rθϕ_gn.ϕ, 2) partials(rθϕ_gn.ϕ, 3) ]
475475
m = transform_deriv(s_from_cyl, rθz)
476-
# @test isapprox(m, m_gn; atol = 1e-12)
476+
#@test isapprox(m, m_gn; atol = 1e-12)
477477
@test m m_gn
478478

479479
@testset "Common types" begin

‎test/runtests.jl

+10-3
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,22 @@ using CoordinateTransformations
44
using ForwardDiff: Dual, partials
55
using StaticArrays
66
using Unitful
7-
using Documenter: doctest
7+
using Documenter
88
using Aqua
99

1010
@testset "CoordinateTransformations" begin
11-
12-
doctest(CoordinateTransformations, manual=false)
1311
include("core.jl")
1412
include("coordinatesystems.jl")
1513
include("affine.jl")
1614
include("perspective.jl")
15+
1716
Aqua.test_all(CoordinateTransformations)
17+
18+
DocMeta.setdocmeta!(
19+
CoordinateTransformations,
20+
:DocTestSetup,
21+
:(using CoordinateTransformations),
22+
recursive=true,
23+
)
24+
doctest(CoordinateTransformations, manual=true)
1825
end

0 commit comments

Comments
 (0)
Please sign in to comment.