diff --git a/NEWS.md b/NEWS.md index 66954b47988..22671388924 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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). @@ -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). diff --git a/ReferenceTests/src/tests/figures_and_makielayout.jl b/ReferenceTests/src/tests/figures_and_makielayout.jl index 787ede722cb..6c6f46a2013 100644 --- a/ReferenceTests/src/tests/figures_and_makielayout.jl +++ b/ReferenceTests/src/tests/figures_and_makielayout.jl @@ -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" : "" diff --git a/docs/reference/blocks/polaraxis.md b/docs/reference/blocks/polaraxis.md index eede5ff6422..426db39665c 100644 --- a/docs/reference/blocks/polaraxis.md +++ b/docs/reference/blocks/polaraxis.md @@ -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} @@ -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 diff --git a/src/basic_recipes/triplot.jl b/src/basic_recipes/triplot.jl index d2ccc94ffc6..917e3ff4265 100644 --- a/src/basic_recipes/triplot.jl +++ b/src/basic_recipes/triplot.jl @@ -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 \ No newline at end of file diff --git a/src/layouting/transformation.jl b/src/layouting/transformation.jl index 840740f8a80..bde0e8b3340 100644 --- a/src/layouting/transformation.jl +++ b/src/layouting/transformation.jl @@ -378,26 +378,36 @@ 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 @@ -405,12 +415,15 @@ 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 diff --git a/src/makielayout/blocks/polaraxis.jl b/src/makielayout/blocks/polaraxis.jl index dc2d7aa240d..40dbcb9db5f 100644 --- a/src/makielayout/blocks/polaraxis.jl +++ b/src/makielayout/blocks/polaraxis.jl @@ -26,19 +26,19 @@ function initialize_block!(po::PolarAxis; palette=nothing) end # Setup camera/limits and Polar transform - usable_fraction, radius_at_origin = setup_camera_matrices!(po) + usable_fraction = setup_camera_matrices!(po) Observables.connect!( po.scene.transformation.transform_func, - @lift(Polar($(po.theta_as_x), $(po.target_theta_0), $(po.direction), $(radius_at_origin))) + @lift(Polar($(po.target_theta_0), $(po.direction), $(po.target_r0), $(po.theta_as_x), $(po.clip_r))) ) Observables.connect!( po.overlay.transformation.transform_func, - @lift(Polar(false, $(po.target_theta_0), $(po.direction))) + @lift(Polar($(po.target_theta_0), $(po.direction), 0.0, false)) ) # Draw clip, grid lines, spine, ticks - rticklabelplot, thetaticklabelplot = draw_axis!(po, radius_at_origin) + rticklabelplot, thetaticklabelplot = draw_axis!(po) # Calculate fraction of screen usable after reserving space for theta ticks # TODO: Should we include rticks here? @@ -213,21 +213,17 @@ function setup_camera_matrices!(po::PolarAxis) setfield!(po, :target_rlims, Observable{Tuple{Float64, Float64}}((0.0, 10.0))) setfield!(po, :target_thetalims, Observable{Tuple{Float64, Float64}}((0.0, 2pi))) setfield!(po, :target_theta_0, map(identity, po.theta_0)) + setfield!(po, :target_r0, Observable{Float32}(po.radius_at_origin[] isa Real ? po.radius_at_origin[] : 0f0)) reset_limits!(po) onany((_, _) -> reset_limits!(po), po.blockscene, po.rlimits, po.thetalimits) - # To keep the inner clip radius below a certain fraction of the outer clip - # radius we map all r > r0 to 0. This computes that r0. - radius_at_origin = map(po.blockscene, po.target_rlims, po.radial_distortion_threshold) do (rmin, rmax), max_fraction - # max_fraction = (rmin - r0) / (rmax - r0) solved for r0 - return max(0.0, (rmin - max_fraction * rmax) / (1 - max_fraction)) - end - # get cartesian bbox defined by axis limits - # OPT: target_radius update triggers radius_at_origin update - data_bbox = map(po.blockscene, po.target_thetalims, radius_at_origin, po.direction, po.target_theta_0) do tlims, ro, dir, t0 - return polaraxis_bbox(po.target_rlims[], tlims, ro, dir, t0) - end + data_bbox = map( + polaraxis_bbox, + po.blockscene, + po.target_rlims, po.target_thetalims, + po.target_r0, po.direction, po.target_theta_0 + ) # fit data_bbox into the usable area of PolarAxis (i.e. with tick space subtracted) onany(po.blockscene, usable_fraction, data_bbox) do usable_fraction, bb @@ -239,9 +235,10 @@ function setup_camera_matrices!(po::PolarAxis) end # same as above, but with rmax scaled to 1 + # OPT: data_bbox triggers on target_r0, target_rlims updates onany(po.blockscene, usable_fraction, data_bbox) do usable_fraction, bb mini = minimum(bb); ws = widths(bb) - rmax = po.target_rlims[][2] - radius_at_origin[] # both update data_bbox + rmax = po.target_rlims[][2] - po.target_r0[] scale = minimum(2usable_fraction ./ ws) trans = to_ndim(Vec3f, -scale .* (mini .+ 0.5ws), 0) scale *= rmax @@ -460,12 +457,16 @@ function setup_camera_matrices!(po::PolarAxis) return Consume(false) end - return usable_fraction, radius_at_origin + return usable_fraction end function reset_limits!(po::PolarAxis) + # Resolve automatic as origin + rmin_to_origin = po.rlimits[][1] === :origin + rlimits = ifelse.(rmin_to_origin, nothing, po.rlimits[]) + # at least one derived limit - if any(isnothing, po.rlimits[]) || any(isnothing, po.thetalimits[]) + if any(isnothing, rlimits) || any(isnothing, po.thetalimits[]) if !isempty(po.scene.plots) # TODO: Why does this include child scenes by default? @@ -480,13 +481,28 @@ function reset_limits!(po::PolarAxis) rmax, thetamax = maximum(lims2d) end - # cleanup autolimits (0 width, negative rmin) + # Determine automatic target_r0 + if po.radius_at_origin[] isa Real + po.target_r0[] = po.radius_at_origin[] + else + po.target_r0[] = min(0.0, rmin) + end + + # cleanup autolimits (0 width, rmin ≥ target_r0) if rmin == rmax - rmin = max(0.0, rmin - 5.0) + if rmin_to_origin + rmin = po.target_r0[] + else + rmin = max(po.target_r0[], rmin - 5.0) + end rmax = rmin + 10.0 else dr = rmax - rmin - rmin = max(0.0, rmin - po.rautolimitmargin[][1] * dr) + if rmin_to_origin + rmin = po.target_r0[] + else + rmin = max(po.target_r0[], rmin - po.rautolimitmargin[][1] * dr) + end rmax += po.rautolimitmargin[][2] * dr end @@ -506,11 +522,11 @@ function reset_limits!(po::PolarAxis) end # apply - po.target_rlims[] = ifelse.(isnothing.(po.rlimits[]), (rmin, rmax), po.rlimits[]) + po.target_rlims[] = ifelse.(isnothing.(rlimits), (rmin, rmax), rlimits) po.target_thetalims[] = ifelse.(isnothing.(po.thetalimits[]), (thetamin, thetamax), po.thetalimits[]) else # all limits set - if po.target_rlims[] != po.rlimits[] - po.target_rlims[] = po.rlimits[] + if po.target_rlims[] != rlimits + po.target_rlims[] = rlimits end if po.target_thetalims[] != po.thetalimits[] po.target_thetalims[] = po.thetalimits[] @@ -537,7 +553,7 @@ function _polar_clip_polygon( return [Polygon(exterior, [interior])] end -function draw_axis!(po::PolarAxis, radius_at_origin) +function draw_axis!(po::PolarAxis) rtick_pos_lbl = Observable{Vector{<:Tuple{AbstractString, Point2f}}}() rtick_align = Observable{Point2f}() rtick_offset = Observable{Point2f}() @@ -557,19 +573,17 @@ function draw_axis!(po::PolarAxis, radius_at_origin) end end - # OPT: target_radius update triggers radius_at_origin update onany( po.blockscene, po.rticks, po.rminorticks, po.rtickformat, po.rtickangle, - po.direction, po.target_thetalims, po.sample_density, radius_at_origin + po.direction, po.target_rlims, po.target_thetalims, po.sample_density, po.target_r0 ) do rticks, rminorticks, rtickformat, rtickangle, - dir, thetalims, sample_density, radius_at_origin + dir, rlims, thetalims, sample_density, target_r0 # For text: - rlims = po.target_rlims[] - rmaxinv = 1.0 / (rlims[2] - radius_at_origin) + rmaxinv = 1.0 / (rlims[2] - target_r0) _rtickvalues, _rticklabels = get_ticks(rticks, identity, rtickformat, rlims...) - _rtickradius = (_rtickvalues .- radius_at_origin) .* rmaxinv + _rtickradius = (_rtickvalues .- target_r0) .* rmaxinv _rtickangle = default_rtickangle(rtickangle, dir, thetalims) rtick_pos_lbl[] = tuple.(_rticklabels, Point2f.(_rtickradius, _rtickangle)) @@ -578,7 +592,7 @@ function draw_axis!(po::PolarAxis, radius_at_origin) rgridpoints[] = GeometryBasics.LineString.([Point2f.(r, thetas) for r in _rtickradius]) _rminortickvalues = get_minor_tickvalues(rminorticks, identity, _rtickvalues, rlims...) - _rminortickvalues .= (_rminortickvalues .- radius_at_origin) .* rmaxinv + _rminortickvalues .= (_rminortickvalues .- target_r0) .* rmaxinv rminorgridpoints[] = GeometryBasics.LineString.([Point2f.(r, thetas) for r in _rminortickvalues]) return @@ -625,8 +639,8 @@ function draw_axis!(po::PolarAxis, radius_at_origin) onany( po.blockscene, po.thetaticks, po.thetaminorticks, po.thetatickformat, po.thetaticklabelpad, - po.direction, po.target_theta_0, po.target_rlims, po.target_thetalims, po.radial_distortion_threshold - ) do thetaticks, thetaminorticks, thetatickformat, px_pad, dir, theta_0, rlims, thetalims, max_clip + po.direction, po.target_theta_0, po.target_rlims, po.target_thetalims, po.target_r0 + ) do thetaticks, thetaminorticks, thetatickformat, px_pad, dir, theta_0, rlims, thetalims, r0 _thetatickvalues, _thetaticklabels = get_ticks(thetaticks, identity, thetatickformat, thetalims...) @@ -652,7 +666,7 @@ function draw_axis!(po::PolarAxis, radius_at_origin) thetatick_pos_lbl[] = tuple.(_thetaticklabels, Point2f.(1, _thetatickvalues)) # Grid lines - rmin = min(rlims[1] / rlims[2], max_clip) + rmin = (rlims[1] - r0) / (rlims[2] - r0) thetagridpoints[] = [Point2f(r, theta) for theta in _thetatickvalues for r in (rmin, 1)] _thetaminortickvalues = get_minor_tickvalues(thetaminorticks, identity, _thetatickvalues, thetalims...) @@ -795,19 +809,19 @@ function draw_axis!(po::PolarAxis, radius_at_origin) rotate!.((outer_clip_plot, inner_clip_plot), (Vec3f(0,0,1),), angle) end - onany(po.blockscene, po.target_rlims, po.radial_distortion_threshold) do lims, maxclip - s = min(lims[1] / lims[2], maxclip) + onany(po.blockscene, po.target_rlims, po.target_r0) do lims, r0 + s = (lims[1] - r0) / (lims[2] - r0) scale!(inner_clip_plot, Vec3f(s, s, 1)) end - notify(po.radial_distortion_threshold) + notify(po.target_r0) # spine traces circle sector - inner circle spine_points = map(po.blockscene, - po.target_rlims, po.target_thetalims, po.radial_distortion_threshold, po.sample_density - ) do (rmin, rmax), thetalims, max_clip, N + po.target_rlims, po.target_thetalims, po.target_r0, po.sample_density + ) do (rmin, rmax), thetalims, r0, N thetamin, thetamax = thetalims - rmin = min(rmin/rmax, max_clip) + rmin = (rmin - r0) / (rmax - r0) rmax = 1.0 # make sure we have 2+ points per arc @@ -906,9 +920,9 @@ end Sets the radial limits of a given `PolarAxis`. """ -rlims!(po::PolarAxis, r::Union{Nothing, Real}) = rlims!(po, po.rlimits[][1], r) +rlims!(po::PolarAxis, r::Union{Symbol, Nothing, Real}) = rlims!(po, po.rlimits[][1], r) -function rlims!(po::PolarAxis, rmin::Union{Nothing, Real}, rmax::Union{Nothing, Real}) +function rlims!(po::PolarAxis, rmin::Union{Symbol, Nothing, Real}, rmax::Union{Nothing, Real}) po.rlimits[] = (rmin, rmax) return end diff --git a/src/makielayout/types.jl b/src/makielayout/types.jl index f9a71ebd212..827026ec5f4 100644 --- a/src/makielayout/types.jl +++ b/src/makielayout/types.jl @@ -1636,6 +1636,7 @@ end target_rlims::Observable{Tuple{Float64, Float64}} target_thetalims::Observable{Tuple{Float64, Float64}} target_theta_0::Observable{Float32} + target_r0::Observable{Float32} @attributes begin # Generic @@ -1664,21 +1665,23 @@ end clip::Bool = true "Sets the color of the clip polygon. Mainly for debug purposes." clipcolor = automatic - "Sets a threshold relative to `rmin/rmax` after which radii are distorted to fit more on the screen. No distortion is applied if `radial_distortion_threshold ≥ 1`" - radial_distortion_threshold::Float64 = 1.0 # Limits & transformation settings - "The radial limits of the PolarAxis." - rlimits = (0.0, nothing) + "The radial limits of the PolarAxis. " + rlimits = (:origin, nothing) "The angle limits of the PolarAxis. (0.0, 2pi) results a full circle. (nothing, nothing) results in limits picked based on plot limits." thetalimits = (0.0, 2pi) "The direction of rotation. Can be -1 (clockwise) or 1 (counterclockwise)." direction::Int = 1 "The angular offset for (1, 0) in the PolarAxis. This rotates the axis." theta_0::Float32 = 0f0 + "Sets the radius at the origin of the PolarAxis such that `r_out = r_in - radius_at_origin`. Can be set to `automatic` to match rmin. Note that this will affect the shape of plotted objects." + radius_at_origin = automatic "Controls the argument order of the Polar transform. If `theta_as_x = true` it is (θ, r), otherwise (r, θ)." theta_as_x::Bool = true + "Controls whether `r < 0` (after applying `radius_at_origin`) gets clipped (true) or not (false)." + clip_r::Bool = true "The relative margins added to the autolimits in r direction." rautolimitmargin::Tuple{Float64, Float64} = (0.05, 0.05) "The relative margins added to the autolimits in theta direction." diff --git a/test/PolarAxis.jl b/test/PolarAxis.jl index 1866f439743..8029e1835f1 100644 --- a/test/PolarAxis.jl +++ b/test/PolarAxis.jl @@ -68,7 +68,7 @@ @test ax.thetaautolimitmargin[] == (0.05, 0.05) # default should have mostly set default limits - @test ax.rlimits[] == (0.0, nothing) + @test ax.rlimits[] == (:origin, nothing) @test ax.thetalimits[] == (0.0, 2pi) @test ax.target_rlims[] == (0.0, 10.0) @test ax.target_thetalims[] == (0.0, 2pi) @@ -130,7 +130,7 @@ # with default limits reset_limits!(ax) - @test ax.rlimits[] == (0.0, nothing) + @test ax.rlimits[] == (:origin, nothing) @test ax.thetalimits[] == (0.0, 2pi) @test ax.target_rlims[] == (0.0, 5.0) @test ax.target_thetalims[] == (0.0, 2pi) @@ -144,17 +144,9 @@ @test all(isapprox.(ax.target_thetalims[], (0.5pi, 1.0pi), rtol=1e-6)) end - @testset "Radial Distortion" begin + @testset "Radial Offset" begin fig = Figure() - ax = PolarAxis(fig[1, 1], radial_distortion_threshold = 0.2, rlimits = (0, 10)) - tf = ax.scene.transformation.transform_func - @test /(ax.target_rlims[]...) == 0.0 - @test /((ax.target_rlims[] .- tf[].r0)...) == 0.0 - rlims!(ax, 1, 10) - @test /(ax.target_rlims[]...) == 0.1 - @test /((ax.target_rlims[] .- tf[].r0)...) == 0.1 - rlims!(ax, 5, 10) - @test /(ax.target_rlims[]...) == 0.5 - @test /((ax.target_rlims[] .- tf[].r0)...) ≈ 0.2 + ax = PolarAxis(fig[1, 1], radius_at_origin = -1.0, rlimits = (0, 10)) + @test ax.scene.transformation.transform_func[].r0 == -1.0 end end \ No newline at end of file diff --git a/test/transformations.jl b/test/transformations.jl index 6033d5f3381..ff3366a9737 100644 --- a/test/transformations.jl +++ b/test/transformations.jl @@ -88,39 +88,49 @@ end end @testset "Polar Transform" begin - tf = Makie.Polar(false) - @test tf.theta_as_x == false + tf = Makie.Polar() + @test tf.theta_as_x == true + @test tf.clip_r == true @test tf.theta_0 == 0.0 @test tf.direction == 1 @test tf.r0 == 0.0 - input = Point2f.(1:6, [0, pi/3, pi/2, pi, 2pi, 3pi]) - output = [r * Point2f(cos(phi), sin(phi)) for (r, phi) in input] - inv = Point2f.(1:6, mod.([0, pi/3, pi/2, pi, 2pi, 3pi], (0..2pi,))) + input = Point2f.([0, pi/3, pi/2, pi, 2pi, 3pi], 1:6) + output = [r * Point2f(cos(phi), sin(phi)) for (phi, r) in input] + inv = Point2f.(mod.([0, pi/3, pi/2, pi, 2pi, 3pi], (0..2pi,)), 1:6) @test apply_transform(tf, input) ≈ output @test apply_transform(Makie.inverse_transform(tf), output) ≈ inv - tf = Makie.Polar(false, pi/2) + tf = Makie.Polar(pi/2, 1, 0, false) + input = Point2f.(1:6, [0, pi/3, pi/2, pi, 2pi, 3pi]) output = [r * Point2f(cos(phi+pi/2), sin(phi+pi/2)) for (r, phi) in input] + inv = Point2f.(1:6, mod.([0, pi/3, pi/2, pi, 2pi, 3pi], (0..2pi,))) @test apply_transform(tf, input) ≈ output @test apply_transform(Makie.inverse_transform(tf), output) ≈ inv - tf = Makie.Polar(false, pi/2, -1) + tf = Makie.Polar(pi/2, -1, 0, false) output = [r * Point2f(cos(-phi-pi/2), sin(-phi-pi/2)) for (r, phi) in input] @test apply_transform(tf, input) ≈ output @test apply_transform(Makie.inverse_transform(tf), output) ≈ inv - tf = Makie.Polar(false, pi/2, -1, 0.5) + tf = Makie.Polar(pi/2, -1, 0.5, false) output = [(r - 0.5) * Point2f(cos(-phi-pi/2), sin(-phi-pi/2)) for (r, phi) in input] @test apply_transform(tf, input) ≈ output @test apply_transform(Makie.inverse_transform(tf), output) ≈ inv - tf = Makie.Polar(true) + tf = Makie.Polar(0, 1, 0, true) input = Point2f.([0, pi/3, pi/2, pi, 2pi, 3pi], 1:6) output = [r * Point2f(cos(phi), sin(phi)) for (phi, r) in input] inv = Point2f.(mod.([0, pi/3, pi/2, pi, 2pi, 3pi], (0..2pi,)), 1:6) @test apply_transform(tf, input) ≈ output @test apply_transform(Makie.inverse_transform(tf), output) ≈ inv + + tf = Makie.Polar(0, 1, 0, true, false) + input = Point2f.([0, pi/3, pi/2, pi, 2pi, 3pi], -6:-1) + output = [r * Point2f(cos(phi), sin(phi)) for (phi, r) in input] + inv = Point2f.(mod.([0, pi/3, pi/2, pi, 2pi, 3pi] .+ pi, (0..2pi,)), 6:-1:1) + @test apply_transform(tf, input) ≈ output + @test apply_transform(Makie.inverse_transform(tf), output) ≈ inv end @testset "Coordinate Systems" begin