Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Continuation of polar axis #2990

Merged
merged 72 commits into from Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
ba2318a
start implementation
jkrumbiegel Apr 17, 2022
ad8900c
First implementation of polar axis
asinghvi17 Jun 1, 2022
9f662fb
Try to make the polar axis "plottable-to"
asinghvi17 Jun 1, 2022
828c9a4
Revert MakieLayout.jl to master state
asinghvi17 Jun 1, 2022
be9302b
Correct documentation of `adjustcam!`
asinghvi17 Jun 1, 2022
8a1d0a6
Implement angular ticks
asinghvi17 Jun 1, 2022
9f2c0a3
Inherit a bit more
asinghvi17 Jun 2, 2022
8c2dc4b
Implement radial ticks
asinghvi17 Jun 2, 2022
3d0f20c
Fix translation for GLMakie
asinghvi17 Jun 4, 2022
eff6180
In Camera2D's ratio check, use only Float32 to compare
asinghvi17 Jun 4, 2022
603f706
Fix bad bbox method, add comments
asinghvi17 Jun 4, 2022
25dca9a
Remove debug statements
asinghvi17 Jun 4, 2022
4fd1d5d
Update for MakieLayout module removal
asinghvi17 Jun 8, 2022
5539a5a
Update the lines when the camera changes.
asinghvi17 Jun 8, 2022
0e4292d
Lift on camera also
asinghvi17 Jun 8, 2022
1d133a1
Add protrusion calculation for theta ticks
asinghvi17 Jun 27, 2022
0ea9644
Allow plot!(po::PolarAxis, ...)
asinghvi17 Jun 27, 2022
e9ba2e4
Manual protrusion calc by checking text bboxes individually.
asinghvi17 Jun 28, 2022
227b0b5
Implement a foundation for hacked theta-lims
asinghvi17 Jun 28, 2022
e662ee6
Fix update_cam!(::Scene)
asinghvi17 Jun 28, 2022
aa0bb82
Quick fix for overlooked item
asinghvi17 Jun 30, 2022
08d7a91
Implement `text_bbox(plot::Text)`
asinghvi17 Jun 30, 2022
8fb816f
Implement inherit for nested theme attributes
asinghvi17 Jun 30, 2022
44eb5ba
Implement title for PolarAxis
asinghvi17 Jun 30, 2022
e2a3582
Switch default theme to inheriting from Axis
asinghvi17 Jun 30, 2022
3dba936
Separate r and theta minor grids in theme
asinghvi17 Jun 30, 2022
bfc2c1d
Update NEWS
asinghvi17 Jun 30, 2022
f2c90c8
General cleanup
asinghvi17 Jun 30, 2022
60ee94a
Adjust ticks, grid and camera on scene area / limit change
asinghvi17 Jun 30, 2022
9432e1f
Change r-tick angle to avoid overlap
asinghvi17 Jun 30, 2022
ae09053
Implement "nonlinear clip" without transparency
asinghvi17 Jun 30, 2022
afbde6e
Address comments from code review
asinghvi17 Jul 1, 2022
061dabd
Merge branch 'master' into jk/polar-axis
Moelf Jul 13, 2022
f6b69b2
Merge branch 'master' into jk/polar-axis
SimonDanisch Jan 14, 2023
2542326
Merge branch 'master' into jk/polar-axis
SimonDanisch May 17, 2023
190cb33
fix errors
ffreyer May 22, 2023
2309c09
prototype axis decorations in polar coords
ffreyer May 29, 2023
64a1654
move axis to overlay scene
ffreyer May 29, 2023
e3e936f
rename θ to theta
ffreyer May 29, 2023
68f5462
add theta tick pad and split onany
ffreyer May 29, 2023
f8809b1
get arbitrary scene areas going
ffreyer May 29, 2023
9a76ff5
cleanup axis clip
ffreyer May 29, 2023
ab168bb
switch to two step limits, remove min radius
ffreyer May 30, 2023
fb3eba3
Merge branch 'master' into ff/jk-as-polar-axis
ffreyer May 30, 2023
2340891
discard camera changes
ffreyer Jun 4, 2023
c42e851
improve observable tracking/cleanup
ffreyer Jun 4, 2023
0069fc0
add camera reset
ffreyer Jun 4, 2023
573dde2
add r tick stroke
ffreyer Jun 4, 2023
651cc48
use degrees for tick labels
ffreyer Jun 4, 2023
171af16
cleanup background color
ffreyer Jun 4, 2023
23cb046
fix theta_0 handling for ticks
ffreyer Jun 4, 2023
d0425a0
turn off minor grid by default
ffreyer Jun 4, 2023
0ce66d7
Merge branch 'master' into ff/jk-as-polar-axis
ffreyer Jun 4, 2023
17eb51a
rename Polar transform and move it
ffreyer Jun 8, 2023
88e5856
Merge branch 'master' into ff/jk-as-polar-axis
SimonDanisch Jul 4, 2023
5f023f4
Merge branch 'master' into ff/jk-as-polar-axis
SimonDanisch Jul 14, 2023
cc02239
temp fix for poly transforms
ffreyer Jul 15, 2023
dcd01f1
cleanup background color handling
ffreyer Jul 15, 2023
7f379c1
cleanup protrusions
ffreyer Jul 15, 2023
c7e86af
cleanup title position
ffreyer Jul 15, 2023
d9620a9
remove default outline on r tick labels
ffreyer Jul 15, 2023
2e03171
general cleanup
ffreyer Jul 15, 2023
17b7208
add docs
ffreyer Jul 15, 2023
ea6d168
add refimg tests
ffreyer Jul 15, 2023
dd1ac4d
add polar transform test & fix inverse
ffreyer Jul 15, 2023
884656e
fix duplicate NEWS entry
ffreyer Jul 17, 2023
ecd8e06
add outlines for theta ticks
ffreyer Jul 17, 2023
c856680
add cycler support to PolarAxis
SimonDanisch Jul 17, 2023
52b13fa
Merge branch 'master' into ff/jk-as-polar-axis
SimonDanisch Jul 17, 2023
43532ee
inherit background color
ffreyer Jul 18, 2023
45ecfb6
add attrdocs
ffreyer Jul 18, 2023
59bbb79
Merge branch 'master' into ff/jk-as-polar-axis
SimonDanisch Jul 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion CairoMakie/src/overrides.jl
Expand Up @@ -115,7 +115,7 @@ draw_poly(scene::Scene, screen::Screen, poly, circle::Circle) = draw_poly(scene,
function draw_poly(scene::Scene, screen::Screen, poly, polygons::AbstractArray{<:Polygon})
model = poly.model[]
space = to_value(get(poly, :space, :data))
projected_polys = project_polygon.(Ref(scene), space, polygons, Ref(model))
projected_polys = project_polygon.(Ref(poly), space, polygons, Ref(model))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The project_x functions in CairoMakie are currently build to take transform_func from passed scenes, which isn't correct but usually irrelevant. However for PolarAxis I added a poly without transformations to fill the outer background which is plotted incorrectly with this approach, so I did a quick and dirty fix here (and in utils.jl). Proper fix/cleanup is in #3040.


color = to_cairo_color(poly.color[], poly)
strokecolor = to_cairo_color(poly.strokecolor[], poly)
Expand Down
25 changes: 13 additions & 12 deletions CairoMakie/src/utils.jl
Expand Up @@ -2,13 +2,13 @@
# Projection utilities #
################################################################################

function project_position(scene, transform_func::T, space, point, model, yflip::Bool = true) where T
function project_position(scene::Scene, transform_func::T, space, point, model::Mat4, yflip::Bool = true) where T
# use transform func
point = Makie.apply_transform(transform_func, point, space)
_project_position(scene, space, point, model, yflip)
end

function _project_position(scene, space, point, model, yflip)
function _project_position(scene::Scene, space, point, model, yflip::Bool)
res = scene.camera.resolution[]
p4d = to_ndim(Vec4f, to_ndim(Vec3f, point, 0f0), 1f0)
clip = Makie.space_to_clip(scene.camera, space) * model * p4d
Expand All @@ -24,8 +24,9 @@ function _project_position(scene, space, point, model, yflip)
return p_0_to_1 .* res
end

function project_position(scene, space, point, model, yflip::Bool = true)
project_position(scene, scene.transformation.transform_func[], space, point, model, yflip)
function project_position(scenelike, space, point, model, yflip::Bool = true)
scene = Makie.get_scene(scenelike)
project_position(scene, Makie.transform_func(scenelike), space, point, model, yflip)
end

function project_scale(scene::Scene, space, s::Number, model = Mat4f(I))
Expand All @@ -46,23 +47,23 @@ function project_scale(scene::Scene, space, s, model = Mat4f(I))
end
end

function project_rect(scene, space, rect::Rect, model)
mini = project_position(scene, space, minimum(rect), model)
maxi = project_position(scene, space, maximum(rect), model)
function project_rect(scenelike, space, rect::Rect, model)
mini = project_position(scenelike, space, minimum(rect), model)
maxi = project_position(scenelike, space, maximum(rect), model)
return Rect(mini, maxi .- mini)
end

function project_polygon(scene, space, poly::P, model) where P <: Polygon
function project_polygon(scenelike, space, poly::P, model) where P <: Polygon
ext = decompose(Point2f, poly.exterior)
interiors = decompose.(Point2f, poly.interiors)
Polygon(
Point2f.(project_position.(Ref(scene), space, ext, Ref(model))),
[Point2f.(project_position.(Ref(scene), space, interior, Ref(model))) for interior in interiors],
Point2f.(project_position.(Ref(scenelike), space, ext, Ref(model))),
[Point2f.(project_position.(Ref(scenelike), space, interior, Ref(model))) for interior in interiors],
)
end

function project_multipolygon(scene, space, multipoly::MP, model) where MP <: MultiPolygon
return MultiPolygon(project_polygon.(Ref(scene), Ref(space), multipoly.polygons, Ref(model)))
function project_multipolygon(scenelike, space, multipoly::MP, model) where MP <: MultiPolygon
return MultiPolygon(project_polygon.(Ref(scenelike), Ref(space), multipoly.polygons, Ref(model)))
end

scale_matrix(x, y) = Cairo.CairoMatrix(x, 0.0, 0.0, y, 0.0, 0.0)
Expand Down
4 changes: 3 additions & 1 deletion NEWS.md
Expand Up @@ -2,6 +2,8 @@

## master

- Added a new Axis type, `PolarAxis`, which is an axis with a polar projection. Input is in `(r, theta)` coordinates and is transformed to `(x, y)` coordinates using the standard polar-to-cartesian transformation. Generally, its attributes are very similar to the usual `Axis` attributes, but `x` is replaced by `r` and `y` by `θ`. It also inherits from the theme of `Axis` in this manner, so should work seamlessly with Makie themes. [#2990](https://github.com/MakieOrg/Makie.jl/pull/2990)
- `inherit` now has a new signature `inherit(scene, attrs::NTuple{N, Symbol}, default_value)`, allowing recipe authors to access nested attributes when trying to inherit from the parent Scene. For example, one could inherit from `scene.Axis.yticks` by `inherit(scene, (:Axis, :yticks), $default_value)`. [#2990](https://github.com/MakieOrg/Makie.jl/pull/2990)
- Deprecated `flatten_plots` in favor of `collect_atomic_plots`. Using the new `collect_atomic_plots` fixed a bug in CairoMakie where the z-level of plots within recipes was not respected. [#2793](https://github.com/MakieOrg/Makie.jl/pull/2793)
- Fixed incorrect line depth in GLMakie [#2843](https://github.com/MakieOrg/Makie.jl/pull/2843)
- Fixed incorrect line alpha in dense lines in GLMakie [#2843](https://github.com/MakieOrg/Makie.jl/pull/2843)
Expand Down Expand Up @@ -165,7 +167,7 @@ role as `datalimits` in `violin` [#2137](https://github.com/MakieOrg/Makie.jl/pu

## v0.17.7

- Improved `Menu` performance, now it should me much harder to reach the boundary of 255 scenes in GLMakie. `Menu` also takes a `default` keyword argument now and can be scrolled if there is too little space available.
- Improved `Menu` performance, now it should be much harder to reach the boundary of 255 scenes in GLMakie. `Menu` also takes a `default` keyword argument now and can be scrolled if there is too little space available.

## v0.17.6

Expand Down
46 changes: 46 additions & 0 deletions ReferenceTests/src/tests/figures_and_makielayout.jl
Expand Up @@ -136,3 +136,49 @@ end
surface!(ax, xs, ys, zs)
fig
end

@reference_test "PolarAxis surface" begin
f = Figure()
ax = PolarAxis(f[1, 1])
zs = [r*cos(phi) for r in range(1, 2, length=100), phi in range(0, 4pi, length=100)]
p = surface!(ax, 0..10, 0..2pi, zs, shading = false, colormap = :coolwarm, colorrange=(-2, 2))
Colorbar(f[1, 2], p)
f
end

# may fail in WGLMakie due to missing dashes
@reference_test "PolarAxis scatterlines spine" begin
f = Figure(resolution = (800, 400))
ax1 = PolarAxis(f[1, 1], title = "No spine", spinevisible = false)
scatterlines!(ax1, range(0, 1, length=100), range(0, 10pi, length=100), color = 1:100)

ax2 = PolarAxis(f[1, 2], title = "Modified spine")
ax2.spinecolor[] = :red
ax2.spinestyle[] = :dash
ax2.spinewidth[] = 5
scatterlines!(ax2, range(0, 1, length=100), range(0, 10pi, length=100), color = 1:100)

f
end

# may fail in CairoMakie due to different text stroke handling
# and in WGLMakie due to missing stroke
@reference_test "PolarAxis decorations" begin
f = Figure(resolution = (400, 400), backgroundcolor = :black)
ax = PolarAxis(
f[1, 1],
backgroundcolor = :black,
rminorgridvisible = true, rminorgridcolor = :red,
rminorgridwidth = 1.0, rminorgridstyle = :dash,
thetaminorgridvisible = true, thetaminorgridcolor = :blue,
thetaminorgridwidth = 1.0, thetaminorgridstyle = :dash,
rgridwidth = 2, rgridcolor = :red,
thetagridwidth = 2, thetagridcolor = :blue,
rticklabelsize = 18, rticklabelcolor = :red,
rticklabelstrokewidth = 1, rticklabelstrokecolor = :white,
thetaticklabelsize = 18, thetaticklabelcolor = :blue,
thetaticklabelstrokewidth = 1, thetaticklabelstrokecolor = :white,
)

f
end
125 changes: 125 additions & 0 deletions docs/examples/blocks/polaraxis.md
@@ -0,0 +1,125 @@
# PolarAxis

The `PolarAxis` is an axis for data in polar coordinates `(radius, angle)`. It
is currently an experimental feature, meaning that some functionality might be
missing or broken, and that the `PolarAxis` is (more) open to breaking changes.

## Creating a PolarAxis

Creating a `PolarAxis` works the same way as creating an `Axis`.

\begin{examplefigure}{svg = true}
```julia
using CairoMakie
CairoMakie.activate!() # hide

f = Figure()

ax = PolarAxis(f[1, 1], title = "Title")

f
```
\end{examplefigure}

## Plotting into an PolarAxis

Like with an `Axis` you can use mutating 2D plot functions directly on a
`PolarAxis`. The input arguments of the plot functions will then be interpreted
in polar coordinates, i.e. as a radius and angle (in radians).

\begin{examplefigure}{svg = true}
```julia
lineobject = lines!(ax, 0..10, sin, color = :red)
scatobject = scatter!(0:0.5:10, cos, color = :orange)

f
```
\end{examplefigure}

Note that not every plot type is compatible with polar transforms. For example
`image` is not as it expects to be drawn on a rectangle. `heatmap` works to a
degree in CairoMakie, but not GLMakie due to differences in the backend
implementation. `surface` works in both, as it is designed to generate a
triangle mesh.

\begin{examplefigure}{svg = false}
```julia
f = Figure()
ax = PolarAxis(f[1, 1])
# The first two arguments should be set to a sensible radial and angular range
zs = [r*cos(phi) for r in range(1, 2, length=100), phi in range(0, 4pi, length=100)]
p = surface!(ax, 0..10, 0..2pi, zs, shading = false, colormap = :coolwarm, colorrange=(-2, 2))
Colorbar(f[1, 2], p)
f
```
\end{examplefigure}

## Hiding spines and decorations

For a `PolarAxis` we interpret the outer ring limitting the plotting are as the
axis spine. You can manipulate it with the `spine...` attributes.

\begin{examplefigure}{svg = true}
```julia
f = Figure(resolution = (800, 400))
ax1 = PolarAxis(f[1, 1], title = "No spine", spinevisible = false)
scatterlines!(ax1, range(0, 1, length=100), range(0, 10pi, length=100), color = 1:100)

ax2 = PolarAxis(f[1, 2], title = "Modified spine")
ax2.spinecolor[] = :red
ax2.spinestyle[] = :dash
ax2.spinewidth[] = 5
scatterlines!(ax2, range(0, 1, length=100), range(0, 10pi, length=100), color = 1:100)

f
```
\end{examplefigure}

Decorations such as grid lines and tick labels can be adjusted through
attributes in much the same way.

\begin{examplefigure}{svg = true}
```julia
f = Figure(resolution = (600, 600), backgroundcolor = :black)
ax = PolarAxis(
f[1, 1],
backgroundcolor = :black,
# r minor grid
rminorgridvisible = true, rminorgridcolor = :red,
rminorgridwidth = 1.0, rminorgridstyle = :dash,
# theta minor grid
thetaminorgridvisible = true, thetaminorgridcolor = :lightblue,
thetaminorgridwidth = 1.0, thetaminorgridstyle = :dash,
# major grid
rgridwidth = 2, rgridcolor = :red,
thetagridwidth = 2, thetagridcolor = :lightblue,
# r labels
rticklabelsize = 18, rticklabelcolor = :red,
rticklabelstrokewidth = 1.0, rticklabelstrokecolor = :white,
# theta labels
thetaticklabelsize = 18, thetaticklabelcolor = :lightblue
)

f
```
\end{examplefigure}

## Interactivity

The `PolarAxis` currently implements zooming by scrolling and allows you to
reset the view with left control + left mouse button. You can change the key
combination for resetting the view with the `reset_button` attribute, which
accepts anything `ispressed` accepts.

Note that `PolarAxis` currently does not implement the interaction itnerface
used by `Axis`.

## Other Notes

### Plotting outside a PolarAxis

Currently there is a scatter and poly plot outside the area of the `PolarAxis`
which clips the content to the relevant area. If you want to draw outside the
circle limiting the polar axis but still within it's scene area, you will need
to translate those plots to a z range between `9000` and `10_000` or disable
clipping via the `clip` attribute.
51 changes: 51 additions & 0 deletions src/layouting/transformation.jl
Expand Up @@ -414,6 +414,57 @@ function is_identity_transform(t)
end


################################################################################
### Polar Transformation
################################################################################

"""
Polar(theta_0::Float64 = 0.0, direction::Int = +1)

This struct defines a general polar-to-cartesian transformation, i.e.,
```math
(r, theta) -> (r \\cos(direction * (theta + theta_0)), r \\sin(direction * (theta + theta_0)))
```

where theta is assumed to be in radians.

`direction` should be either -1 or +1, and `theta_0` may be any value.
"""
struct Polar
theta_0::Float64
direction::Int
Polar(theta_0 = 0.0, direction = +1) = new(theta_0, direction)
end

Base.broadcastable(x::Polar) = (x,)

function apply_transform(trans::Polar, point::VecTypes{2, T}) where T <: Real
y, x = point[1] .* sincos((point[2] + trans.theta_0) * trans.direction)
return Point2{T}(x, y)
end

# Point2 may get expanded to Point3. In that case we leave z untransformed
function apply_transform(f::Polar, point::VecTypes{N2, T}) where {N2, T}
p_dim = to_ndim(Point2f, point, 0.0)
p_trans = apply_transform(f, p_dim)
if 2 < N2
p_large = ntuple(i-> i <= 2 ? p_trans[i] : point[i], N2)
return Point{N2, Float32}(p_large)
else
return to_ndim(Point{N2, Float32}, p_trans, 0.0)
end
end

function inverse_transform(trans::Polar)
return Makie.PointTrans{2}() do point
typeof(point)(
hypot(point[1], point[2]),
mod(trans.direction * atan(point[2], point[1]) - trans.theta_0, 0..2pi)
)
end
end


# this is a simplification which will only really work with non-rotated or
# scaled scene transformations, but for 2D scenes this should work well enough.
# and this way we can use the z-value as a means to shift the drawing order
Expand Down
4 changes: 3 additions & 1 deletion src/makielayout/MakieLayout.jl
Expand Up @@ -21,6 +21,7 @@ include("lineaxis.jl")
include("interactions.jl")
include("blocks/axis.jl")
include("blocks/axis3d.jl")
include("blocks/polaraxis.jl")
include("blocks/colorbar.jl")
include("blocks/label.jl")
include("blocks/slider.jl")
Expand All @@ -36,6 +37,7 @@ include("blocks/textbox.jl")

export Axis
export Axis3
export PolarAxis
export Slider
export SliderGrid
export IntervalSlider
Expand All @@ -60,7 +62,7 @@ export labelslider!, labelslidergrid!
export addmouseevents!
export interactions, register_interaction!, deregister_interaction!, activate_interaction!, deactivate_interaction!
export MouseEventTypes, MouseEvent, ScrollEvent, KeysEvent
# export hlines!, vlines!, abline!, hspan!, vspan!
export hlines!, vlines!, abline!, hspan!, vspan!
export Cycle
export Cycled

Expand Down