Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

[weakdeps]
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"

[extensions]
RotationsMakieExt = "Makie"
RotationsRecipesBaseExt = "RecipesBase"

[compat]
Expand All @@ -21,6 +23,7 @@ BenchmarkTools = "1"
ForwardDiff = "0.10"
InteractiveUtils = "1"
LinearAlgebra = "1"
Makie = "0.24"
Plots = "1"
Quaternions = "0.5.3, 0.6, 0.7"
Random = "1"
Expand All @@ -36,6 +39,7 @@ BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Quaternions = "94ee1d12-ae83-5a48-8b1c-48b8ff168ae0"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Expand Down
1 change: 1 addition & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
4 changes: 3 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using Rotations
using Documenter
using Makie

DocMeta.setdocmeta!(Rotations, :DocTestSetup, :(using Rotations); recursive=true)

RotationsMakieExt = Base.get_extension(Rotations, :RotationsMakieExt)
makedocs(;
modules=[Rotations],
modules=[Rotations, RotationsMakieExt],
repo="https://github.com/JuliaGeometry/Rotations.jl/blob/{commit}{path}#{line}",
sitename="Rotations.jl",
format=Documenter.HTML(;
Expand Down
81 changes: 72 additions & 9 deletions docs/src/visualizing.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,75 @@
# Visualizing Rotation
In general, ``3 \times 3`` matrix has ``9`` parameters, so it's hard to visualize a matrix as a point.
However, the Lie group ``SO(3)`` and ``SU(2)`` are ``3``-dimensional space, and it is able to visualize its element as a point in our ``3``-dimensional space.
```@setup ext
using Rotations, Makie
Ext = Base.get_extension(Rotations, :RotationsMakieExt)
```
# Visualizing Rotations

## using `MRP`
(TBW)
Visualizing rotations is crucial for understanding their behavior and effects. While a general ``3 \times 3`` rotation matrix has 9 parameters, rotations actually live on the 3-dimensional manifold ``SO(3)``, making them suitable for 3D visualization.

## using `RodriguesParam`
(TBW)
This package provides plotting capabilities through [Makie.jl](https://makie.juliaplots.org/) via the `RotationsMakieExt` extension, which is automatically loaded when both `Rotations` and a Makie backend are available.

## using `RotationVec`
(TBW)
## Plotting Rotation Matrices

### Basic 2D Rotation Plotting

```julia
using Rotations
using CairoMakie # or GLMakie for interactive plots

# Create a 2D rotation
rot2d = RotMatrix{2}(π/4) # 45-degree rotation

# Plot the rotation
fig, ax, plt = plot(rot2d)
```

```@docs
RotationsMakieExt.rotation2plot
RotationsMakieExt.rotation2plot!
RotationsMakieExt.Rotation2Plot
```

The functions are also accessible from the `Visualization` module of the package.

### Basic 3D Rotation Plotting

```julia
# Create a 3D rotation from Euler angles
rot3d = RotXYZ(π/6, π/4, π/3)

# Plot the rotation
fig, ax, plt = plot(rot3d)
```

```@docs
RotationsMakieExt.rotation3plot
RotationsMakieExt.rotation3plot!
RotationsMakieExt.Rotation3Plot
```

### Combining Multiple Rotations

```julia
using GLMakie

# Create several rotations
rotations = [
RotX(π/6),
RotY(π/4),
RotZ(π/3),
RotXYZ(π/6, π/4, π/3)
]

# Plot them in a grid
fig = Figure(resolution = (800, 800))

for (i, rot) in enumerate(rotations)
ax = Axis3(fig[div(i-1, 2) + 1, mod(i-1, 2) + 1],
title = "Rotation $i")
plot!(ax, rot,
boxcolor = [:red, :green, :blue, :orange][i],
axissize = 1.5)
end

fig
```
132 changes: 132 additions & 0 deletions ext/RotationsMakieExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
module RotationsMakieExt

using Rotations
using Makie
using StaticArrays

import Rotations.Visualization: rotation2plot, rotation2plot!,rotation3plot ,rotation3plot

Makie.plottype(::Rotation{2}) = Rotation2Plot
Makie.plottype(::Rotation{3}) = Rotation3Plot

@recipe Rotation2Plot (rot::Rotation{2},) begin
begin
arrows_attributes = Makie.documented_attributes(Arrows2D)
filter!(a -> a.first ∉ [:color], arrows_attributes.d)
arrows_attributes
end...
"Origin point for the 2D rotation visualization"
origin = SVector(0, 0)
"Size of the reference square"
boxsize = 0.2
"Length of the axis lines"
axissize = 1.0
"Colours for the two axes (x, y)"
axiscolors = [:red, :green]
"Colour of the reference square"
boxcolor = :gray
end

function Makie.plot!(p::Rotation2Plot{<:Tuple{<:Rotation{2}}})
# Use ComputationPipeline for dynamic updates
map!(p.attributes, [:rot, :origin, :boxsize, :axissize], [:arrows_origin, :arrows1_end, :arrows2_end, :box_points]) do rot, origin, l, L
e₁ = rot[:, 1]
e₂ = rot[:, 2]
ox, oy = origin

# Compute box corners
box_points = [Point2f(ox, oy) + rot * Point2f(x, y) for x in (-l, l) for y in (-l, l)][[1, 2, 4, 3]]

return (Point2f(ox, oy), Point2f(ox + L * e₁[1], oy + L * e₁[2]), Point2f(ox + L * e₂[1], oy + L * e₂[2]), box_points)
end

map!(p.attributes, [:axiscolors], [:arrows1_color, :arrows2_color]) do axiscolors
(axiscolors[1], axiscolors[2])
end

arrows2d!(p, p.attributes, p.arrows_origin, p.arrows1_end; color=p.arrows1_color)
arrows2d!(p, p.attributes, p.arrows_origin, p.arrows2_end; color=p.arrows2_color)
poly!(p, p.attributes, p.box_points; color=p.boxcolor)

return p
end

@recipe Rotation3Plot (rot::Rotation{3},) begin
begin
arrows_attributes = Makie.documented_attributes(Arrows3D)
filter!(a -> a.first ∉ [:color], arrows_attributes.d)
arrows_attributes
end...
"Origin point for the 3D rotation visualization"
origin = SVector(0, 0, 0)
"Size of the reference cube"
boxsize = 0.2
"Length of the axis lines"
axissize = 1.0
"Colours for the three axes (x, y, z)"
axiscolors = [:red, :green, :blue]
"Colour of the reference cube faces"
boxcolor = :gray
"Colour of cube edges"
edgecolor = :black
end

function Makie.plot!(p::Rotation3Plot{<:Tuple{<:Rotation{3}}})
# Use ComputationPipeline for dynamic updates
map!(p.attributes, [:rot, :origin, :boxsize, :axissize], [:arrows_origin, :arrows1_end, :arrows2_end, :arrows3_end, :cube_points, :cube_faces, :cube_edges]) do rot, origin, l, L
e₁ = rot[:, 1]
e₂ = rot[:, 2]
e₃ = rot[:, 3]
ox, oy, oz = origin

# Compute cube vertices
cube_points = [Point3f(ox, oy, oz) + rot * Point3f(x, y, z) for x in (-l, l) for y in (-l, l) for z in (-l, l)]

# Define cube faces (triangulated)
cube_faces = [
1, 2, 4, 4, 3, 1, # bottom
5, 6, 8, 8, 7, 5, # top
1, 2, 6, 6, 5, 1, # front
3, 4, 8, 8, 7, 3, # back
1, 3, 7, 7, 5, 1, # left
2, 4, 8, 8, 6, 2 # right
]

# Define cube edges for wireframe
cube_edges = [
# Bottom face edges
cube_points[1], cube_points[2], cube_points[2], cube_points[4],
cube_points[4], cube_points[3], cube_points[3], cube_points[1],
# Top face edges
cube_points[5], cube_points[6], cube_points[6], cube_points[8],
cube_points[8], cube_points[7], cube_points[7], cube_points[5],
# Vertical edges
cube_points[1], cube_points[5], cube_points[2], cube_points[6],
cube_points[3], cube_points[7], cube_points[4], cube_points[8]
]

return (
Point3f(ox, oy, oz),
Point3f(ox + L * e₁[1], oy + L * e₁[2], oz + L * e₁[3]),
Point3f(ox + L * e₂[1], oy + L * e₂[2], oz + L * e₂[3]),
Point3f(ox + L * e₃[1], oy + L * e₃[2], oz + L * e₃[3]),
cube_points, cube_faces, cube_edges
)
end

map!(p.attributes, [:axiscolors], [:arrows1_color, :arrows2_color, :arrows3_color]) do axiscolors
(axiscolors[1], axiscolors[2], axiscolors[3])
end

arrows3d!(p, p.attributes, p.arrows_origin, p.arrows1_end, color=p.arrows1_color)
arrows3d!(p, p.attributes, p.arrows_origin, p.arrows2_end, color=p.arrows2_color)
arrows3d!(p, p.attributes, p.arrows_origin, p.arrows3_end, color=p.arrows3_color)
mesh!(p, p.attributes, p.cube_points, p.cube_faces; color=p.boxcolor)
linesegments!(p, p.attributes, p.cube_edges; color=p.edgecolor)

return p
end

Makie.preferred_axis_type(::Rotation3Plot) = Makie.Axis3

end
1 change: 1 addition & 0 deletions src/Rotations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ include("eigen.jl")
include("rand.jl")
include("rotation_between.jl")
include("deprecated.jl")
include("extensions.jl")

export
# Rotation types
Expand Down
8 changes: 8 additions & 0 deletions src/extensions.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Visualization
function rotation2plot end
function rotation2plot! end
function rotation3plot end
function rotation3plot! end

export rotation2plot, rotation2plot!, rotation3plot, rotation3plot!
end
Loading