From cac175b4b7038d3c7d83b71b48d2391ee52d4065 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 25 Jul 2023 15:07:01 +0200 Subject: [PATCH 01/18] add `ReversibleScale` type - simplify `LogFunctions` --- src/colorsampler.jl | 4 ++-- src/layouting/transformation.jl | 8 ++++---- src/makielayout/blocks/axis.jl | 2 +- src/makielayout/lineaxis.jl | 23 ++++++++++++----------- src/types.jl | 15 +++++++++++++++ 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/colorsampler.jl b/src/colorsampler.jl index e0c728b6d8a..c8c36900bbb 100644 --- a/src/colorsampler.jl +++ b/src/colorsampler.jl @@ -179,7 +179,7 @@ end struct ColorMap{N,T<:AbstractArray{<:Number,N},T2<:AbstractArray{<:Number,N}} color::Observable{T} colormap::Observable{Vector{RGBAf}} - scale::Observable{Function} + scale::Observable{Union{ReversibleScale,Function}} mapping::Observable{Union{Nothing, Vector{Float64}}} colorrange::Observable{Vec{2,Float64}} @@ -198,7 +198,7 @@ function assemble_colors(::T, @nospecialize(color), @nospecialize(plot)) where { color_tight = convert(Observable{T}, color) colormap = Observable(RGBAf[]; ignore_equal_values=true) categorical = Observable(false) - colorscale = convert(Observable{Function}, plot.colorscale) + colorscale = convert(Observable, plot.colorscale) mapping = Observable{Union{Nothing, Vector{Float64}}}(nothing) function update_colors(cmap, a) diff --git a/src/layouting/transformation.jl b/src/layouting/transformation.jl index 7ab943e16f9..3ed36c6dd7c 100644 --- a/src/layouting/transformation.jl +++ b/src/layouting/transformation.jl @@ -400,12 +400,11 @@ end const REVERSIBLE_SCALES = Union{ # typeof(identity), # no, this is a noop - typeof(log10), - typeof(log), - typeof(log2), - typeof(sqrt), + LogFunctions, typeof(pseudolog10), typeof(logit), + typeof(sqrt), + ReversibleScale, Symlog10, } @@ -417,6 +416,7 @@ inverse_transform(::typeof(sqrt)) = x -> x ^ 2 inverse_transform(::typeof(pseudolog10)) = inv_pseudolog10 inverse_transform(F::Tuple) = map(inverse_transform, F) inverse_transform(::typeof(logit)) = logistic +inverse_transform(s::ReversibleScale) = s.backward inverse_transform(s::Symlog10) = x -> inv_symlog10(x, s.low, s.high) inverse_transform(s) = nothing diff --git a/src/makielayout/blocks/axis.jl b/src/makielayout/blocks/axis.jl index 8656998749a..cd1f3e2ad83 100644 --- a/src/makielayout/blocks/axis.jl +++ b/src/makielayout/blocks/axis.jl @@ -1360,7 +1360,7 @@ defaultlimits(::typeof(Makie.pseudolog10)) = (0.0, 100.0) defaultlimits(::Makie.Symlog10) = (0.0, 100.0) defined_interval(::typeof(identity)) = OpenInterval(-Inf, Inf) -defined_interval(::Union{typeof(log2), typeof(log10), typeof(log)}) = OpenInterval(0.0, Inf) +defined_interval(::LogFunctions) = OpenInterval(0.0, Inf) defined_interval(::typeof(sqrt)) = Interval{:closed,:open}(0, Inf) defined_interval(::typeof(Makie.logit)) = OpenInterval(0.0, 1.0) defined_interval(::typeof(Makie.pseudolog10)) = OpenInterval(-Inf, Inf) diff --git a/src/makielayout/lineaxis.jl b/src/makielayout/lineaxis.jl index d8dbcd3b6a9..8edbb27394b 100644 --- a/src/makielayout/lineaxis.jl +++ b/src/makielayout/lineaxis.jl @@ -556,12 +556,12 @@ end get_tickvalues(::Automatic, ::typeof(identity), vmin, vmax) = get_tickvalues(WilkinsonTicks(5, k_min = 3), vmin, vmax) # fall back to identity if not overloaded scale function is used with automatic -get_tickvalues(::Automatic, F, vmin, vmax) = get_tickvalues(automatic, identity, vmin, vmax) +get_tickvalues(::Automatic, _, vmin, vmax) = get_tickvalues(automatic, identity, vmin, vmax) # fall back to non-scale aware behavior if no special version is overloaded -get_tickvalues(ticks, scale, vmin, vmax) = get_tickvalues(ticks, vmin, vmax) +get_tickvalues(ticks, _, vmin, vmax) = get_tickvalues(ticks, vmin, vmax) -function get_ticks(ticks_and_labels::Tuple{Any, Any}, any_scale, ::Automatic, vmin, vmax) +function get_ticks(ticks_and_labels::Tuple{Any, Any}, _, ::Automatic, vmin, vmax) n1 = length(ticks_and_labels[1]) n2 = length(ticks_and_labels[2]) if n1 != n2 @@ -570,7 +570,7 @@ function get_ticks(ticks_and_labels::Tuple{Any, Any}, any_scale, ::Automatic, vm ticks_and_labels end -function get_ticks(tickfunction::Function, any_scale, formatter, vmin, vmax) +function get_ticks(tickfunction::Function, _, formatter, vmin, vmax) result = tickfunction(vmin, vmax) if result isa Tuple{Any, Any} tickvalues, ticklabels = result @@ -581,18 +581,20 @@ function get_ticks(tickfunction::Function, any_scale, formatter, vmin, vmax) return tickvalues, ticklabels end +_logbase(s::ReversibleScale) = s.logbase _logbase(::typeof(log10)) = "10" _logbase(::typeof(log2)) = "2" _logbase(::typeof(log)) = "e" +_logbase(::Any) = "" - -function get_ticks(::Automatic, scale::Union{typeof(log10), typeof(log2), typeof(log)}, - any_formatter, vmin, vmax) - get_ticks(LogTicks(WilkinsonTicks(5, k_min = 3)), scale, any_formatter, vmin, vmax) +function get_ticks(::Automatic, scale::Union{LogFunctions,ReversibleScale}, any_formatter, vmin, vmax) + wt = WilkinsonTicks(5, k_min = 3) + ticks = isempty(_logbase(scale)) ? wt : LogTicks(wt) + get_ticks(ticks, scale, any_formatter, vmin, vmax) end # log ticks just use the normal pipeline but with log'd limits, then transform the labels -function get_ticks(l::LogTicks, scale::Union{typeof(log10), typeof(log2), typeof(log)}, ::Automatic, vmin, vmax) +function get_ticks(l::LogTicks, scale::Union{LogFunctions,ReversibleScale}, ::Automatic, vmin, vmax) ticks_scaled = get_tickvalues(l.linear_ticks, identity, scale(vmin), scale(vmax)) ticks = Makie.inverse_transform(scale).(ticks_scaled) @@ -727,8 +729,7 @@ function get_minor_tickvalues(i::IntervalsBetween, scale, tickvalues, vmin, vmax end # for log scales, we need to step in log steps at the edges -function get_minor_tickvalues(i::IntervalsBetween, scale::Union{typeof(log),typeof(log2),typeof(log10)}, - tickvalues, vmin, vmax) +function get_minor_tickvalues(i::IntervalsBetween, scale::LogFunctions, tickvalues, vmin, vmax) vals = Float64[] length(tickvalues) < 2 && return vals n = i.n diff --git a/src/types.jl b/src/types.jl index c28e74df6a8..b562fd1ca3d 100644 --- a/src/types.jl +++ b/src/types.jl @@ -378,3 +378,18 @@ end # The color type we ideally use for most color attributes const RGBColors = Union{RGBAf, Vector{RGBAf}, Vector{Float32}} + +const LogFunctions = Union{typeof(log10), typeof(log2), typeof(log)} + +struct ReversibleScale{F <: Function, B <: Function} + forward::F + backward::B + logbase::String + function ReversibleScale(forward, backward, logbase = "") + new{typeof(forward),typeof(backward)}(forward, backward, logbase) + end +end + +function (s::ReversibleScale)(args...) # functor + s.forward(args...) +end From f26d8163e0eef1860db2c4d6d2dd1fbcd5dc118f Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 25 Jul 2023 18:35:25 +0200 Subject: [PATCH 02/18] fix `scaled_steps` --- ReferenceTests/src/tests/examples2d.jl | 9 +++++++++ src/makielayout/blocks/axis.jl | 8 ++++---- src/makielayout/blocks/colorbar.jl | 23 ++++++++++++----------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/ReferenceTests/src/tests/examples2d.jl b/ReferenceTests/src/tests/examples2d.jl index f79c4416ae7..0256f0d3e75 100644 --- a/ReferenceTests/src/tests/examples2d.jl +++ b/ReferenceTests/src/tests/examples2d.jl @@ -597,6 +597,15 @@ end fig end +@reference_test "custom colorscale (heatmap)" begin + x = 10.0.^(1:0.1:4) + y = 1.0:0.1:5.0 + scale = Makie.ReversibleScale(x -> log10(x + 2), x -> exp10(x) - 2, "10") + fig, ax, hm = heatmap(x, y, (x, y) -> x; axis = (; xscale = scale), colorscale = scale) + Colorbar(fig[1, 2], hm) + fig +end + @reference_test "colorscale (lines)" begin xs = 0:0.01:10 ys = 2 .* (1 .+ sin.(xs)) diff --git a/src/makielayout/blocks/axis.jl b/src/makielayout/blocks/axis.jl index cd1f3e2ad83..bc21a0c1502 100644 --- a/src/makielayout/blocks/axis.jl +++ b/src/makielayout/blocks/axis.jl @@ -1349,10 +1349,9 @@ defaultlimits(limits::Tuple{Real, Nothing}, scale) = (limits[1], defaultlimits(s defaultlimits(limits::Tuple{Nothing, Real}, scale) = (defaultlimits(scale)[1], limits[2]) defaultlimits(limits::Tuple{Nothing, Nothing}, scale) = defaultlimits(scale) - -defaultlimits(::typeof(log10)) = (1.0, 1000.0) -defaultlimits(::typeof(log2)) = (1.0, 8.0) -defaultlimits(::typeof(log)) = (1.0, exp(3.0)) +defaultlimits(scale::Union{LogFunctions,ReversibleScale}) = let inv_scale = inverse_transform(scale) + (inv_scale(0.0), inv_scale(3.0)) +end defaultlimits(::typeof(identity)) = (0.0, 10.0) defaultlimits(::typeof(sqrt)) = (0.0, 100.0) defaultlimits(::typeof(Makie.logit)) = (0.01, 0.99) @@ -1360,6 +1359,7 @@ defaultlimits(::typeof(Makie.pseudolog10)) = (0.0, 100.0) defaultlimits(::Makie.Symlog10) = (0.0, 100.0) defined_interval(::typeof(identity)) = OpenInterval(-Inf, Inf) +defined_interval(::ReversibleScale) = OpenInterval(-Inf, Inf) defined_interval(::LogFunctions) = OpenInterval(0.0, Inf) defined_interval(::typeof(sqrt)) = Interval{:closed,:open}(0, Inf) defined_interval(::typeof(Makie.logit)) = OpenInterval(0.0, 1.0) diff --git a/src/makielayout/blocks/colorbar.jl b/src/makielayout/blocks/colorbar.jl index 6ccbc928238..f8fc60d1788 100644 --- a/src/makielayout/blocks/colorbar.jl +++ b/src/makielayout/blocks/colorbar.jl @@ -68,7 +68,12 @@ function Colorbar(fig_or_scene, contourf::Union{Contourf, Tricontourf}; kwargs.. end -colorbar_range(start, stop, length, _) = LinRange(start, stop, length) # noop +colorbar_range(start, stop, length, _::typeof(identity)) = LinRange(start, stop, length) +function colorbar_range(start, stop, length, _) + @warn "cannot determine inverse_transform" + colorbar_range(start, stop, length, identity) # noop (nothing we can do, bailing out) +end + function colorbar_range(start, stop, length, scale::REVERSIBLE_SCALES) inverse_transform(scale).(range(start, stop; length)) end @@ -146,7 +151,7 @@ function initialize_block!(cb::Colorbar) map_is_categorical = lift(x -> x isa PlotUtils.CategoricalColorGradient, blockscene, cgradient) steps = lift(blockscene, cgradient, cb.nsteps, cb.scale) do cgradient, n, scale - s = if cgradient isa PlotUtils.CategoricalColorGradient + if cgradient isa PlotUtils.CategoricalColorGradient cgradient.values else collect(colorbar_range(0, 1, n, scale)) @@ -363,17 +368,13 @@ end Sets the space allocated for the ticklabels of the `Colorbar` to the minimum that is needed and returns that value. """ -function tight_ticklabel_spacing!(cb::Colorbar) - space = tight_ticklabel_spacing!(cb.axis) - return space -end +tight_ticklabel_spacing!(cb::Colorbar) = tight_ticklabel_spacing!(cb.axis) function scaled_steps(steps, scale, lims) - # first scale to limits so we can actually apply the scale to the values - # (log(0) doesn't work etc.) - s_limits = steps .* (lims[2] - lims[1]) .+ lims[1] # scale with scaling function - s_limits_scaled = scale.(s_limits) + steps_scaled = scale.(steps) + # normalize to lims range + steps_lim_scaled = @. steps_scaled * (scale(lims[2]) - scale(lims[1])) + scale(lims[1]) # then rescale to 0 to 1 - s_scaled = (s_limits_scaled .- s_limits_scaled[1]) ./ (s_limits_scaled[end] - s_limits_scaled[1]) + @. (steps_lim_scaled - steps_lim_scaled[begin]) / (steps_lim_scaled[end] - steps_lim_scaled[begin]) end From bed582386068024ad8150dcc3dcb84a5db04484f Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 25 Jul 2023 19:45:42 +0200 Subject: [PATCH 03/18] add news entry --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index ba55a509260..04973beed26 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,7 @@ ## master +- Allow arbitrary reversible scale functions through `Makie.ReversibleScale`. - Fix automatic normal generation for meshes with shading and no normals [#3041](https://github.com/MakieOrg/Makie.jl/pull/3041). ## v0.19.7 From 56d597ac8d1f77a772ea6a4e942c199ccdb0a1e2 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 8 Aug 2023 13:02:22 +0200 Subject: [PATCH 04/18] add more flexibility --- ReferenceTests/src/tests/examples2d.jl | 2 +- src/makielayout/blocks/axis.jl | 5 +++-- src/types.jl | 8 +++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ReferenceTests/src/tests/examples2d.jl b/ReferenceTests/src/tests/examples2d.jl index 0256f0d3e75..cb845e8f889 100644 --- a/ReferenceTests/src/tests/examples2d.jl +++ b/ReferenceTests/src/tests/examples2d.jl @@ -600,7 +600,7 @@ end @reference_test "custom colorscale (heatmap)" begin x = 10.0.^(1:0.1:4) y = 1.0:0.1:5.0 - scale = Makie.ReversibleScale(x -> log10(x + 2), x -> exp10(x) - 2, "10") + scale = Makie.ReversibleScale(x -> log10(x + 2), x -> exp10(x) - 2; logbase = "10") fig, ax, hm = heatmap(x, y, (x, y) -> x; axis = (; xscale = scale), colorscale = scale) Colorbar(fig[1, 2], hm) fig diff --git a/src/makielayout/blocks/axis.jl b/src/makielayout/blocks/axis.jl index bc21a0c1502..b4b120bce6d 100644 --- a/src/makielayout/blocks/axis.jl +++ b/src/makielayout/blocks/axis.jl @@ -1349,7 +1349,8 @@ defaultlimits(limits::Tuple{Real, Nothing}, scale) = (limits[1], defaultlimits(s defaultlimits(limits::Tuple{Nothing, Real}, scale) = (defaultlimits(scale)[1], limits[2]) defaultlimits(limits::Tuple{Nothing, Nothing}, scale) = defaultlimits(scale) -defaultlimits(scale::Union{LogFunctions,ReversibleScale}) = let inv_scale = inverse_transform(scale) +defaultlimits(scale::ReversibleScale) = inverse_transform(scale).(scale.limits) +defaultlimits(scale::LogFunctions) = let inv_scale = inverse_transform(scale) (inv_scale(0.0), inv_scale(3.0)) end defaultlimits(::typeof(identity)) = (0.0, 10.0) @@ -1358,8 +1359,8 @@ defaultlimits(::typeof(Makie.logit)) = (0.01, 0.99) defaultlimits(::typeof(Makie.pseudolog10)) = (0.0, 100.0) defaultlimits(::Makie.Symlog10) = (0.0, 100.0) +defined_interval(scale::ReversibleScale) = scale.interval defined_interval(::typeof(identity)) = OpenInterval(-Inf, Inf) -defined_interval(::ReversibleScale) = OpenInterval(-Inf, Inf) defined_interval(::LogFunctions) = OpenInterval(0.0, Inf) defined_interval(::typeof(sqrt)) = Interval{:closed,:open}(0, Inf) defined_interval(::typeof(Makie.logit)) = OpenInterval(0.0, 1.0) diff --git a/src/types.jl b/src/types.jl index b562fd1ca3d..d6629cc4d93 100644 --- a/src/types.jl +++ b/src/types.jl @@ -381,12 +381,14 @@ const RGBColors = Union{RGBAf, Vector{RGBAf}, Vector{Float32}} const LogFunctions = Union{typeof(log10), typeof(log2), typeof(log)} -struct ReversibleScale{F <: Function, B <: Function} +struct ReversibleScale{F <: Function, B <: Function, I <: AbstractInterval} forward::F backward::B logbase::String - function ReversibleScale(forward, backward, logbase = "") - new{typeof(forward),typeof(backward)}(forward, backward, logbase) + limits::NTuple{2,Float32} + interval::I + function ReversibleScale(forward, backward; logbase = "", limits = (0f0, 10f0), interval = OpenInterval(-Inf32, Inf32)) + new{typeof(forward),typeof(backward),typeof(interval)}(forward, backward, logbase, limits, interval) end end From ec9ed8489e0109e4d9210a792b600f1c67ffda79 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Fri, 11 Aug 2023 12:19:54 +0200 Subject: [PATCH 05/18] simplify `interval` kw --- src/makielayout/blocks/axis.jl | 2 +- src/types.jl | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/makielayout/blocks/axis.jl b/src/makielayout/blocks/axis.jl index b4b120bce6d..5f2ca8b3806 100644 --- a/src/makielayout/blocks/axis.jl +++ b/src/makielayout/blocks/axis.jl @@ -499,7 +499,7 @@ function initialize_block!(ax::Axis; palette = nothing) register_events!(ax, scene) # these are the user defined limits - on(blockscene, ax.limits) do mlims + on(blockscene, ax.limits) do _ reset_limits!(ax) end diff --git a/src/types.jl b/src/types.jl index d6629cc4d93..74b1463d79a 100644 --- a/src/types.jl +++ b/src/types.jl @@ -387,7 +387,11 @@ struct ReversibleScale{F <: Function, B <: Function, I <: AbstractInterval} logbase::String limits::NTuple{2,Float32} interval::I - function ReversibleScale(forward, backward; logbase = "", limits = (0f0, 10f0), interval = OpenInterval(-Inf32, Inf32)) + function ReversibleScale(forward, backward; logbase = "", limits = (0f0, 10f0), interval = (-Inf32, Inf32)) + if !(interval isa AbstractInterval) + interval = OpenInterval(Float32.(interval)...) + end + limits = Tuple(Float32.(limits)) new{typeof(forward),typeof(backward),typeof(interval)}(forward, backward, logbase, limits, interval) end end From 93a4af3dc8f73a01cff057977619c7248933aee7 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Fri, 11 Aug 2023 12:34:42 +0200 Subject: [PATCH 06/18] add docstring --- src/types.jl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/types.jl b/src/types.jl index 74b1463d79a..bf4907169b5 100644 --- a/src/types.jl +++ b/src/types.jl @@ -200,6 +200,9 @@ end Struct to hold all relevant matrices and additional parameters, to let backends apply camera based transformations. + +## Fields +$(TYPEDFIELDS) """ struct Camera """ @@ -381,11 +384,34 @@ const RGBColors = Union{RGBAf, Vector{RGBAf}, Vector{Float32}} const LogFunctions = Union{typeof(log10), typeof(log2), typeof(log)} +""" + ReversibleScale + +Custom scale struct, taking a scaling (forward) and an inverse (backward) arbitrary scale function. + +## Fields +$(TYPEDFIELDS) +""" struct ReversibleScale{F <: Function, B <: Function, I <: AbstractInterval} + """ + forward transformation (e.g. `log10`) + """ forward::F + """ + inverse transformation (e.g. `exp10` for `log10` such that backward ∘ forward ≡ identity) + """ backward::B + """ + ticks base (e.g. "10" for pseudo `log10` ticks) (optional) + """ logbase::String + """ + default limits (optional) + """ limits::NTuple{2,Float32} + """ + valid limits interval (optional) + """ interval::I function ReversibleScale(forward, backward; logbase = "", limits = (0f0, 10f0), interval = (-Inf32, Inf32)) if !(interval isa AbstractInterval) From 82c68d4a873a9c4c27955dc6a9f3e0c597dede3b Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 20 Aug 2023 14:22:24 +0200 Subject: [PATCH 07/18] apply sugestions - clarify warnings --- src/makielayout/blocks/colorbar.jl | 2 +- src/makielayout/lineaxis.jl | 4 ++-- src/types.jl | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/makielayout/blocks/colorbar.jl b/src/makielayout/blocks/colorbar.jl index f8fc60d1788..0f15019f686 100644 --- a/src/makielayout/blocks/colorbar.jl +++ b/src/makielayout/blocks/colorbar.jl @@ -70,7 +70,7 @@ end colorbar_range(start, stop, length, _::typeof(identity)) = LinRange(start, stop, length) function colorbar_range(start, stop, length, _) - @warn "cannot determine inverse_transform" + @warn "Cannot determine inverse transform: maybe use `Makie.ReverseibleScale` instead ? " colorbar_range(start, stop, length, identity) # noop (nothing we can do, bailing out) end diff --git a/src/makielayout/lineaxis.jl b/src/makielayout/lineaxis.jl index 8edbb27394b..a540eb7157a 100644 --- a/src/makielayout/lineaxis.jl +++ b/src/makielayout/lineaxis.jl @@ -585,11 +585,11 @@ _logbase(s::ReversibleScale) = s.logbase _logbase(::typeof(log10)) = "10" _logbase(::typeof(log2)) = "2" _logbase(::typeof(log)) = "e" -_logbase(::Any) = "" +_logbase(::Any) = nothing function get_ticks(::Automatic, scale::Union{LogFunctions,ReversibleScale}, any_formatter, vmin, vmax) wt = WilkinsonTicks(5, k_min = 3) - ticks = isempty(_logbase(scale)) ? wt : LogTicks(wt) + ticks = isnothing(_logbase(scale)) ? wt : LogTicks(wt) get_ticks(ticks, scale, any_formatter, vmin, vmax) end diff --git a/src/types.jl b/src/types.jl index bf4907169b5..763968da018 100644 --- a/src/types.jl +++ b/src/types.jl @@ -404,7 +404,7 @@ struct ReversibleScale{F <: Function, B <: Function, I <: AbstractInterval} """ ticks base (e.g. "10" for pseudo `log10` ticks) (optional) """ - logbase::String + logbase::Union{Nothing,String} """ default limits (optional) """ @@ -413,7 +413,7 @@ struct ReversibleScale{F <: Function, B <: Function, I <: AbstractInterval} valid limits interval (optional) """ interval::I - function ReversibleScale(forward, backward; logbase = "", limits = (0f0, 10f0), interval = (-Inf32, Inf32)) + function ReversibleScale(forward, backward; logbase = nothing, limits = (0f0, 10f0), interval = (-Inf32, Inf32)) if !(interval isa AbstractInterval) interval = OpenInterval(Float32.(interval)...) end From 4ec2f989f2878fd45bb766537346222b7f969b7d Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 20 Aug 2023 17:13:42 +0200 Subject: [PATCH 08/18] remove space --- src/colorsampler.jl | 4 ++-- src/makielayout/blocks/colorbar.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/colorsampler.jl b/src/colorsampler.jl index 929a26ea84d..47a16029f98 100644 --- a/src/colorsampler.jl +++ b/src/colorsampler.jl @@ -179,7 +179,7 @@ end struct ColorMap{N,T<:AbstractArray{<:Number,N},T2<:AbstractArray{<:Number,N}} color::Observable{T} colormap::Observable{Vector{RGBAf}} - scale::Observable{Union{ReversibleScale,Function}} + scale::Observable{Union{ReversibleScale, Function}} mapping::Observable{Union{Nothing, Vector{Float64}}} colorrange::Observable{Vec{2,Float64}} @@ -198,7 +198,7 @@ function assemble_colors(::T, @nospecialize(color), @nospecialize(plot)) where { color_tight = convert(Observable{T}, color) colormap = Observable(RGBAf[]; ignore_equal_values=true) categorical = Observable(false) - colorscale = convert(Observable, plot.colorscale) + colorscale = convert(Observable{Union{ReversibleScale, Function}}, plot.colorscale) mapping = Observable{Union{Nothing, Vector{Float64}}}(nothing) function update_colors(cmap, a) diff --git a/src/makielayout/blocks/colorbar.jl b/src/makielayout/blocks/colorbar.jl index 0f15019f686..979f0f1eadc 100644 --- a/src/makielayout/blocks/colorbar.jl +++ b/src/makielayout/blocks/colorbar.jl @@ -70,7 +70,7 @@ end colorbar_range(start, stop, length, _::typeof(identity)) = LinRange(start, stop, length) function colorbar_range(start, stop, length, _) - @warn "Cannot determine inverse transform: maybe use `Makie.ReverseibleScale` instead ? " + @warn "Cannot determine inverse transform: maybe use `Makie.ReverseibleScale` instead ?" colorbar_range(start, stop, length, identity) # noop (nothing we can do, bailing out) end From 7befef13d965b3e21356b680b8a933a3806d4d49 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 22 Aug 2023 19:03:53 +0200 Subject: [PATCH 09/18] simplify `colorbar_range` --- src/layouting/transformation.jl | 10 ---------- src/makielayout/blocks/colorbar.jl | 16 ++++++++-------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/layouting/transformation.jl b/src/layouting/transformation.jl index 86195495856..be9edbab9a4 100644 --- a/src/layouting/transformation.jl +++ b/src/layouting/transformation.jl @@ -399,16 +399,6 @@ function inv_symlog10(x, low, high) end end -const REVERSIBLE_SCALES = Union{ - # typeof(identity), # no, this is a noop - LogFunctions, - typeof(pseudolog10), - typeof(logit), - typeof(sqrt), - ReversibleScale, - Symlog10, -} - inverse_transform(::typeof(identity)) = identity inverse_transform(::typeof(log10)) = exp10 inverse_transform(::typeof(log)) = exp diff --git a/src/makielayout/blocks/colorbar.jl b/src/makielayout/blocks/colorbar.jl index 8aa4be14438..374a933099c 100644 --- a/src/makielayout/blocks/colorbar.jl +++ b/src/makielayout/blocks/colorbar.jl @@ -90,14 +90,14 @@ function Colorbar(fig_or_scene, voronoi::Voronoiplot; kwargs...) ) end -colorbar_range(start, stop, length, _::typeof(identity)) = LinRange(start, stop, length) -function colorbar_range(start, stop, length, _) - @warn "Cannot determine inverse transform: maybe use `Makie.ReverseibleScale` instead ?" - colorbar_range(start, stop, length, identity) # noop (nothing we can do, bailing out) -end - -function colorbar_range(start, stop, length, scale::REVERSIBLE_SCALES) - inverse_transform(scale).(range(start, stop; length)) +function colorbar_range(start, stop, length, colorscale) + rev = inverse_transform(colorscale) + if isnothing(rev) + @warn "Cannot determine inverse transform: you can use `colorscale=Makie.ReversibleScale($(colorscale), reverse($(colorscale)))` instead. +Falling back to `identity`, which leads to sub optimal ticks!" + return LinRange(start, stop, length) + end + rev.(range(start, stop; length)) end function initialize_block!(cb::Colorbar) From 70113ca781739f48ecb2619825bd4073abe0d703 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Wed, 23 Aug 2023 18:03:12 +0200 Subject: [PATCH 10/18] add inverse transform checks --- src/layouting/transformation.jl | 6 +++--- src/makielayout/blocks/colorbar.jl | 14 +++++++------- src/types.jl | 26 +++++++++++++++++--------- test/makielayout.jl | 12 ++++++++++++ 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/layouting/transformation.jl b/src/layouting/transformation.jl index be9edbab9a4..b1541407ad8 100644 --- a/src/layouting/transformation.jl +++ b/src/layouting/transformation.jl @@ -401,15 +401,15 @@ end inverse_transform(::typeof(identity)) = identity inverse_transform(::typeof(log10)) = exp10 -inverse_transform(::typeof(log)) = exp inverse_transform(::typeof(log2)) = exp2 +inverse_transform(::typeof(log)) = exp inverse_transform(::typeof(sqrt)) = x -> x ^ 2 inverse_transform(::typeof(pseudolog10)) = inv_pseudolog10 inverse_transform(F::Tuple) = map(inverse_transform, F) inverse_transform(::typeof(logit)) = logistic -inverse_transform(s::ReversibleScale) = s.backward +inverse_transform(s::ReversibleScale) = s.reverse inverse_transform(s::Symlog10) = x -> inv_symlog10(x, s.low, s.high) -inverse_transform(s) = nothing +inverse_transform(::Any) = nothing function is_identity_transform(t) return t === identity || t isa Tuple && all(x-> x === identity, t) diff --git a/src/makielayout/blocks/colorbar.jl b/src/makielayout/blocks/colorbar.jl index 374a933099c..a23240397c9 100644 --- a/src/makielayout/blocks/colorbar.jl +++ b/src/makielayout/blocks/colorbar.jl @@ -91,13 +91,13 @@ function Colorbar(fig_or_scene, voronoi::Voronoiplot; kwargs...) end function colorbar_range(start, stop, length, colorscale) - rev = inverse_transform(colorscale) - if isnothing(rev) - @warn "Cannot determine inverse transform: you can use `colorscale=Makie.ReversibleScale($(colorscale), reverse($(colorscale)))` instead. -Falling back to `identity`, which leads to sub optimal ticks!" - return LinRange(start, stop, length) - end - rev.(range(start, stop; length)) + colorscale === identity && return LinRange(start, stop, length) + + reverse = inverse_transform(colorscale) + isnothing(reverse) && throw(ArgumentError( + "Cannot determine inverse transform: you can use `Makie.ReversibleScale($(colorscale), reverse($(colorscale)))` instead." + )) + reverse.(range(start, stop; length)) end function initialize_block!(cb::Colorbar) diff --git a/src/types.jl b/src/types.jl index 763968da018..53e9e1316a7 100644 --- a/src/types.jl +++ b/src/types.jl @@ -387,7 +387,7 @@ const LogFunctions = Union{typeof(log10), typeof(log2), typeof(log)} """ ReversibleScale -Custom scale struct, taking a scaling (forward) and an inverse (backward) arbitrary scale function. +Custom scale struct, taking a forward and reverse arbitrary scale function. ## Fields $(TYPEDFIELDS) @@ -398,9 +398,9 @@ struct ReversibleScale{F <: Function, B <: Function, I <: AbstractInterval} """ forward::F """ - inverse transformation (e.g. `exp10` for `log10` such that backward ∘ forward ≡ identity) + reverse transformation (e.g. `exp10` for `log10` such that reverse ∘ forward ≡ identity) """ - backward::B + reverse::B """ ticks base (e.g. "10" for pseudo `log10` ticks) (optional) """ @@ -413,12 +413,20 @@ struct ReversibleScale{F <: Function, B <: Function, I <: AbstractInterval} valid limits interval (optional) """ interval::I - function ReversibleScale(forward, backward; logbase = nothing, limits = (0f0, 10f0), interval = (-Inf32, Inf32)) - if !(interval isa AbstractInterval) - interval = OpenInterval(Float32.(interval)...) - end - limits = Tuple(Float32.(limits)) - new{typeof(forward),typeof(backward),typeof(interval)}(forward, backward, logbase, limits, interval) + function ReversibleScale(forward, reverse = Automatic(); logbase = nothing, limits = (0f0, 10f0), interval = (-Inf32, Inf32)) + reverse isa Automatic && (reverse = inverse_transform(forward)) + isnothing(reverse) && throw(ArgumentError( + "Cannot determine inverse transform: you can use `Makie.ReversibleScale($(forward), reverse($(forward)))` instead." + )) + interval isa AbstractInterval || (interval = OpenInterval(Float32.(interval)...)) + + lft, rgt = limits = Tuple(Float32.(limits)) + + Id = reverse ∘ forward + lft ≈ Id(lft) || throw(ArgumentError("Invalid inverse transform: $lft !≈ $(Id(lft))")) + rgt ≈ Id(rgt) || throw(ArgumentError("Invalid inverse transform: $rgt !≈ $(Id(rgt))")) + + new{typeof(forward),typeof(reverse),typeof(interval)}(forward, reverse, logbase, limits, interval) end end diff --git a/test/makielayout.jl b/test/makielayout.jl index 8d0bd20d107..b8e67b3c86a 100644 --- a/test/makielayout.jl +++ b/test/makielayout.jl @@ -376,3 +376,15 @@ end @test_nowarn axislegend() f end + +@testset "ReversibleScale" begin + @test Makie.ReversibleScale(identity).reverse === identity + @test Makie.ReversibleScale(log).reverse === exp + @test_throws ArgumentError Makie.ReversibleScale(x -> log10(x)) # missing reverse scale + @test_throws ArgumentError Makie.ReversibleScale(sqrt, exp10) # incorrect reverse scale +end + +@testset "Invalid inverse transform" begin + f = Figure() + @test_throws ArgumentError Colorbar(f[1, 1], limits = (1, 100), scale = x -> log10(x)) +end From 17454b5acfa381503edd92b074002bc7a738042d Mon Sep 17 00:00:00 2001 From: t-bltg Date: Wed, 23 Aug 2023 18:54:16 +0200 Subject: [PATCH 11/18] renames --- src/layouting/transformation.jl | 2 +- src/makielayout/blocks/colorbar.jl | 9 +++++---- src/types.jl | 24 ++++++++++++------------ test/makielayout.jl | 8 ++++---- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/layouting/transformation.jl b/src/layouting/transformation.jl index b1541407ad8..19b25aaa250 100644 --- a/src/layouting/transformation.jl +++ b/src/layouting/transformation.jl @@ -407,7 +407,7 @@ inverse_transform(::typeof(sqrt)) = x -> x ^ 2 inverse_transform(::typeof(pseudolog10)) = inv_pseudolog10 inverse_transform(F::Tuple) = map(inverse_transform, F) inverse_transform(::typeof(logit)) = logistic -inverse_transform(s::ReversibleScale) = s.reverse +inverse_transform(s::ReversibleScale) = s.inverse inverse_transform(s::Symlog10) = x -> inv_symlog10(x, s.low, s.high) inverse_transform(::Any) = nothing diff --git a/src/makielayout/blocks/colorbar.jl b/src/makielayout/blocks/colorbar.jl index a23240397c9..7a4accf7deb 100644 --- a/src/makielayout/blocks/colorbar.jl +++ b/src/makielayout/blocks/colorbar.jl @@ -93,11 +93,12 @@ end function colorbar_range(start, stop, length, colorscale) colorscale === identity && return LinRange(start, stop, length) - reverse = inverse_transform(colorscale) - isnothing(reverse) && throw(ArgumentError( - "Cannot determine inverse transform: you can use `Makie.ReversibleScale($(colorscale), reverse($(colorscale)))` instead." + inverse = inverse_transform(colorscale) + isnothing(inverse) && throw(ArgumentError( + "Cannot determine inverse transform: you can use `Makie.ReversibleScale($(colorscale), inverse($(colorscale)))` instead." )) - reverse.(range(start, stop; length)) + + inverse.(range(start, stop; length)) end function initialize_block!(cb::Colorbar) diff --git a/src/types.jl b/src/types.jl index 53e9e1316a7..95bba20cf8c 100644 --- a/src/types.jl +++ b/src/types.jl @@ -387,22 +387,22 @@ const LogFunctions = Union{typeof(log10), typeof(log2), typeof(log)} """ ReversibleScale -Custom scale struct, taking a forward and reverse arbitrary scale function. +Custom scale struct, taking a forward and inverse arbitrary scale function. ## Fields $(TYPEDFIELDS) """ -struct ReversibleScale{F <: Function, B <: Function, I <: AbstractInterval} +struct ReversibleScale{F <: Function, I <: Function, T <: AbstractInterval} """ forward transformation (e.g. `log10`) """ forward::F """ - reverse transformation (e.g. `exp10` for `log10` such that reverse ∘ forward ≡ identity) + inverse transformation (e.g. `exp10` for `log10` such that inverse ∘ forward ≡ identity) """ - reverse::B + inverse::I """ - ticks base (e.g. "10" for pseudo `log10` ticks) (optional) + LogTicks base (e.g. "10" for pseudo log ticks) (optional) """ logbase::Union{Nothing,String} """ @@ -412,21 +412,21 @@ struct ReversibleScale{F <: Function, B <: Function, I <: AbstractInterval} """ valid limits interval (optional) """ - interval::I - function ReversibleScale(forward, reverse = Automatic(); logbase = nothing, limits = (0f0, 10f0), interval = (-Inf32, Inf32)) - reverse isa Automatic && (reverse = inverse_transform(forward)) - isnothing(reverse) && throw(ArgumentError( - "Cannot determine inverse transform: you can use `Makie.ReversibleScale($(forward), reverse($(forward)))` instead." + interval::T + function ReversibleScale(forward, inverse = Automatic(); logbase = nothing, limits = (0f0, 10f0), interval = (-Inf32, Inf32)) + inverse isa Automatic && (inverse = inverse_transform(forward)) + isnothing(inverse) && throw(ArgumentError( + "Cannot determine inverse transform: you can use `Makie.ReversibleScale($(forward), inverse($(forward)))` instead." )) interval isa AbstractInterval || (interval = OpenInterval(Float32.(interval)...)) lft, rgt = limits = Tuple(Float32.(limits)) - Id = reverse ∘ forward + Id = inverse ∘ forward lft ≈ Id(lft) || throw(ArgumentError("Invalid inverse transform: $lft !≈ $(Id(lft))")) rgt ≈ Id(rgt) || throw(ArgumentError("Invalid inverse transform: $rgt !≈ $(Id(rgt))")) - new{typeof(forward),typeof(reverse),typeof(interval)}(forward, reverse, logbase, limits, interval) + new{typeof(forward),typeof(inverse),typeof(interval)}(forward, inverse, logbase, limits, interval) end end diff --git a/test/makielayout.jl b/test/makielayout.jl index b8e67b3c86a..32e5ea2cde7 100644 --- a/test/makielayout.jl +++ b/test/makielayout.jl @@ -378,10 +378,10 @@ end end @testset "ReversibleScale" begin - @test Makie.ReversibleScale(identity).reverse === identity - @test Makie.ReversibleScale(log).reverse === exp - @test_throws ArgumentError Makie.ReversibleScale(x -> log10(x)) # missing reverse scale - @test_throws ArgumentError Makie.ReversibleScale(sqrt, exp10) # incorrect reverse scale + @test Makie.ReversibleScale(identity).inverse === identity + @test Makie.ReversibleScale(log).inverse === exp + @test_throws ArgumentError Makie.ReversibleScale(x -> log10(x)) # missing inverse scale + @test_throws ArgumentError Makie.ReversibleScale(sqrt, exp10) # incorrect inverse scale end @testset "Invalid inverse transform" begin From ef8c387e9d06c355f1e4c3d2e7de667e2165d7a6 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 27 Aug 2023 15:29:32 +0200 Subject: [PATCH 12/18] drop `LogTicks` - rework `Symlog10` and `pseudolog10` --- src/layouting/transformation.jl | 49 +++++++++++---------------------- src/makielayout/blocks/axis.jl | 8 +++--- src/makielayout/lineaxis.jl | 12 +++----- src/types.jl | 8 ++---- test/makielayout.jl | 14 ++++++++++ 5 files changed, 40 insertions(+), 51 deletions(-) diff --git a/src/layouting/transformation.jl b/src/layouting/transformation.jl index 19b25aaa250..8859ff918cd 100644 --- a/src/layouting/transformation.jl +++ b/src/layouting/transformation.jl @@ -339,7 +339,6 @@ function apply_transform(f, itr::ClosedInterval) return apply_transform(f, mini) .. apply_transform(f, maxi) end - function apply_transform(f, r::Rect) mi = minimum(r) ma = maximum(r) @@ -360,55 +359,39 @@ apply_transform(f::typeof(identity), r::Rect) = r apply_transform(f::NTuple{2, typeof(identity)}, r::Rect) = r apply_transform(f::NTuple{3, typeof(identity)}, r::Rect) = r +const pseudolog10 = ReversibleScale( + x -> sign(x) * log10(abs(x) + 1), + x -> sign(x) * (exp10(abs(x)) - 1) +) -pseudolog10(x) = sign(x) * log10(abs(x) + 1) -inv_pseudolog10(x) = sign(x) * (exp10(abs(x)) - 1) - -struct Symlog10 - low::Float64 - high::Float64 - function Symlog10(low, high) - if !(low < 0 && high > 0) - error("Low bound needs to be smaller than 0 and high bound larger than 0. You gave $low, $high.") - end - new(Float64(low), Float64(high)) - end -end - -Symlog10(x) = Symlog10(-x, x) - -function (s::Symlog10)(x) - if x > 0 - x <= s.high ? x / s.high * log10(s.high) : log10(x) +Symlog10(hi) = Symlog10(-hi, hi) +Symlog10(lo, hi) = ReversibleScale( + x -> if x > 0 + x <= hi ? x / hi * log10(hi) : log10(x) elseif x < 0 - x >= s.low ? x / abs(s.low) * log10(abs(s.low)) : sign(x) * log10(abs(x)) + x >= lo ? x / abs(lo) * log10(abs(lo)) : -log10(abs(x)) else x - end -end - -function inv_symlog10(x, low, high) - if x > 0 - l = log10(high) - x <= l ? x / l * high : exp10(x) + end, + x -> if x > 0 + l = log10(hi) + x <= l ? x / l * hi : exp10(x) elseif x < 0 - l = sign(x) * log10(abs(low)) - x >= l ? x / l * abs(low) : sign(x) * exp10(abs(x)) + l = -log10(abs(lo)) + x >= l ? x / l * abs(lo) : -exp10(abs(x)) else x end -end +) inverse_transform(::typeof(identity)) = identity inverse_transform(::typeof(log10)) = exp10 inverse_transform(::typeof(log2)) = exp2 inverse_transform(::typeof(log)) = exp inverse_transform(::typeof(sqrt)) = x -> x ^ 2 -inverse_transform(::typeof(pseudolog10)) = inv_pseudolog10 inverse_transform(F::Tuple) = map(inverse_transform, F) inverse_transform(::typeof(logit)) = logistic inverse_transform(s::ReversibleScale) = s.inverse -inverse_transform(s::Symlog10) = x -> inv_symlog10(x, s.low, s.high) inverse_transform(::Any) = nothing function is_identity_transform(t) diff --git a/src/makielayout/blocks/axis.jl b/src/makielayout/blocks/axis.jl index a8021f98ca9..e194faed532 100644 --- a/src/makielayout/blocks/axis.jl +++ b/src/makielayout/blocks/axis.jl @@ -1363,16 +1363,16 @@ end defaultlimits(::typeof(identity)) = (0.0, 10.0) defaultlimits(::typeof(sqrt)) = (0.0, 100.0) defaultlimits(::typeof(Makie.logit)) = (0.01, 0.99) -defaultlimits(::typeof(Makie.pseudolog10)) = (0.0, 100.0) -defaultlimits(::Makie.Symlog10) = (0.0, 100.0) +# defaultlimits(::typeof(Makie.pseudolog10)) = (0.0, 100.0) +# defaultlimits(::Makie.Symlog10) = (0.0, 100.0) defined_interval(scale::ReversibleScale) = scale.interval defined_interval(::typeof(identity)) = OpenInterval(-Inf, Inf) defined_interval(::LogFunctions) = OpenInterval(0.0, Inf) defined_interval(::typeof(sqrt)) = Interval{:closed,:open}(0, Inf) defined_interval(::typeof(Makie.logit)) = OpenInterval(0.0, 1.0) -defined_interval(::typeof(Makie.pseudolog10)) = OpenInterval(-Inf, Inf) -defined_interval(::Makie.Symlog10) = OpenInterval(-Inf, Inf) +# defined_interval(::typeof(Makie.pseudolog10)) = OpenInterval(-Inf, Inf) +# defined_interval(::Makie.Symlog10) = OpenInterval(-Inf, Inf) function update_state_before_display!(ax::Axis) reset_limits!(ax) diff --git a/src/makielayout/lineaxis.jl b/src/makielayout/lineaxis.jl index a540eb7157a..41ee1c9a100 100644 --- a/src/makielayout/lineaxis.jl +++ b/src/makielayout/lineaxis.jl @@ -581,20 +581,17 @@ function get_ticks(tickfunction::Function, _, formatter, vmin, vmax) return tickvalues, ticklabels end -_logbase(s::ReversibleScale) = s.logbase _logbase(::typeof(log10)) = "10" _logbase(::typeof(log2)) = "2" _logbase(::typeof(log)) = "e" -_logbase(::Any) = nothing -function get_ticks(::Automatic, scale::Union{LogFunctions,ReversibleScale}, any_formatter, vmin, vmax) - wt = WilkinsonTicks(5, k_min = 3) - ticks = isnothing(_logbase(scale)) ? wt : LogTicks(wt) +function get_ticks(::Automatic, scale::LogFunctions, any_formatter, vmin, vmax) + ticks = LogTicks(WilkinsonTicks(5, k_min = 3)) get_ticks(ticks, scale, any_formatter, vmin, vmax) end # log ticks just use the normal pipeline but with log'd limits, then transform the labels -function get_ticks(l::LogTicks, scale::Union{LogFunctions,ReversibleScale}, ::Automatic, vmin, vmax) +function get_ticks(l::LogTicks, scale::LogFunctions, ::Automatic, vmin, vmax) ticks_scaled = get_tickvalues(l.linear_ticks, identity, scale(vmin), scale(vmax)) ticks = Makie.inverse_transform(scale).(ticks_scaled) @@ -607,7 +604,7 @@ function get_ticks(l::LogTicks, scale::Union{LogFunctions,ReversibleScale}, ::Au ) labels = rich.(_logbase(scale), superscript.(labels_scaled, offset = Vec2f(0.1f0, 0f0))) - (ticks, labels) + ticks, labels end # function get_ticks(::Automatic, scale::typeof(Makie.logit), any_formatter, vmin, vmax) @@ -686,7 +683,6 @@ Gets tick labels by formatting each value in `values` according to a `Formatting """ get_ticklabels(formatstring::AbstractString, values) = [Formatting.format(formatstring, v) for v in values] - function get_ticks(m::MultiplesTicks, any_scale, ::Automatic, vmin, vmax) dvmin = vmin / m.multiple dvmax = vmax / m.multiple diff --git a/src/types.jl b/src/types.jl index 95bba20cf8c..d16d13fc561 100644 --- a/src/types.jl +++ b/src/types.jl @@ -402,10 +402,6 @@ struct ReversibleScale{F <: Function, I <: Function, T <: AbstractInterval} """ inverse::I """ - LogTicks base (e.g. "10" for pseudo log ticks) (optional) - """ - logbase::Union{Nothing,String} - """ default limits (optional) """ limits::NTuple{2,Float32} @@ -413,7 +409,7 @@ struct ReversibleScale{F <: Function, I <: Function, T <: AbstractInterval} valid limits interval (optional) """ interval::T - function ReversibleScale(forward, inverse = Automatic(); logbase = nothing, limits = (0f0, 10f0), interval = (-Inf32, Inf32)) + function ReversibleScale(forward, inverse = Automatic(); limits = (0f0, 10f0), interval = (-Inf32, Inf32)) inverse isa Automatic && (inverse = inverse_transform(forward)) isnothing(inverse) && throw(ArgumentError( "Cannot determine inverse transform: you can use `Makie.ReversibleScale($(forward), inverse($(forward)))` instead." @@ -426,7 +422,7 @@ struct ReversibleScale{F <: Function, I <: Function, T <: AbstractInterval} lft ≈ Id(lft) || throw(ArgumentError("Invalid inverse transform: $lft !≈ $(Id(lft))")) rgt ≈ Id(rgt) || throw(ArgumentError("Invalid inverse transform: $rgt !≈ $(Id(rgt))")) - new{typeof(forward),typeof(inverse),typeof(interval)}(forward, inverse, logbase, limits, interval) + new{typeof(forward),typeof(inverse),typeof(interval)}(forward, inverse, limits, interval) end end diff --git a/test/makielayout.jl b/test/makielayout.jl index 32e5ea2cde7..8cdd41ff8c9 100644 --- a/test/makielayout.jl +++ b/test/makielayout.jl @@ -388,3 +388,17 @@ end f = Figure() @test_throws ArgumentError Colorbar(f[1, 1], limits = (1, 100), scale = x -> log10(x)) end + +@testset "Colorscales" begin + x = 10.0.^(1:0.1:4) + y = 1.0:0.1:5.0 + z = broadcast((x, y) -> x, x, y') + + scale = Makie.Symlog10(2) + fig, ax, hm = heatmap(x, y, z; colorscale = scale, axis = (; xscale = scale)) + Colorbar(fig[1, 2], hm) + + scale = Makie.pseudolog10 + fig, ax, hm = heatmap(x, y, z; colorscale = scale, axis = (; xscale = scale)) + Colorbar(fig[1, 2], hm) +end From 512d9f46b55d97e4cb416b62f3a54907f8191026 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 27 Aug 2023 16:08:42 +0200 Subject: [PATCH 13/18] add limits --- src/layouting/transformation.jl | 14 ++++++++------ src/makielayout/blocks/axis.jl | 4 ---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/layouting/transformation.jl b/src/layouting/transformation.jl index 8859ff918cd..c7a31c4deda 100644 --- a/src/layouting/transformation.jl +++ b/src/layouting/transformation.jl @@ -361,19 +361,20 @@ apply_transform(f::NTuple{3, typeof(identity)}, r::Rect) = r const pseudolog10 = ReversibleScale( x -> sign(x) * log10(abs(x) + 1), - x -> sign(x) * (exp10(abs(x)) - 1) + x -> sign(x) * (exp10(abs(x)) - 1); + limits=(0f0, 3f0) ) Symlog10(hi) = Symlog10(-hi, hi) -Symlog10(lo, hi) = ReversibleScale( - x -> if x > 0 +function Symlog10(lo, hi) + forward(x) = if x > 0 x <= hi ? x / hi * log10(hi) : log10(x) elseif x < 0 x >= lo ? x / abs(lo) * log10(abs(lo)) : -log10(abs(x)) else x - end, - x -> if x > 0 + end + inverse(x) = if x > 0 l = log10(hi) x <= l ? x / l * hi : exp10(x) elseif x < 0 @@ -382,7 +383,8 @@ Symlog10(lo, hi) = ReversibleScale( else x end -) + ReversibleScale(forward, inverse; limits=(0f0, 3f0)) +end inverse_transform(::typeof(identity)) = identity inverse_transform(::typeof(log10)) = exp10 diff --git a/src/makielayout/blocks/axis.jl b/src/makielayout/blocks/axis.jl index e194faed532..ad179a11e3e 100644 --- a/src/makielayout/blocks/axis.jl +++ b/src/makielayout/blocks/axis.jl @@ -1363,16 +1363,12 @@ end defaultlimits(::typeof(identity)) = (0.0, 10.0) defaultlimits(::typeof(sqrt)) = (0.0, 100.0) defaultlimits(::typeof(Makie.logit)) = (0.01, 0.99) -# defaultlimits(::typeof(Makie.pseudolog10)) = (0.0, 100.0) -# defaultlimits(::Makie.Symlog10) = (0.0, 100.0) defined_interval(scale::ReversibleScale) = scale.interval defined_interval(::typeof(identity)) = OpenInterval(-Inf, Inf) defined_interval(::LogFunctions) = OpenInterval(0.0, Inf) defined_interval(::typeof(sqrt)) = Interval{:closed,:open}(0, Inf) defined_interval(::typeof(Makie.logit)) = OpenInterval(0.0, 1.0) -# defined_interval(::typeof(Makie.pseudolog10)) = OpenInterval(-Inf, Inf) -# defined_interval(::Makie.Symlog10) = OpenInterval(-Inf, Inf) function update_state_before_display!(ax::Axis) reset_limits!(ax) From 774488337699fbf5a1e20e2a49475eb8948945d7 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 27 Aug 2023 16:58:50 +0200 Subject: [PATCH 14/18] remove test --- ReferenceTests/src/tests/examples2d.jl | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ReferenceTests/src/tests/examples2d.jl b/ReferenceTests/src/tests/examples2d.jl index e0a97ceaae8..1b60e466edb 100644 --- a/ReferenceTests/src/tests/examples2d.jl +++ b/ReferenceTests/src/tests/examples2d.jl @@ -597,15 +597,6 @@ end fig end -@reference_test "custom colorscale (heatmap)" begin - x = 10.0.^(1:0.1:4) - y = 1.0:0.1:5.0 - scale = Makie.ReversibleScale(x -> log10(x + 2), x -> exp10(x) - 2; logbase = "10") - fig, ax, hm = heatmap(x, y, (x, y) -> x; axis = (; xscale = scale), colorscale = scale) - Colorbar(fig[1, 2], hm) - fig -end - @reference_test "colorscale (lines)" begin xs = 0:0.01:10 ys = 2 .* (1 .+ sin.(xs)) From 254f0a2cf075b929f63fb627af9d624e66a2ea47 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 29 Aug 2023 18:32:45 +0200 Subject: [PATCH 15/18] export `ReversibleScale` --- NEWS.md | 2 +- src/Makie.jl | 1 + src/makielayout/blocks/colorbar.jl | 2 +- src/types.jl | 2 +- test/makielayout.jl | 8 ++++---- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/NEWS.md b/NEWS.md index 904d2bf94b2..e039f6e4f21 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,7 @@ # News ## master -- Allow arbitrary reversible scale functions through `Makie.ReversibleScale`. +- Allow arbitrary reversible scale functions through `ReversibleScale`. - Fixed some errors around dynamic changes of `ax.xscale` or `ax.yscale` [#3084](https://github.com/MakieOrg/Makie.jl/pull/3084) - Improved Barplot Label Alignment [#3160](https://github.com/MakieOrg/Makie.jl/issues/3160). diff --git a/src/Makie.jl b/src/Makie.jl index 11c719e6f70..3b954500da7 100644 --- a/src/Makie.jl +++ b/src/Makie.jl @@ -286,6 +286,7 @@ export save, colorbuffer export cgrad, available_gradients, showgradients export Pattern +export ReversibleScale export assetpath # default icon for Makie diff --git a/src/makielayout/blocks/colorbar.jl b/src/makielayout/blocks/colorbar.jl index 7a4accf7deb..266d6efb049 100644 --- a/src/makielayout/blocks/colorbar.jl +++ b/src/makielayout/blocks/colorbar.jl @@ -95,7 +95,7 @@ function colorbar_range(start, stop, length, colorscale) inverse = inverse_transform(colorscale) isnothing(inverse) && throw(ArgumentError( - "Cannot determine inverse transform: you can use `Makie.ReversibleScale($(colorscale), inverse($(colorscale)))` instead." + "Cannot determine inverse transform: you can use `ReversibleScale($(colorscale), inverse($(colorscale)))` instead." )) inverse.(range(start, stop; length)) diff --git a/src/types.jl b/src/types.jl index d16d13fc561..fd8fb4078c6 100644 --- a/src/types.jl +++ b/src/types.jl @@ -412,7 +412,7 @@ struct ReversibleScale{F <: Function, I <: Function, T <: AbstractInterval} function ReversibleScale(forward, inverse = Automatic(); limits = (0f0, 10f0), interval = (-Inf32, Inf32)) inverse isa Automatic && (inverse = inverse_transform(forward)) isnothing(inverse) && throw(ArgumentError( - "Cannot determine inverse transform: you can use `Makie.ReversibleScale($(forward), inverse($(forward)))` instead." + "Cannot determine inverse transform: you can use `ReversibleScale($(forward), inverse($(forward)))` instead." )) interval isa AbstractInterval || (interval = OpenInterval(Float32.(interval)...)) diff --git a/test/makielayout.jl b/test/makielayout.jl index 596251dbc01..54ebaec3f7c 100644 --- a/test/makielayout.jl +++ b/test/makielayout.jl @@ -378,10 +378,10 @@ end end @testset "ReversibleScale" begin - @test Makie.ReversibleScale(identity).inverse === identity - @test Makie.ReversibleScale(log).inverse === exp - @test_throws ArgumentError Makie.ReversibleScale(x -> log10(x)) # missing inverse scale - @test_throws ArgumentError Makie.ReversibleScale(sqrt, exp10) # incorrect inverse scale + @test ReversibleScale(identity).inverse === identity + @test ReversibleScale(log).inverse === exp + @test_throws ArgumentError ReversibleScale(x -> log10(x)) # missing inverse scale + @test_throws ArgumentError ReversibleScale(sqrt, exp10) # incorrect inverse scale end @testset "Invalid inverse transform" begin From 2e6c03d1df250b91662e530e336d4554acc6f130 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 29 Aug 2023 18:38:17 +0200 Subject: [PATCH 16/18] add `ReversibleScale` docs to `heatmap` --- docs/examples/plotting_functions/heatmap.md | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/examples/plotting_functions/heatmap.md b/docs/examples/plotting_functions/heatmap.md index fb6e8325774..989826c0eca 100644 --- a/docs/examples/plotting_functions/heatmap.md +++ b/docs/examples/plotting_functions/heatmap.md @@ -116,3 +116,25 @@ Colorbar(fig[:, end+1], colorrange = joint_limits) # equivalent fig ``` \end{examplefigure} + + +### Using a custom colorscale + +One can define a custom (color)scale using the `ReversibleScale` type. When the transformation is simple enough (`log`, `sqrt`, ...), the reverse transform is automatically deduced. + +\begin{examplefigure}{} +```julia +using CairoMakie +CairoMakie.activate!() # hide + +x = 10.0.^(1:0.1:4) +y = 1.0:0.1:5.0 +z = broadcast((x, y) -> x, x, y') + +scale = ReversibleScale(log10) +fig, ax, hm = heatmap(x, y, z; colorscale = scale, axis = (; xscale = scale)) +Colorbar(fig[1, 2], hm) + +fig +``` +\end{examplefigure} From 29f21bdd910f7a9d0a7c835c8449e6dbcd3c93e8 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Tue, 5 Sep 2023 13:30:19 +0200 Subject: [PATCH 17/18] update `contour` docs --- docs/reference/plots/contour.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/plots/contour.md b/docs/reference/plots/contour.md index c1aa8231645..4c9d8fd5bdd 100644 --- a/docs/reference/plots/contour.md +++ b/docs/reference/plots/contour.md @@ -57,8 +57,8 @@ x = y = range(-6, 6; length=100) z = himmelblau.(x, y') levels = 10.0.^range(0.3, 3.5; length=10) -colormap = Makie.sampler(:hsv, 100; scaling=Makie.Scaling(x -> x^(1 / 10), nothing)) -f, ax, ct = contour(x, y, z; labels=true, levels, colormap) +colorscale = ReversibleScale(x -> x^(1 / 10), x -> x^10) +f, ax, ct = contour(x, y, z; labels=true, levels, colormap=:hsv, colorscale) f ``` \end{examplefigure} From 07b26b0b5aadd199b380aef2ed2aa90ed49b3eee Mon Sep 17 00:00:00 2001 From: t-bltg Date: Wed, 6 Sep 2023 16:37:06 +0200 Subject: [PATCH 18/18] enhance docs example --- docs/reference/plots/heatmap.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/plots/heatmap.md b/docs/reference/plots/heatmap.md index 87c06e0c86a..a7302c53b7b 100644 --- a/docs/reference/plots/heatmap.md +++ b/docs/reference/plots/heatmap.md @@ -129,9 +129,9 @@ CairoMakie.activate!() # hide x = 10.0.^(1:0.1:4) y = 1.0:0.1:5.0 -z = broadcast((x, y) -> x, x, y') +z = broadcast((x, y) -> x - 10, x, y') -scale = ReversibleScale(log10) +scale = ReversibleScale(x -> asinh(x / 2) / log(10), x -> 2sinh(log(10) * x)) fig, ax, hm = heatmap(x, y, z; colorscale = scale, axis = (; xscale = scale)) Colorbar(fig[1, 2], hm)