From 0fb9fe5baec28c10195dc101f385f5c74ad1a1ce Mon Sep 17 00:00:00 2001 From: Simon Date: Tue, 12 Sep 2023 22:13:39 +0200 Subject: [PATCH 1/3] improve colorbar errors for plots that don't use a colormap (#3090) * improve colorbar errors for plots that don't use a colormap * don't mess with existing is_atomic_plot * improve errors * add tests * fix error * try a more generic approach * recursively search for colormap * implement categorical=true correctly * correctly get cgrad values * always use ColorMap * fix most cases?! * fiiiixies!? * improve ReversibleScale printing * one last bug? * fix cairomakie + Makie unit tests * fix contourf * allow type change for ticks * clean up implementation * add reference test * add test * update comment * fix broadcast * fix continous with values --- GLMakie/src/drawing_primitives.jl | 6 +- MakieCore/src/attributes.jl | 1 + .../src/tests/figures_and_makielayout.jl | 42 +- WGLMakie/src/lines.jl | 4 +- WGLMakie/src/meshes.jl | 2 +- docs/reference/blocks/colorbar.md | 5 +- docs/reference/plots/hexbin.md | 1 - src/basic_recipes/contourf.jl | 16 +- src/colorsampler.jl | 125 ++++-- src/layouting/transformation.jl | 5 +- src/makielayout/blocks/colorbar.jl | 361 ++++++++++-------- src/makielayout/blocks/legend.jl | 39 +- src/scenes.jl | 13 - src/types.jl | 13 +- test/makielayout.jl | 21 +- 15 files changed, 378 insertions(+), 276 deletions(-) diff --git a/GLMakie/src/drawing_primitives.jl b/GLMakie/src/drawing_primitives.jl index 3dd5476484a..5e048f45c48 100644 --- a/GLMakie/src/drawing_primitives.jl +++ b/GLMakie/src/drawing_primitives.jl @@ -72,9 +72,9 @@ end function handle_intensities!(attributes, plot) color = plot.calculated_colors - if color[] isa Makie.ColorMap + if color[] isa Makie.ColorMapping attributes[:intensity] = color[].color_scaled - interp = color[].categorical[] ? :nearest : :linear + interp = color[].color_mapping_type[] === Makie.continuous ? :linear : :nearest attributes[:color_map] = Texture(color[].colormap; minfilter=interp) attributes[:color_norm] = color[].colorrange_scaled attributes[:nan_color] = color[].nan_color @@ -417,7 +417,7 @@ function draw_atomic(screen::Screen, scene::Scene, heatmap::Heatmap) t = Makie.transform_func_obs(heatmap) mat = heatmap[3] space = heatmap.space # needs to happen before connect_camera! call - xypos = map(t, heatmap[1], heatmap[2], space) do t, x, y, space + xypos = lift(t, heatmap[1], heatmap[2], space) do t, x, y, space x1d = xy_convert(x, size(mat[], 1)) y1d = xy_convert(y, size(mat[], 2)) # Only if transform doesn't do anything, we can stay linear in 1/2D diff --git a/MakieCore/src/attributes.jl b/MakieCore/src/attributes.jl index b552e0d7ecd..2b30965c7fe 100644 --- a/MakieCore/src/attributes.jl +++ b/MakieCore/src/attributes.jl @@ -31,6 +31,7 @@ Attributes(pairs::AbstractVector) = Attributes(Dict{Symbol, Observable}(node_pai Attributes(pairs::Iterators.Pairs) = Attributes(collect(pairs)) Attributes(nt::NamedTuple) = Attributes(; nt...) attributes(x::Attributes) = getfield(x, :attributes) +attributes(x::AbstractPlot) = getfield(x, :attributes) Base.keys(x::Attributes) = keys(x.attributes) Base.values(x::Attributes) = values(x.attributes) function Base.iterate(x::Attributes, state...) diff --git a/ReferenceTests/src/tests/figures_and_makielayout.jl b/ReferenceTests/src/tests/figures_and_makielayout.jl index 9d3d24d5cee..98557769540 100644 --- a/ReferenceTests/src/tests/figures_and_makielayout.jl +++ b/ReferenceTests/src/tests/figures_and_makielayout.jl @@ -151,13 +151,13 @@ end 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 @@ -166,9 +166,9 @@ end @reference_test "PolarAxis decorations" begin f = Figure(resolution = (400, 400), backgroundcolor = :black) ax = PolarAxis( - f[1, 1], + f[1, 1], backgroundcolor = :black, - rminorgridvisible = true, rminorgridcolor = :red, + rminorgridvisible = true, rminorgridcolor = :red, rminorgridwidth = 1.0, rminorgridstyle = :dash, thetaminorgridvisible = true, thetaminorgridcolor = :blue, thetaminorgridwidth = 1.0, thetaminorgridstyle = :dash, @@ -179,7 +179,6 @@ end thetaticklabelsize = 18, thetaticklabelcolor = :blue, thetaticklabelstrokewidth = 1, thetaticklabelstrokecolor = :white, ) - f end @@ -192,3 +191,36 @@ end end f end + +@reference_test "Colorbar for recipes" begin + fig, ax, pl = barplot(1:3; color=1:3, colormap=Makie.Categorical(:viridis), figure=(;resolution=(800, 800))) + Colorbar(fig[1, 2], pl; size=100) + x = LinRange(-1, 1, 20) + y = LinRange(-1, 1, 20) + z = LinRange(-1, 1, 20) + values = [sin(x[i]) * cos(y[j]) * sin(z[k]) for i in 1:20, j in 1:20, k in 1:20] + + # TO not make this fail in CairoMakie, we dont actually plot the volume + _f, ax, cp = contour(x, y, z, values; levels=10, colormap=:viridis) + Colorbar(fig[2, :], cp; size=300) + + # horizontal colorbars + Colorbar(fig[1, 3][2, 1]; limits=(0, 10), colormap=:viridis, + vertical=false) + Colorbar(fig[1, 3][3, 1]; limits=(0, 5), size=25, + colormap=cgrad(:Spectral, 5; categorical=true), vertical=false) + Colorbar(fig[1, 3][4, 1]; limits=(-1, 1), colormap=:heat, vertical=false, flipaxis=false, + highclip=:cyan, lowclip=:red) + xs = LinRange(0, 20, 50) + ys = LinRange(0, 15, 50) + zs = [cos(x) * sin(y) for x in xs, y in ys] + ax, hm = contourf(fig[2, 3][1, 1], xs, ys, zs; + colormap=:Spectral, levels=[-1, -0.5, -0.25, 0, 0.25, 0.5, 1]) + Colorbar(fig[2, 3][1, 2], hm; ticks=-1:0.25:1) + + ax, hm = contourf(fig[3, :][1, 1], xs, ys, zs; + colormap=:Spectral, colorscale=sqrt, levels=[ 0, 0.25, 0.5, 1]) + Colorbar(fig[3, :][1, 2], hm; width=200) + + fig +end diff --git a/WGLMakie/src/lines.jl b/WGLMakie/src/lines.jl index f8fce2b5f68..bfdcfc6f158 100644 --- a/WGLMakie/src/lines.jl +++ b/WGLMakie/src/lines.jl @@ -38,7 +38,7 @@ function create_shader(scene::Scene, plot::Union{Lines,LineSegments}) linewidth = converted_attribute(plot, :linewidth) cmap = plot.calculated_colors[] - color = cmap isa Makie.ColorMap ? cmap.color_scaled : plot.calculated_colors + color = cmap isa Makie.ColorMapping ? cmap.color_scaled : plot.calculated_colors for (k, attribute) in [:linewidth => linewidth, :color => color] attribute = lift(attribute) do x @@ -52,7 +52,7 @@ function create_shader(scene::Scene, plot::Union{Lines,LineSegments}) uniforms[Symbol("$(k)_end")] = attribute else if attribute[] isa AbstractVector{<:Number} && k == :color - @assert cmap isa Makie.ColorMap + @assert cmap isa Makie.ColorMapping attribute = lift(Makie.numbers_to_colors, attribute, cmap.colormap, identity, cmap.colorrange_scaled, cmap.lowclip, cmap.highclip, diff --git a/WGLMakie/src/meshes.jl b/WGLMakie/src/meshes.jl index 83e314c1e33..55744ec005a 100644 --- a/WGLMakie/src/meshes.jl +++ b/WGLMakie/src/meshes.jl @@ -32,7 +32,7 @@ function handle_color!(plot, uniforms, buffers, uniform_color_name = :uniform_co uniforms[uniform_color_name] = Sampler(convert_text(color); minfilter=minfilter) elseif color[] isa AbstractMatrix uniforms[uniform_color_name] = Sampler(convert_text(color); minfilter=minfilter) - elseif color[] isa Makie.ColorMap + elseif color[] isa Makie.ColorMapping if color[].color_scaled[] isa AbstractVector buffers[:color] = Buffer(color[].color_scaled) else diff --git a/docs/reference/blocks/colorbar.md b/docs/reference/blocks/colorbar.md index b2fe0561463..568d5eaa905 100644 --- a/docs/reference/blocks/colorbar.md +++ b/docs/reference/blocks/colorbar.md @@ -1,5 +1,3 @@ - - # Colorbar A Colorbar needs a colormap and a tuple of low/high limits. @@ -67,7 +65,6 @@ fig ``` \end{examplefigure} - ## Attributes -\attrdocs{Colorbar} \ No newline at end of file +\attrdocs{Colorbar} diff --git a/docs/reference/plots/hexbin.md b/docs/reference/plots/hexbin.md index 9c3c56d56f9..0541ad5c15c 100644 --- a/docs/reference/plots/hexbin.md +++ b/docs/reference/plots/hexbin.md @@ -198,7 +198,6 @@ tightlimits!(ax) Colorbar(f[1, 2], hb, label = "Number of airports", - ticks = (0:3, ["0", "10", "100", "1000"]), height = Relative(0.5) ) f diff --git a/src/basic_recipes/contourf.jl b/src/basic_recipes/contourf.jl index 4da445fc29f..d2e0d7a8f55 100644 --- a/src/basic_recipes/contourf.jl +++ b/src/basic_recipes/contourf.jl @@ -48,27 +48,24 @@ end # _computed_extendlow # _computed_extendhigh -function _get_isoband_levels(levels::Int, mi, ma) - edges = Float32.(LinRange(mi, ma, levels+1)) -end +_get_isoband_levels(levels::Int, mi, ma) = Float32.(LinRange(mi, ma, levels+1)) function _get_isoband_levels(levels::AbstractVector{<:Real}, mi, ma) edges = Float32.(levels) @assert issorted(edges) edges end - -conversion_trait(::Type{<:Contourf}) = ContinuousSurface() - function _get_isoband_levels(::Val{:normal}, levels, values) - _get_isoband_levels(levels, extrema_nan(values)...) + return _get_isoband_levels(levels, extrema_nan(values)...) end function _get_isoband_levels(::Val{:relative}, levels::AbstractVector, values) mi, ma = extrema_nan(values) - Float32.(levels .* (ma - mi) .+ mi) + return Float32.(levels .* (ma - mi) .+ mi) end +conversion_trait(::Type{<:Contourf}) = ContinuousSurface() + function Makie.plot!(c::Contourf{<:Tuple{<:AbstractVector{<:Real}, <:AbstractVector{<:Real}, <:AbstractMatrix{<:Real}}}) xs, ys, zs = c[1:3] @@ -93,7 +90,6 @@ function Makie.plot!(c::Contourf{<:Tuple{<:AbstractVector{<:Real}, <:AbstractVec map!(compute_highcolor, c, highcolor, c.extendhigh, c.colormap) c.attributes[:_computed_extendhigh] = highcolor is_extended_high = lift(!isnothing, c, highcolor) - PolyType = typeof(Polygon(Point2f[], [Point2f[]])) polys = Observable(PolyType[]) @@ -161,9 +157,7 @@ inner polygons which are holes in the outer polygon. It is possible that one group has multiple outer polygons with multiple holes each. """ function _group_polys(points, ids) - polys = [points[ids .== i] for i in unique(ids)] - npolys = length(polys) polys_lastdouble = [push!(p, first(p)) for p in polys] diff --git a/src/colorsampler.jl b/src/colorsampler.jl index 47a16029f98..92e1c7d2eba 100644 --- a/src/colorsampler.jl +++ b/src/colorsampler.jl @@ -176,10 +176,27 @@ function numbers_to_colors(numbers::Union{AbstractArray{<:Number, N},Number}, end end -struct ColorMap{N,T<:AbstractArray{<:Number,N},T2<:AbstractArray{<:Number,N}} +""" + ColorMappingType + +* categorical: there are n categories, and n colors are assigned to each category +* banded: there are ranges edge_start..edge_end, inside which values are mapped to one color +* continous: colors are mapped continuously to values +""" +@enum ColorMappingType categorical banded continuous + + +struct ColorMapping{N,T<:AbstractArray{<:Number,N},T2<:AbstractArray{<:Number,N}} + # The pure color values from the plot this colormapping is associated to + # Will be always an array of numbers color::Observable{T} colormap::Observable{Vector{RGBAf}} - scale::Observable{Union{ReversibleScale, Function}} + raw_colormap::Observable{Vector{RGBAf}} # the above is scaled (when coming from cgrad), this is not + + # Scaling function that gets applied to color + scale::Observable{Function} + + # The 0-1 scaled values from crange, which describe the colormapping mapping::Observable{Union{Nothing, Vector{Float64}}} colorrange::Observable{Vec{2,Float64}} @@ -187,77 +204,115 @@ struct ColorMap{N,T<:AbstractArray{<:Number,N},T2<:AbstractArray{<:Number,N}} highclip::Observable{Union{Automatic, RGBAf}} # Defaults to last color in colormap nan_color::Observable{RGBAf} - categorical::Observable{Bool} + color_mapping_type::Observable{ColorMappingType} # scaled attributes colorrange_scaled::Observable{Vec2f} color_scaled::Observable{T2} end -function assemble_colors(::T, @nospecialize(color), @nospecialize(plot)) where {N, T<:AbstractArray{<:Number, N}} - color_tight = convert(Observable{T}, color) - colormap = Observable(RGBAf[]; ignore_equal_values=true) - categorical = Observable(false) - colorscale = convert(Observable{Union{ReversibleScale, Function}}, plot.colorscale) - mapping = Observable{Union{Nothing, Vector{Float64}}}(nothing) +struct Categorical{T} <: AbstractVector{RGBAf} + values::Vector{T} +end +Categorical(values) = Categorical(to_colormap(values)) +Base.getindex(c::Categorical, i) = c.values[i] +Base.size(c::Categorical) = size(c.values) + +_array_value_type(::Categorical{T}) where T = Vector{T} +_array_value_type(A::AbstractArray{<:Number}) = typeof(A) +_array_value_type(r::AbstractRange) = Vector{eltype(r)} # use vector instead, to have a few less types to worry about + +_to_colormap(x::PlotUtils.ColorGradient) = to_colormap(x.colors) +_to_colormap(x) = to_colormap(x) + + +colormapping_type(@nospecialize(colormap)) = continuous +colormapping_type(::PlotUtils.CategoricalColorGradient) = banded +colormapping_type(::Categorical) = categorical + +function ColorMapping( + color::AbstractArray{<:Number, N}, colors_obs, colormap, colorrange, + colorscale, alpha, lowclip, highclip, nan_color, + color_mapping_type=lift(colormapping_type, colormap; ignore_equal_values=true)) where {N} + + T = _array_value_type(color) + color_tight = convert(Observable{T}, colors_obs) + map_colors = Observable(RGBAf[]; ignore_equal_values=true) + raw_colormap = Observable(RGBAf[]; ignore_equal_values=true) + mapping = Observable{Union{Nothing,Vector{Float64}}}(nothing; ignore_equal_values=true) + colorscale = convert(Observable{Function}, colorscale) function update_colors(cmap, a) colors = to_colormap(cmap) + raw_colors = _to_colormap(cmap) # dont do the scaling from cgrad if a < 1.0 - colors = map(c -> RGBAf(Colors.color(c), alpha(c) * a), colors) + colors = map(c -> RGBAf(Colors.color(c), Colors.alpha(c) * a), colors) + raw_colors = map(c -> RGBAf(Colors.color(c), Colors.alpha(c) * a), raw_colors) end - colormap[] = colors - categorical[] = cmap isa PlotUtils.CategoricalColorGradient - if colormap isa PlotUtils.ColorGradient + map_colors[] = colors + raw_colormap[] = raw_colors + if cmap isa PlotUtils.ColorGradient mapping[] = cmap.values end return end - onany(update_colors, plot, plot.colormap, plot.alpha) - update_colors(plot.colormap[], plot.alpha[]) - lowclip = Observable{Union{Automatic,RGBAf}}(automatic; ignore_equal_values=true) - on(plot, plot.lowclip; update=true) do lc - lowclip[] = lc isa Automatic ? lc : to_color(lc) + onany(update_colors, colormap, alpha) + update_colors(colormap[], alpha[]) + + _lowclip = Observable{Union{Automatic,RGBAf}}(automatic; ignore_equal_values=true) + on(lowclip; update=true) do lc + _lowclip[] = lc isa Union{Nothing, Automatic} ? automatic : to_color(lc) return end - highclip = Observable{Union{Automatic,RGBAf}}(automatic; ignore_equal_values=true) - on(plot, plot.highclip; update=true) do hc - highclip[] = hc isa Automatic ? hc : to_color(hc) + _highclip = Observable{Union{Automatic,RGBAf}}(automatic; ignore_equal_values=true) + on(highclip; update=true) do hc + _highclip[] = hc isa Union{Nothing,Automatic} ? automatic : to_color(hc) return end - nan_color = lift(to_color, plot.nan_color) - colorrange = Observable(Vec{2, Float64}(0); ignore_equal_values=true) - colorrange = lift(color_tight, plot.colorrange; ignore_equal_values=true) do color, crange + colorrange = lift(color_tight, colorrange; ignore_equal_values=true) do color, crange return crange isa Automatic ? Vec2{Float64}(distinct_extrema_nan(color)) : Vec2{Float64}(crange) end colorrange_scaled = lift(colorrange, colorscale; ignore_equal_values=true) do range, scale return Vec2f(apply_scale(scale, range)) end + color_scaled = lift(color_tight, colorscale) do color, scale return el32convert(apply_scale(scale, color)) end - return ColorMap{N, T, typeof(color_scaled[])}( + CT = ColorMapping{N,T,typeof(color_scaled[])} + + return CT( color_tight, - colormap, + map_colors, + raw_colormap, colorscale, mapping, colorrange, - lowclip, - highclip, - nan_color, - categorical, + _lowclip, + _highclip, + lift(to_color, nan_color), + color_mapping_type, colorrange_scaled, - color_scaled - ) + color_scaled) +end + +function assemble_colors(c::AbstractArray{<:Number}, @nospecialize(color), @nospecialize(plot)) + return ColorMapping(c, color, plot.colormap, plot.colorrange, plot.colorscale, plot.alpha, plot.lowclip, + plot.highclip, plot.nan_color) end -function to_color(c::ColorMap) +function to_color(c::ColorMapping) return numbers_to_colors(c.color_scaled[], c.colormap[], identity, c.colorrange_scaled[], lowclip(c)[], highclip(c)[], c.nan_color[]) end +function Base.get(c::ColorMapping, value::Number) + return numbers_to_colors([value], c.colormap[], c.scale[], c.colorrange_scaled[], lowclip(c)[], + highclip(c)[], c.nan_color[])[1] +end + function assemble_colors(colortype, color, plot) return lift(plot, color, plot.alpha) do color, a if a < 1.0 @@ -276,5 +331,5 @@ function assemble_colors(::Number, color, plot) cm.nan_color) end -highclip(cmap::ColorMap) = lift((cm, hc) -> hc isa Automatic ? last(cm) : hc, cmap.colormap, cmap.highclip) -lowclip(cmap::ColorMap) = lift((cm, hc) -> hc isa Automatic ? first(cm) : hc, cmap.colormap, cmap.lowclip) +highclip(cmap::ColorMapping) = lift((cm, hc) -> hc isa Automatic ? last(cm) : hc, cmap.colormap, cmap.highclip) +lowclip(cmap::ColorMapping) = lift((cm, hc) -> hc isa Automatic ? first(cm) : hc, cmap.colormap, cmap.lowclip) diff --git a/src/layouting/transformation.jl b/src/layouting/transformation.jl index 897c49e5576..bf8ed3c03e1 100644 --- a/src/layouting/transformation.jl +++ b/src/layouting/transformation.jl @@ -362,7 +362,8 @@ 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); - limits=(0f0, 3f0) + limits=(0f0, 3f0), + name=:pseudolog10 ) Symlog10(hi) = Symlog10(-hi, hi) @@ -383,7 +384,7 @@ function Symlog10(lo, hi) else x end - ReversibleScale(forward, inverse; limits=(0f0, 3f0)) + return ReversibleScale(forward, inverse; limits=(0.0f0, 3.0f0), name=:Symlog10) end inverse_transform(::typeof(identity)) = identity diff --git a/src/makielayout/blocks/colorbar.jl b/src/makielayout/blocks/colorbar.jl index 266d6efb049..75b1a34a617 100644 --- a/src/makielayout/blocks/colorbar.jl +++ b/src/makielayout/blocks/colorbar.jl @@ -25,90 +25,89 @@ function colorbar_check(keys, kwargs_keys) end end -function Colorbar(fig_or_scene, plot::AbstractPlot; kwargs...) - colorbar_check((:colormap, :limits, :highclip, :lowclip), keys(kwargs)) - - if haskey(plot, :calculated_colors) && plot.calculated_colors[] isa ColorMap - cmap = plot.calculated_colors[] - scale = cmap.scale +function extract_colormap(@nospecialize(plot::AbstractPlot)) + has_colorrange = haskey(plot, :colorrange) && !(plot.colorrange[] isa Makie.Automatic) + if haskey(plot, :calculated_colors) && plot.calculated_colors[] isa Makie.ColorMapping + return plot.calculated_colors[] + elseif has_colorrange && all(x -> haskey(plot, x), [:colormap, :colorrange, :color]) && plot.color[] isa AbstractVector{<:Colorant} + return ColorMapping( + plot.color[], plot.color, plot.colormap, plot.colorrange, + get(plot, :colorscale, Observable(identity)), + get(plot, :alpha, Observable(1.0)), + get(plot, :highclip, Observable(automatic)), + get(plot, :lowclip, Observable(automatic)), + get(plot, :nan_color, Observable(RGBAf(0,0,0,0))), + ) else - cmap = plot - scale = plot.colorscale + return nothing end +end - Colorbar( - fig_or_scene; - colormap=cmap.colormap, - limits=cmap.colorrange, - scale=scale, - highclip=cmap.highclip, - lowclip=cmap.lowclip, - kwargs... - ) +function extract_colormap(plot::StreamPlot) + return extract_colormap(plot.plots[1]) end -function Colorbar(fig_or_scene, contourf::Union{Contourf, Tricontourf}; kwargs...) - colorbar_check((:colormap, :limits, :highclip, :lowclip), keys(kwargs)) +function extract_colormap(plot::Union{Contourf,Tricontourf}) + levels = plot._computed_levels + limits = lift(l -> (l[1], l[end]), levels) + function extend_color(color, computed) + color === nothing && return automatic + color == :auto || color == automatic && return computed + return computed + end + elow = lift(extend_color, plot.extendlow, plot._computed_extendlow) + ehigh = lift(extend_color, plot.extendhigh, plot._computed_extendhigh) + return ColorMapping(levels[], levels, plot._computed_colormap, limits, plot.colorscale, Observable(1.0), + elow, ehigh, plot.nan_color) +end - steps = contourf._computed_levels - limits = lift(steps) do steps - steps[1], steps[end] +function extract_colormap_recursive(@nospecialize(plot::T)) where {T <: AbstractPlot} + cmap = extract_colormap(plot) + if !isnothing(cmap) + return cmap + else + colormaps = [extract_colormap_recursive(child) for child in plot.plots] + if length(colormaps) == 1 + return colormaps[1] + elseif isempty(colormaps) + return nothing + else + # Prefer ColorMapping if in doubt! + cmaps = filter(x-> x isa ColorMapping, colormaps) + length(cmaps) == 1 && return cmaps[1] + error("Multiple colormaps found for plot $(plot), please specify which one to use manually. Please overload `Makie.extract_colormap(::$(T))` to allow for the automatical creation of a Colorbar.") + end end - - Colorbar( - fig_or_scene; - colormap = contourf._computed_colormap, - limits = limits, - lowclip = contourf._computed_extendlow, - highclip = contourf._computed_extendhigh, - scale = contourf.colorscale, - kwargs... - ) end -function Colorbar(fig_or_scene, voronoi::Voronoiplot; kwargs...) +function Colorbar(fig_or_scene, plot::AbstractPlot; kwargs...) colorbar_check((:colormap, :limits, :highclip, :lowclip), keys(kwargs)) - - # switch to lowered version - if voronoi.plots[1] isa Voronoiplot - voronoi = voronoi.plots[1] + cmap = extract_colormap_recursive(plot) + func = plotfunc(plot) + if isnothing(cmap) + error("Neither $(func) nor any of its children use a colormap. Cannot create a Colorbar from this plot, please create it manually. + If this is a recipe, one needs to overload `Makie.extract_colormap(::$(Combined{func}))` to allow for the automatical creation of a Colorbar.") + end + if !(cmap isa ColorMapping) + error("extract_colormap(::$(Combined{func})) returned an invalid value: $cmap. Needs to return either a `Makie.ColorMapping`.") end - range = map(voronoi._calculated_colors, voronoi.colorrange) do cs, cr - cr === automatic ? extrema(cs) : cr + if to_value(cmap.color) isa Union{AbstractVector{<: Colorant}, Colorant} + error("""Plot $(func)'s color attribute uses colors directly, so it can't be used to create a Colorbar, since no numbers are mapped to a color via the colormap. + Please create the colorbar manually e.g. via `Colorbar(f[1, 2], colorrange=the_range, colormap=the_colormap)`.. + """) end - Colorbar( + return Colorbar( fig_or_scene; - colormap=voronoi.colormap, - limits=range, - scale=voronoi.colorscale, - highclip=voronoi.highclip, - lowclip=voronoi.lowclip, + colormap=cmap, kwargs... ) end -function colorbar_range(start, stop, length, colorscale) - colorscale === identity && return LinRange(start, stop, length) - - inverse = inverse_transform(colorscale) - isnothing(inverse) && throw(ArgumentError( - "Cannot determine inverse transform: you can use `ReversibleScale($(colorscale), inverse($(colorscale)))` instead." - )) - - inverse.(range(start, stop; length)) -end - function initialize_block!(cb::Colorbar) blockscene = cb.blockscene - limits = lift(blockscene, cb.limits, cb.colorrange) do limits, colorrange - if all(!isnothing, (limits, colorrange)) - error("Both colorrange + limits are set, please only set one, they're aliases. colorrange: $(colorrange), limits: $(limits)") - end - return something(limits, colorrange, (0, 1)) - end onany(blockscene, cb.size, cb.vertical) do sz, vertical if vertical @@ -120,131 +119,140 @@ function initialize_block!(cb::Colorbar) framebox = lift(round_to_IRect2D, blockscene, cb.layoutobservables.computedbbox) - cgradient = Observable{PlotUtils.ColorGradient}() - map!(blockscene, cgradient, cb.colormap) do cmap - if cmap isa PlotUtils.ColorGradient - # if we have a colorgradient directly, we want to keep it intact - # to enable correct categorical colormap behavior etc - return cmap - else - # this is a bit weird, first convert to a vector of colors, - # then use cgrad, but at least I can use `get` on that later - converted = Makie.to_colormap(cmap) - return cgrad(converted) + # TODO, always convert to ColorMapping! + if cb.colormap[] isa ColorMapping + cmap = cb.colormap[] + else + # Old way without Colormapping. We keep it, to be able to create a colormap directly + limits = lift(blockscene, cb.limits, cb.colorrange) do limits, colorrange + if all(!isnothing, (limits, colorrange)) + error("Both colorrange + limits are set, please only set one, they're aliases. colorrange: $(colorrange), limits: $(limits)") + end + return something(limits, colorrange, (0, 1)) end + alpha = Observable(1.0) # dont have these as fields in Colorbar + nan_color = Observable(RGBAf(0, 0, 0, 0)) + cmap = ColorMapping( + Float64[], Observable(Float64[]), cb.colormap, limits, + cb.scale, alpha, cb.lowclip, cb.highclip, nan_color) end - function isvisible(x, compare) - x isa Automatic && return false - x isa Nothing && return false - c = to_color(x) - alpha(c) <= 0.0 && return false - return c != compare - end - - lowclip_tri_visible = lift(isvisible, blockscene, cb.lowclip, lift(x-> get(x, 0), blockscene, cgradient)) - highclip_tri_visible = lift(isvisible, blockscene, cb.highclip, lift(x-> get(x, 1), blockscene, cgradient)) - - tri_heights = lift(blockscene, highclip_tri_visible, lowclip_tri_visible, framebox) do hv, lv, box - if cb.vertical[] - (lv * width(box), hv * width(box)) + colormap = lift(cmap.raw_colormap, cmap.colormap, cmap.mapping) do rcm, cm, mapping + if isnothing(mapping) + return rcm else - (lv * height(box), hv * height(box)) - end .* sin(pi/3) + # if there is a mapping, we want to apply it to the colormap, which is already done for cmap.colormap (by calling to_colormap(cgrad(...))) + # In the future, we may want to use cmap.mapping to do this ourselves + return cm + end end - - barsize = lift(blockscene, tri_heights) do heights - if cb.vertical[] - max(1, height(framebox[]) - sum(heights)) + limits = cmap.colorrange + colors = lift(blockscene, cmap.mapping, cmap.color_mapping_type, cmap.color, cb.nsteps, limits; + ignore_equal_values=true) do mapping, mapping_type, values, n, limits + if mapping === nothing + if mapping_type === Makie.banded + error("Banded without a mapping is invalid. Please use colormap=cgrad(...; categorical=true)") + elseif mapping_type === Makie.categorical + return convert(Vector{Float64},1:length(unique(values))) + else + return convert(Vector{Float64}, LinRange(limits..., n)) + end else - max(1, width(framebox[]) - sum(heights)) + if mapping_type === Makie.categorical + # This is because cmap.mapping comes from cgrad.values, which doesn't encode categorical colormapping correctly + error("Mapping should not be used for categorical colormaps") + end + if mapping_type === Makie.continuous + # we need at least nsteps, to correctly sample from the colormap (which has the mapping applied already) + return convert(Vector{Float64}, LinRange(limits..., n)) + else + # Mapping is always 0..1, but color should be scaled + return limits[1] .+ (mapping .* (limits[2] - limits[1])) + end + return end end - barbox = lift(blockscene, barsize) do sz - fbox = framebox[] + lowclip_tri_visible = lift(x -> !(x isa Automatic), blockscene, cmap.lowclip; ignore_equal_values=true) + highclip_tri_visible = lift(x -> !(x isa Automatic), blockscene, cmap.highclip; ignore_equal_values=true) + tri_heights = lift(blockscene, highclip_tri_visible, lowclip_tri_visible, framebox; ignore_equal_values=true) do hv, lv, box if cb.vertical[] - BBox(left(fbox), right(fbox), bottom(fbox) + tri_heights[][1], top(fbox) - tri_heights[][2]) + return (lv * width(box), hv * width(box)) else - BBox(left(fbox) + tri_heights[][1], right(fbox) - tri_heights[][2], bottom(fbox), top(fbox)) - end + return (lv * height(box), hv * height(box)) + end .* sin(pi / 3) end - - map_is_categorical = lift(x -> x isa PlotUtils.CategoricalColorGradient, blockscene, cgradient) - - steps = lift(blockscene, cgradient, cb.nsteps, cb.scale) do cgradient, n, scale - if cgradient isa PlotUtils.CategoricalColorGradient - cgradient.values + barbox = lift(blockscene, framebox; ignore_equal_values=true) do fbox + if cb.vertical[] + return BBox(left(fbox), right(fbox), bottom(fbox) + tri_heights[][1], top(fbox) - tri_heights[][2]) else - collect(colorbar_range(0, 1, n, scale)) - end::Vector{Float64} + return BBox(left(fbox) + tri_heights[][1], right(fbox) - tri_heights[][2], bottom(fbox), top(fbox)) + end end - # it's hard to draw categorical and continous colormaps well - # with the same primitives - - # therefore we make one interpolated image for continous - # colormaps and number of polys for categorical colormaps - # at the same time, then we just set one of them invisible - # depending on the type of colormap - # this should solve most white-line issues - - # for categorical colormaps we make a number of rectangle polys - rects_and_colors = lift(blockscene, barbox, cb.vertical, steps, cgradient, cb.scale, - limits) do bbox, v, steps, gradient, scale, lims - - # we need to convert the 0 to 1 steps into rescaled 0 to 1 steps given the - # colormap's `scale` attribute + xrange = Observable(Float32[]; ignore_equal_values=true) + yrange = Observable(Float32[]; ignore_equal_values=true) - s_scaled = scaled_steps(steps, scale, lims) - - xmin, ymin = minimum(bbox) - xmax, ymax = maximum(bbox) - - rects = if v - yvals = s_scaled .* (ymax - ymin) .+ ymin - [BBox(xmin, xmax, b, t) - for (b, t) in zip(yvals[1:end-1], yvals[2:end])] + function update_xyrange(bb, v, colors, scale, mapping_type) + xmin, ymin = minimum(bb) + xmax, ymax = maximum(bb) + if mapping_type == Makie.categorical + colors = edges(colors) + end + s_scaled = scale.(colors) + mini, maxi = extrema(s_scaled) + s_scaled = (s_scaled .- mini) ./ (maxi - mini) + if v + xrange[] = LinRange(xmin, xmax, 2) + yrange[] = s_scaled .* (ymax - ymin) .+ ymin else - xvals = s_scaled .* (xmax - xmin) .+ xmin - [BBox(l, r, ymin, ymax) - for (l, r) in zip(xvals[1:end-1], xvals[2:end])] + xrange[] = s_scaled .* (xmax - xmin) .+ xmin + yrange[] = LinRange(ymin, ymax, 2) end - - colors = get.(Ref(gradient), (steps[1:end-1] .+ steps[2:end]) ./2) - rects, colors + return end - colors = lift(x -> getindex(x, 2), blockscene, rects_and_colors) - rects = poly!(blockscene, - lift(x -> getindex(x, 1), blockscene, rects_and_colors); - color = colors, - visible = map_is_categorical, - inspectable = false - ) + update_xyrange(barbox[], cb.vertical[], colors[], cmap.scale[], cmap.color_mapping_type[]) + onany(update_xyrange, blockscene, barbox, cb.vertical, colors, cmap.scale, cmap.color_mapping_type) # for continous colormaps we sample a 1d image # to avoid white lines when rendering vector graphics - - continous_pixels = lift(blockscene, cb.vertical, cb.nsteps, cgradient, limits, - cb.scale) do v, n, grad, lims, scale - - s_steps = scaled_steps(colorbar_range(0, 1, n, scale), scale, lims) - px = get.(Ref(grad), s_steps) - return v ? reshape(px, 1, n) : reshape(px, n, 1) + continous_pixels = lift(blockscene, cb.vertical, colors, + cmap.color_mapping_type) do v, colors, mapping_type + if mapping_type !== Makie.categorical + colors = (colors[1:end-1] .+ colors[2:end]) ./2 + end + n = length(colors) + return v ? reshape((colors), 1, n) : reshape((colors), n, 1) + end + # TODO, implement interpolate = true for irregular grics in CairoMakie + # Then, we can just use heatmap! and don't need the image plot! + show_cats = Observable(false; ignore_equal_values=true) + show_continous = Observable(false; ignore_equal_values=true) + on(blockscene, cmap.color_mapping_type; update=true) do type + if type === continuous + show_continous[] = true + show_cats[] = false + else + show_continous[] = false + show_cats[] = true + end end - cont_image = image!(blockscene, - lift(bb -> range(left(bb), right(bb); length=2), blockscene, barbox), - lift(bb -> range(bottom(bb), top(bb); length=2), blockscene, barbox), - continous_pixels, - visible=lift(!, blockscene, map_is_categorical), - interpolate = true, + heatmap!(blockscene, + xrange, yrange, continous_pixels; + colormap=colormap, + visible=show_cats, + inspectable=false + ) + image!(blockscene, + lift(x-> LinRange(extrema(x)..., 2), xrange), lift(y-> LinRange(extrema(y)..., 2), yrange), continous_pixels; + colormap = colormap, + visible = show_continous, inspectable = false ) - highclip_tri = lift(blockscene, barbox, cb.spinewidth) do box, spinewidth if cb.vertical[] lb, rb = topline(box) @@ -259,11 +267,11 @@ function initialize_block!(cb::Colorbar) end end - highclip_tri_color = lift(blockscene, cb.highclip) do hc + highclip_tri_color = lift(blockscene, cmap.highclip) do hc to_color(hc isa Automatic || isnothing(hc) ? :transparent : hc) end - highclip_tri_poly = poly!(blockscene, highclip_tri, color = highclip_tri_color, + poly!(blockscene, highclip_tri, color = highclip_tri_color, strokecolor = :transparent, visible = highclip_tri_visible, inspectable = false) @@ -281,11 +289,11 @@ function initialize_block!(cb::Colorbar) end end - lowclip_tri_color = lift(blockscene, cb.lowclip) do lc + lowclip_tri_color = lift(blockscene, cmap.lowclip) do lc to_color(lc isa Automatic || isnothing(lc) ? :transparent : lc) end - lowclip_tri_poly = poly!(blockscene, lowclip_tri, color = lowclip_tri_color, + poly!(blockscene, lowclip_tri, color = lowclip_tri_color, strokecolor = :transparent, visible = lowclip_tri_visible, inspectable = false) @@ -336,11 +344,22 @@ function initialize_block!(cb::Colorbar) end + ticks = Observable{Any}() + map!(ticks, colors, cmap.color_mapping_type, cb.ticks) do cs, type, ticks + # For categorical we just enumerate + type === Makie.categorical ? (1:length(cs), string.(cs)) : ticks + end + + lims = lift(colors, cmap.color_mapping_type, limits) do cs, type, limits + return type === Makie.categorical ? (0.5, length(cs) + 0.5) : limits + end + axis = LineAxis(blockscene, endpoints = axispoints, flipped = cb.flipaxis, - limits = limits, ticklabelalign = cb.ticklabelalign, label = cb.label, + limits=lims, ticklabelalign=cb.ticklabelalign, label=cb.label, labelpadding = cb.labelpadding, labelvisible = cb.labelvisible, labelsize = cb.labelsize, labelcolor = cb.labelcolor, labelrotation = cb.labelrotation, - labelfont = cb.labelfont, ticklabelfont = cb.ticklabelfont, ticks = cb.ticks, tickformat = cb.tickformat, + labelfont=cb.labelfont, ticklabelfont=cb.ticklabelfont, ticks=ticks, + tickformat=cb.tickformat, ticklabelsize = cb.ticklabelsize, ticklabelsvisible = cb.ticklabelsvisible, ticksize = cb.ticksize, ticksvisible = cb.ticksvisible, ticklabelpad = cb.ticklabelpad, tickalign = cb.tickalign, ticklabelrotation = cb.ticklabelrotation, @@ -349,15 +368,13 @@ function initialize_block!(cb::Colorbar) spinecolor = :transparent, spinevisible = :false, flip_vertical_label = cb.flip_vertical_label, minorticksvisible = cb.minorticksvisible, minortickalign = cb.minortickalign, minorticksize = cb.minorticksize, minortickwidth = cb.minortickwidth, - minortickcolor = cb.minortickcolor, minorticks = cb.minorticks, scale = cb.scale) + minortickcolor = cb.minortickcolor, minorticks = cb.minorticks, scale = cmap.scale) cb.axis = axis - onany(blockscene, axis.protrusion, cb.vertical, cb.flipaxis) do axprotrusion, vertical, flipaxis - left, right, top, bottom = 0f0, 0f0, 0f0, 0f0 if vertical @@ -379,9 +396,15 @@ function initialize_block!(cb::Colorbar) # trigger protrusions with one of the attributes notify(cb.vertical) - + # We set everything via the ColorMapping now. To be backwards compatible, we always set those fields: + setfield!(cb, :limits, convert(Observable{Any}, limits)) + setfield!(cb, :colormap, convert(Observable{Any}, cmap.colormap)) + setfield!(cb, :highclip, convert(Observable{Any}, cmap.highclip)) + setfield!(cb, :lowclip, convert(Observable{Any}, cmap.lowclip)) + setfield!(cb, :scale, convert(Observable{Any}, cmap.scale)) # trigger bbox notify(cb.layoutobservables.suggestedbbox) + notify(barbox) return end @@ -399,5 +422,5 @@ function scaled_steps(steps, scale, lims) # normalize to lims range steps_lim_scaled = @. steps_scaled * (scale(lims[2]) - scale(lims[1])) + scale(lims[1]) # then rescale to 0 to 1 - @. (steps_lim_scaled - steps_lim_scaled[begin]) / (steps_lim_scaled[end] - steps_lim_scaled[begin]) + return @. (steps_lim_scaled - steps_lim_scaled[begin]) / (steps_lim_scaled[end] - steps_lim_scaled[begin]) end diff --git a/src/makielayout/blocks/legend.jl b/src/makielayout/blocks/legend.jl index d711ce48338..8d995e774f6 100644 --- a/src/makielayout/blocks/legend.jl +++ b/src/makielayout/blocks/legend.jl @@ -257,10 +257,10 @@ function connect_block_layoutobservables!(leg::Legend, layout_width, layout_heig end + function legendelement_plots!(scene, element::MarkerElement, bbox::Observable{Rect2f}, defaultattrs::Attributes) merge!(element.attributes, defaultattrs) attrs = element.attributes - fracpoints = attrs.markerpoints points = lift((bb, fp) -> fractionpoint.(Ref(bb), fp), scene, bbox, fracpoints) scat = scatter!(scene, points, color = attrs.markercolor, marker = attrs.marker, @@ -390,44 +390,43 @@ function _rename_attributes!(T, a) a end +choose_scalar(attr, default) = is_scalar_attribute(to_value(attr)) ? attr : default -function scalar_lift(plot, attr, default) - observable = Observable{Any}() - map!(plot, observable, attr, default) do at, def - Makie.is_scalar_attribute(at) ? at : def - end - return observable +function extract_color(@nospecialize(plot), color_default) + color = haskey(plot, :calculated_color) ? plot.calculated_color : plot.color + color[] isa ColorMapping && return color_default + return choose_scalar(color, color_default) end function legendelements(plot::Union{Lines, LineSegments}, legend) LegendElement[LineElement( - color = scalar_lift(plot, plot.color, legend.linecolor), - linestyle = scalar_lift(plot, plot.linestyle, legend.linestyle), - linewidth = scalar_lift(plot, plot.linewidth, legend.linewidth))] + color = extract_color(plot, legend.linecolor), + linestyle = choose_scalar(plot.linestyle, legend.linestyle), + linewidth = choose_scalar(plot.linewidth, legend.linewidth))] end - function legendelements(plot::Scatter, legend) LegendElement[MarkerElement( - color = scalar_lift(plot, plot.color, legend.markercolor), - marker = scalar_lift(plot, plot.marker, legend.marker), - markersize = scalar_lift(plot, plot.markersize, legend.markersize), - strokewidth = scalar_lift(plot, plot.strokewidth, legend.markerstrokewidth), - strokecolor = scalar_lift(plot, plot.strokecolor, legend.markerstrokecolor), + color = extract_color(plot, legend.markercolor), + marker = choose_scalar(plot.marker, legend.marker), + markersize = choose_scalar(plot.markersize, legend.markersize), + strokewidth = choose_scalar(plot.strokewidth, legend.markerstrokewidth), + strokecolor = choose_scalar(plot.strokecolor, legend.markerstrokecolor), )] end function legendelements(plot::Union{Poly, Violin, BoxPlot, CrossBar, Density}, legend) + color = extract_color(plot, legend.polycolor) LegendElement[PolyElement( - color = scalar_lift(plot, plot.color, legend.polycolor), - strokecolor = scalar_lift(plot, plot.strokecolor, legend.polystrokecolor), - strokewidth = scalar_lift(plot, plot.strokewidth, legend.polystrokewidth), + color = color, + strokecolor = choose_scalar(plot.strokecolor, legend.polystrokecolor), + strokewidth = choose_scalar(plot.strokewidth, legend.polystrokewidth), )] end function legendelements(plot::Band, legend) # there seems to be no stroke for Band, so we set it invisible - LegendElement[PolyElement(polycolor = scalar_lift(plot, plot.color, legend.polystrokecolor), polystrokecolor = :transparent, polystrokewidth = 0)] + LegendElement[PolyElement(polycolor = choose_scalar(plot.color, legend.polystrokecolor), polystrokecolor = :transparent, polystrokewidth = 0)] end # if there is no specific overload available, we go through the child plots and just stack diff --git a/src/scenes.jl b/src/scenes.jl index 9766a840978..319286b993d 100644 --- a/src/scenes.jl +++ b/src/scenes.jl @@ -561,19 +561,6 @@ function plots_from_camera(scene::Scene, camera::Camera, list=AbstractPlot[]) list end -""" -Flattens all the combined plots and returns a Vector of Atomic plots -""" -function flatten_combined(plots::Vector, flat=AbstractPlot[]) - for elem in plots - if (elem isa Combined) - flatten_combined(elem.plots, flat) - else - push!(flat, elem) - end - end - flat -end function insertplots!(screen::AbstractDisplay, scene::Scene) for elem in scene.plots diff --git a/src/types.jl b/src/types.jl index fd8fb4078c6..b91dd552cce 100644 --- a/src/types.jl +++ b/src/types.jl @@ -392,7 +392,7 @@ Custom scale struct, taking a forward and inverse arbitrary scale function. ## Fields $(TYPEDFIELDS) """ -struct ReversibleScale{F <: Function, I <: Function, T <: AbstractInterval} +struct ReversibleScale{F <: Function, I <: Function, T <: AbstractInterval} <: Function """ forward transformation (e.g. `log10`) """ @@ -409,7 +409,8 @@ struct ReversibleScale{F <: Function, I <: Function, T <: AbstractInterval} valid limits interval (optional) """ interval::T - function ReversibleScale(forward, inverse = Automatic(); limits = (0f0, 10f0), interval = (-Inf32, Inf32)) + name::Symbol + function ReversibleScale(forward, inverse = Automatic(); limits = (0f0, 10f0), interval = (-Inf32, Inf32), name=Symbol(forward)) inverse isa Automatic && (inverse = inverse_transform(forward)) isnothing(inverse) && throw(ArgumentError( "Cannot determine inverse transform: you can use `ReversibleScale($(forward), inverse($(forward)))` instead." @@ -422,10 +423,10 @@ 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, limits, interval) + return new{typeof(forward),typeof(inverse),typeof(interval)}(forward, inverse, limits, interval, name) end end -function (s::ReversibleScale)(args...) # functor - s.forward(args...) -end +(s::ReversibleScale)(args...) = s.forward(args...) # functor +Base.show(io::IO, s::ReversibleScale) = print(io, "ReversibleScale($(s.name))") +Base.show(io::IO, ::MIME"text/plain", s::ReversibleScale) = print(io, "ReversibleScale($(s.name))") diff --git a/test/makielayout.jl b/test/makielayout.jl index 54ebaec3f7c..f2ae4ca3835 100644 --- a/test/makielayout.jl +++ b/test/makielayout.jl @@ -168,6 +168,19 @@ end @test ticklabel_strings[1] == "0.0" @test ticklabel_strings[end] == "1.0" end + @testset "errors" begin + f, ax, pl1 = scatter(rand(10)) + pl2 = scatter!(ax, rand(10); color=rand(RGBf, 10)) + pl3 = barplot!(ax, 1:3; colorrange=(0, 1)) + @test_throws ErrorException Colorbar(f[1, 2], pl1) + @test_throws ErrorException Colorbar(f[1, 2], pl2) + @test_throws ErrorException Colorbar(f[1, 2], pl3) + end + @testset "Recipes" begin + f, ax, pl = barplot(1:3; color=1:3) + cbar = Colorbar(f[1, 2], pl) + @test cbar.limits[] == Vec(1.0, 3.0) + end end @testset "cycling" begin @@ -384,10 +397,10 @@ end @test_throws ArgumentError ReversibleScale(sqrt, exp10) # incorrect inverse scale end -@testset "Invalid inverse transform" begin - f = Figure() - @test_throws ArgumentError Colorbar(f[1, 1], limits = (1, 100), scale = x -> log10(x)) -end +# @testset "Invalid inverse transform" begin +# 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) From c0724178f0a5035895d78ad53464c0ef3af27598 Mon Sep 17 00:00:00 2001 From: Glen Hertz Date: Fri, 15 Sep 2023 12:54:06 -0400 Subject: [PATCH 2/3] Fix typo in DataInspector docs (#3234) --- src/interaction/inspector.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interaction/inspector.jl b/src/interaction/inspector.jl index e7cfa61eb67..393d91f6fc1 100644 --- a/src/interaction/inspector.jl +++ b/src/interaction/inspector.jl @@ -219,7 +219,7 @@ disable!(inspector::DataInspector) = inspector.attributes.enabled[] = false Creates a data inspector which will show relevant information in a tooltip when you hover over a plot. -This functionality can eb disabled on a per-plot basis by setting +This functionality can be disabled on a per-plot basis by setting `plot.inspectable[] = false`. The displayed text can be adjusted by setting `plot.inspector_label` to a function `(plot, index, position) -> "my_label"` returning a label. See Makie documentation for more detail. From fdc84b8de1520248eb2b7f5cbd16cd1a1a266fe4 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel <22495855+jkrumbiegel@users.noreply.github.com> Date: Sat, 16 Sep 2023 17:34:23 +0200 Subject: [PATCH 3/3] Adjust Documenter compat for docs build (#3237) adjust Documenter compat for docs build --- docs/Project.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/Project.toml b/docs/Project.toml index d05280df457..0398c2f82ac 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -27,3 +27,6 @@ Observables = "510215fc-4207-5dde-b226-833fc4488ee2" RPRMakie = "22d9f318-5e34-4b44-b769-6e3734a732a6" RadeonProRender = "27029320-176d-4a42-b57d-56729d2ad457" WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008" + +[compat] +Documenter = "<1" \ No newline at end of file