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

Replace radial_distortion_threshhold with directly setting radius_at_origin #3381

Merged
merged 17 commits into from
Nov 20, 2023
Merged
4 changes: 3 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# News

## master

## 0.20

- GLMakie has gained support for HiDPI (aka Retina) screens. This also enables saving images with higher resolution than screen pixel dimensions [#2544](https://github.com/MakieOrg/Makie.jl/pull/2544).
Expand All @@ -9,13 +11,13 @@
- Changed the glyph used for negative numbers in tick labels from hyphen to minus [#3379](https://github.com/MakieOrg/Makie.jl/pull/3379).
- New declarative API for AlgebraOfGraphics, Pluto and easier dashboards [#3281](https://github.com/MakieOrg/Makie.jl/pull/3281).
- WGLMakie gets faster line rendering with less updating bugs [#3062](https://github.com/MakieOrg/Makie.jl/pull/3062).
- **breaking** Replaced `PolarAxis.radial_distortion_threshold` with `PolarAxis.radius_at_origin`. [#3381](https://github.com/MakieOrg/Makie.jl/pull/3381)
- **breaking** Deprecated the `resolution` keyword in favor of `size` to reflect that this value is not a pixel resolution anymore [#3343](https://github.com/MakieOrg/Makie.jl/pull/3343).
- **breaking** Refactored the `SurfaceLike` family of traits into `VertexGrid`, `CellGrid` and `ImageLike` [#3106](https://github.com/MakieOrg/Makie.jl/pull/3106).
- **breaking** Deprecated `pixelarea(scene)` and `scene.px_area` in favor of viewport.
- **breaking** Refactoring the `Combined` Plot object and renaming it to `Plot`, improving compile times ~2x [#3082](https://github.com/MakieOrg/Makie.jl/pull/3082).
- **breaking** removed old depreactions in [#3113](https://github.com/MakieOrg/Makie.jl/pull/3113/commits/3a39210ef87a0032d78cb27c0c1019faa604effd).


## v0.19.12

- Added `cornerradius` attribute to `Box` for rounded corners [#3346](https://github.com/MakieOrg/Makie.jl/pull/3346).
Expand Down
17 changes: 17 additions & 0 deletions ReferenceTests/src/tests/figures_and_makielayout.jl
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,23 @@ end
f
end

@reference_test "PolarAxis radial shift and clip" begin
phis = range(pi/4, 9pi/4, length=201)
rs = 1.0 ./ sin.(range(pi/4, 3pi/4, length=51)[1:end-1])
rs = vcat(rs, rs, rs, rs, rs[1])

fig = Figure(size = (900, 300))
ax1 = PolarAxis(fig[1, 1], clip_r = false, radius_at_origin = -2) # red square, black, blue bulging
ax2 = PolarAxis(fig[1, 2], clip_r = false, radius_at_origin = 0) # red flower, black square, blue bulging
ax3 = PolarAxis(fig[1, 3], clip_r = false, radius_at_origin = 0.5) # red large flower, black star, blue square
for ax in (ax1, ax2, ax3)
lines!(ax, phis, rs .- 2, color = :red, linewidth = 4)
lines!(ax, phis, rs, color = :black, linewidth = 4)
lines!(ax, phis, rs .+ 0.5, color = :blue, linewidth = 4)
end
fig
end

@reference_test "Axis3 axis reversal" begin
f = Figure(size = (1000, 1000))
revstr(dir, rev) = rev ? "$dir rev" : ""
Expand Down
64 changes: 55 additions & 9 deletions docs/reference/blocks/polaraxis.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,9 @@ clipping via the `clip` attribute.

For reference, the z values used by `PolarAxis` are `po.griddepth[] = 8999` for grid lines, 9000 for the clip polygons, 9001 for spines and 9002 for tick labels.

### Radial Distortion
### Radial Offset

If you have a plot with a large rmin and rmax over a wide range of angles you will end up with a narrow PolarAxis.
If you have a plot with rlimits far away from 0 you will end up with a lot of empty space in the PolarAxis.
Consider for example:

\begin{examplefigure}{svg = true}
Expand All @@ -218,22 +218,68 @@ fig
```
\end{examplefigure}

In this case you may want to distort the r-direction to make more of your data visible.
This can be done by setting `ax.radial_distortion_threshold` to a value between 0 and 1.
In this case you may want to offset the r-direction to make more of your data visible.
This can be done by setting `ax.radius_at_origin` which translates radii as `r_out = r_in - radius_at_origin`.

\begin{examplefigure}{svg = true}
```julia
fig = Figure()
ax = PolarAxis(fig[1, 1], thetalimits = (0, pi), radial_distortion_threshold = 0.2, rlimits = (nothing, nothing))
ax = PolarAxis(fig[1, 1], thetalimits = (0, pi), radius_at_origin = 8)
lines!(ax, range(0, pi, length=100), 10 .+ sin.(0.3 .* (1:100)))
fig
```
\end{examplefigure}

Internally PolarAxis will check `rmin/rmax` against the set threshold.
If that ratio exceed the threshold, the polar transform is adjusted to shift all radii by some `r0` such that `(rmin - r0) / rmax - r0) == ax.radial_distortion_threshold`.
In effect this will hold the inner cutout/clip radius at a fraction of the outer radius.
Note that at `ax.radial_distortion_threshold >= 1.0` (default) this will never distort your data.
This can also be used to show a plot with negative radii:

\begin{examplefigure}{svg = true}
```julia
fig = Figure()
ax = PolarAxis(fig[1, 1], thetalimits = (0, pi), radius_at_origin = -12)
lines!(ax, range(0, pi, length=100), sin.(0.3 .* (1:100)) .- 10)
fig
```
\end{examplefigure}

Note however that translating radii results in some level of distortion:

\begin{examplefigure}{svg = true}
```julia
phis = range(pi/4, 9pi/4, length=201)
rs = 1.0 ./ sin.(range(pi/4, 3pi/4, length=51)[1:end-1])
rs = vcat(rs, rs, rs, rs, rs[1])

fig = Figure(size = (900, 300))
ax1 = PolarAxis(fig[1, 1], radius_at_origin = -2, title = "radius_at_origin = -2")
ax2 = PolarAxis(fig[1, 2], radius_at_origin = 0, title = "radius_at_origin = 0")
ax3 = PolarAxis(fig[1, 3], radius_at_origin = 0.5, title = "radius_at_origin = 0.5")
for ax in (ax1, ax2, ax3)
lines!(ax, phis, rs .- 2, color = :red, linewidth = 4)
lines!(ax, phis, rs, color = :black, linewidth = 4)
lines!(ax, phis, rs .+ 0.5, color = :blue, linewidth = 4)
end
fig
```
\end{examplefigure}

### Radial clipping

By default radii `r_out = r_in - radius_at_origin < 0` are clipped by the Polar transform.
This can be disabled by setting `ax.clip_r = false`.
With that setting `r_out < 0` will pass through the polar transform as is, resulting in a coordinate at $(|r_{out}|, \theta - pi)$.

\begin{examplefigure}{svg = true}
```julia
fig = Figure(size = (600, 300))
ax1 = PolarAxis(fig[1, 1], radius_at_origin = 0.0, clip_r = true, title = "clip_r = true")
ax2 = PolarAxis(fig[1, 2], radius_at_origin = 0.0, clip_r = false, title = "clip_r = false")
for ax in (ax1, ax2)
lines!(ax, 0..2pi, phi -> cos(2phi) - 0.5, color = :red, linewidth = 4)
lines!(ax, 0..2pi, phi -> sin(2phi), color = :black, linewidth = 4)
end
fig
```
\end{examplefigure}

## Attributes

Expand Down
14 changes: 14 additions & 0 deletions src/basic_recipes/triplot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,17 @@ function Makie.plot!(p::Triplot{<:Tuple{<:DelTri.Triangulation}})
strokecolor=p.strokecolor, marker=p.marker, visible=p.show_points, depth_shift=-3.0f-5)
return p
end


function data_limits(p::Triplot{<:Tuple{<:Vector{<:Point}}})
if transform_func(p) isa Polar
# Because the Polar transform is handled explicitly we cannot rely
# on the default data_limits. (data limits are pre transform)
iter = (to_ndim(Point3f, p, 0f0) for p in p.converted[1][])
limits_from_transformed_points(iter)
else
# First component is either another Voronoiplot or a poly plot. Both
# cases span the full limits of the plot
data_limits(p.plots[1])
end
end
Comment on lines +244 to +255
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Test failures come from this adjusting PolarAxis limits a little. It's the same code as we already have for voronoiplots

39 changes: 26 additions & 13 deletions src/layouting/transformation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -378,39 +378,52 @@ end
################################################################################

"""
Polar(theta_0::Float64 = 0.0, direction::Int = +1, r0::Float64 = 0)
Polar(theta_as_x = true, clip_r = true, theta_0::Float64 = 0.0, direction::Int = +1, r0::Float64 = 0)

This struct defines a general polar-to-cartesian transformation, i.e.

This struct defines a general polar-to-cartesian transformation, i.e.,
```math
(r, θ) -> ((r - r₀) ⋅ \\cos(direction ⋅ (θ + θ₀)), (r - r₀) ⋅ \\sin(direction \\cdot (θ + θ₀)))
```

where theta is assumed to be in radians.

`direction` should be either -1 or +1, `r0` should be positive and `theta_0` may be any value.

Note that for `r0 != 0` the inversion may return wrong results.
where θ is assumed to be in radians.

Controls:
- `theta_as_x = true` controls the order of incoming arguments. If true, a `Point2f`
is interpreted as `(θ, r)`, otherwise `(r, θ)`.
- `clip_r = true` controls whether negative radii are clipped. If true, `r < 0`
produces `NaN`, otherwise they simply enter in the formula above as is. Note that
the inversion only returns `r ≥ 0`
- `theta_0 = 0` offsets angles by the specified amount.
- `direction = +1` inverts the direction of θ.
- `r0 = 0` offsets radii by the specified amount. Not that this will affect the
shape of transformed objects.
"""
struct Polar
theta_as_x::Bool
clip_r::Bool
theta_0::Float64
direction::Int
r0::Float64
function Polar(theta_as_x = true, theta_0 = 0.0, direction = +1, r0 = 0)
return new(theta_as_x, theta_0, direction, r0)

function Polar(theta_0::Real = 0.0, direction::Int = +1, r0::Real = 0, theta_as_x::Bool = true, clip_r::Bool = true)
return new(theta_as_x, clip_r, theta_0, direction, r0)
end
end

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

function apply_transform(trans::Polar, point::VecTypes{2, T}) where T <: Real
if trans.theta_as_x
r = max(0.0, point[2] - trans.r0)
θ = trans.direction * (point[1] + trans.theta_0)
θ, r = point
else
r = max(0.0, point[1] - trans.r0)
θ = trans.direction * (point[2] + trans.theta_0)
r, θ = point
end
r = r - trans.r0
if trans.clip_r && (r < 0.0)
return Point2{T}(NaN)
end
θ = trans.direction * (θ + trans.theta_0)
y, x = r .* sincos(θ)
return Point2{T}(x, y)
end
Expand Down
Loading
Loading