diff --git a/src/args.jl b/src/args.jl index ede8feaca..eb0e803fe 100644 --- a/src/args.jl +++ b/src/args.jl @@ -72,7 +72,7 @@ const _allTypes = vcat( _3dTypes, ) -const _z_colored_series = [:contour, :contour3d, :heatmap, :histogram2d, :surface, :hexbin] +const _z_colored_series = Dict([:contour, :contour3d, :heatmap, :histogram2d, :surface, :hexbin] .=> true) const _typeAliases = Dict{Symbol,Symbol}( :n => :none, @@ -2027,11 +2027,13 @@ has_black_border_for_default(st::Function) = has_black_border_for_default(st::Symbol) = like_histogram(st) || st in (:hexbin, :bar, :shape) +default_cgrad() = PlotUtils.DEFAULT_COLOR_GRADIENT[] # MOVE TO PLOTUTILS + # converts a symbol or string into a Colorant or ColorGradient # and assigns a color automatically get_series_color(c, sp::Subplot, n::Int, seriestype) = if c === :auto - like_surface(seriestype) ? cgrad() : _cycle(sp[:color_palette], n) + like_surface(seriestype) ? default_cgrad() : _cycle(sp[:color_palette], n) elseif isa(c, Int) _cycle(sp[:color_palette], c) else diff --git a/src/axes.jl b/src/axes.jl index 8d510ae86..350eaa349 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -403,9 +403,39 @@ function reset_extrema!(sp::Subplot) end end +finitemin(x::Real, y::Real) = min(promote(x, y)...) +finitemax(x::Real, y::Real) = max(promote(x, y)...) + +finitemin(x::T, y::T) where {T<:AbstractFloat} = ifelse((y < x) | (signbit(y) > signbit(x)), + ifelse(isfinite(y), y, x), + ifelse(isfinite(x), x, y)) +finitemax(x::T, y::T) where {T<:AbstractFloat} = ifelse((y > x) | (signbit(y) < signbit(x)), + ifelse(isfinite(y), y, x), + ifelse(isfinite(x), x, y)) + +function finite_extrema(v::AbstractArray) + emin, emax = Inf, -Inf + for x in v + emin = finitemin(emin, x) + emax = finitemax(emax, x) + end + emin, emax +end + +finite_extrema(v) = extrema(v) + +finite_minimum(v::AbstractArray) = reduce(finitemin, v) +finite_maximum(v::AbstractArray) = reduce(finitemax, v) + function expand_extrema!(ex::Extrema, v::Number) - ex.emin = isfinite(v) ? min(v, ex.emin) : ex.emin - ex.emax = isfinite(v) ? max(v, ex.emax) : ex.emax + ex.emin = finitemin(v, ex.emin) + ex.emax = finitemax(v, ex.emax) + ex +end + +function expand_extrema!(ex::Extrema, v::Extrema) + ex.emin = finitemin(v.emin, ex.emin) + ex.emax = finitemax(v.emax, ex.emax) ex end @@ -415,16 +445,22 @@ expand_extrema!(axis::Axis, v::Number) = expand_extrema!(axis[:extrema], v) expand_extrema!(axis::Axis, ::Nothing) = axis[:extrema] expand_extrema!(axis::Axis, ::Bool) = axis[:extrema] -function expand_extrema!(axis::Axis, v::Tuple{MIN,MAX}) where {MIN<:Number,MAX<:Number} +function expand_extrema!(axis::Axis, v::Tuple{<:Number, <:Number}) ex = axis[:extrema]::Extrema - ex.emin = isfinite(v[1]) ? min(v[1], ex.emin) : ex.emin - ex.emax = isfinite(v[2]) ? max(v[2], ex.emax) : ex.emax + ex.emin = finitemin(v[1], ex.emin) + ex.emax = finitemax(v[2], ex.emax) ex end -function expand_extrema!(axis::Axis, v::AVec{N}) where {N<:Number} - ex = axis[:extrema]::Extrema - foreach(vi -> expand_extrema!(ex, vi), v) - ex +function expand_extrema!(axis::Axis, v::AbstractArray{<:Number}) + vex = if length(v) > 1024 + vex = finite_extrema(@view v[1:1000]) + stride = length(v) ÷ 1024 + 1 + vex2 = finite_extrema(@view v[1001:stride:end]) + finitemin(vex[1], vex2[1]), finitemax(vex[2], vex2[2]) + else + finite_extrema(v) + end + expand_extrema!(axis, vex) end function expand_extrema!(sp::Subplot, plotattributes::AKW) @@ -468,6 +504,31 @@ function expand_extrema!(sp::Subplot, plotattributes::AKW) expand_extrema!(axis, plotattributes[letter]) end end + + # expand for bar_width + if plotattributes[:seriestype] === :bar + dsym = vert ? :x : :y + data = plotattributes[dsym] + + if (bw = plotattributes[:bar_width]) === nothing + pos = filter(>(0), diff(sort(data))) + plotattributes[:bar_width] = bw = _bar_width * finite_minimum(pos) + end + axis = sp.attr[get_attr_symbol(dsym, :axis)] + ex = finite_extrema(data) + expand_extrema!(axis, ex[1] + 0.5maximum(bw)) + expand_extrema!(axis, ex[2] - 0.5minimum(bw)) + elseif plotattributes[:seriestype] === :heatmap + for letter in (:x, :y) + data = plotattributes[letter] + axis = sp[get_attr_symbol(letter, :axis)] + scale = get(plotattributes, get_attr_symbol(letter, :scale), :identity) + ex = scale === :identity ? + heatmap_extrema(data) : + heatmap_extrema(data, scale) + expand_extrema!(axis, ex) + end + end # # expand for fillrange/bar_width # fillaxis, baraxis = sp.attr[:yaxis], sp.attr[:xaxis] @@ -489,29 +550,6 @@ function expand_extrema!(sp::Subplot, plotattributes::AKW) end end - # expand for bar_width - if plotattributes[:seriestype] === :bar - dsym = vert ? :x : :y - data = plotattributes[dsym] - - if (bw = plotattributes[:bar_width]) === nothing - pos = filter(>(0), diff(sort(data))) - plotattributes[:bar_width] = bw = _bar_width * ignorenan_minimum(pos) - end - axis = sp.attr[get_attr_symbol(dsym, :axis)] - expand_extrema!(axis, ignorenan_maximum(data) + 0.5maximum(bw)) - expand_extrema!(axis, ignorenan_minimum(data) - 0.5minimum(bw)) - end - - # expand for heatmaps - if plotattributes[:seriestype] === :heatmap - for letter in (:x, :y) - data = plotattributes[letter] - axis = sp[get_attr_symbol(letter, :axis)] - scale = get(plotattributes, get_attr_symbol(letter, :scale), :identity) - expand_extrema!(axis, heatmap_edges(data, scale)) - end - end end function expand_extrema!(sp::Subplot, xmin, xmax, ymin, ymax) diff --git a/src/backends/gaston.jl b/src/backends/gaston.jl index 41938363c..6f9969031 100644 --- a/src/backends/gaston.jl +++ b/src/backends/gaston.jl @@ -293,6 +293,7 @@ function gaston_seriesconf!( extra_curves = String[] clims = get_clims(sp, series) + clims = clims.emin, clims.emax if st ∈ (:scatter, :scatter3d) lc, dt, lw = gaston_lc_ls_lw(series, clims, i) pt, ps, mc = gaston_mk_ms_mc(series, clims, i) diff --git a/src/backends/gr.jl b/src/backends/gr.jl index c0e283a1c..d86f9a1cc 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -898,8 +898,12 @@ remap(x, lo, hi) = (x - lo) / (hi - lo) get_z_normalized(z, clims...) = isnan(z) ? 256 / 255 : remap(clamp(z, clims...), clims...) function gr_clims(sp, args...) - sp[:clims] === :auto || return get_clims(sp) - lo, hi = get_clims(sp, args...) + if sp[:clims] !== :auto + lims = get_clims(sp) + return lims.emin, lims.emax + end + lims = get_clims(sp, args...) + lo, hi = lims.emin, lims.emax if lo == hi if lo == 0 hi = one(hi) diff --git a/src/backends/inspectdr.jl b/src/backends/inspectdr.jl index 337c47e06..3427d35e4 100644 --- a/src/backends/inspectdr.jl +++ b/src/backends/inspectdr.jl @@ -215,6 +215,7 @@ function _series_added(plt::Plot{InspectDRBackend}, series::Series) (plot = sp.o) === nothing && return clims = get_clims(sp, series) + clims = clims.emin, clims.emax _vectorize(v) = isa(v, Vector) ? v : collect(v) #InspectDR only supports vectors x, y = if st === :straightline diff --git a/src/backends/pgfplotsx.jl b/src/backends/pgfplotsx.jl index 6f491d0a8..6342ff1d6 100644 --- a/src/backends/pgfplotsx.jl +++ b/src/backends/pgfplotsx.jl @@ -140,8 +140,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) bgc_inside = plot_color(sp[:background_color_inside]) update_clims(sp) axis_opt = Options( - "point meta max" => get_clims(sp)[2], - "point meta min" => get_clims(sp)[1], + "point meta max" => get_clims(sp).emax, + "point meta min" => get_clims(sp).emin, "legend cell align" => "left", "legend columns" => pgfx_legend_col(sp[:legend_column]), "title" => sp[:title], diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl index b7444eb23..3f3045ec3 100644 --- a/src/backends/plotly.jl +++ b/src/backends/plotly.jl @@ -509,6 +509,7 @@ as_gradient(grad, α) = cgrad(alpha = α) function plotly_series(plt::Plot, series::Series) sp = series[:subplot] clims = get_clims(sp, series) + clims = clims.emin, clims.emax (st = series[:seriestype]) === :shape && return plotly_series_shapes(plt, series, clims) @@ -953,7 +954,8 @@ end function plotly_colorbar_hack(series::Series, plotattributes_base::KW, sym::Symbol) plotattributes_out = deepcopy(plotattributes_base) - cmin, cmax = get_clims(series[:subplot]) + clims = get_clims(series[:subplot]) + cmin, cmax = clims.emin, clims.emax plotattributes_out[:showlegend] = false plotattributes_out[:type] = RecipesPipeline.is3d(series) ? :scatter3d : :scatter plotattributes_out[:hoverinfo] = :none diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 3965fc107..61287d5a1 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -409,7 +409,8 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series) # handle zcolor and get c/cmap needs_colorbar = hascolorbar(sp) - vmin, vmax = clims = get_clims(sp, series) + clims = get_clims(sp, series) + vmin, vmax = clims = clims.emin, clims.emax # Dict to store extra kwargs extrakw = if st === :wireframe || st === :hexbin @@ -1011,7 +1012,8 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) elseif any( colorbar_series[attr] !== nothing for attr in (:line_z, :fill_z, :marker_z) ) - cmin, cmax = get_clims(sp) + clims = get_clims(sp) + cmin, cmax = clims.emin, clims.emax norm = pycolors."Normalize"(vmin = cmin, vmax = cmax) f = if colorbar_series[:line_z] !== nothing py_linecolormap @@ -1467,6 +1469,7 @@ function py_add_legend(plt::Plot, sp::Subplot, ax) should_add_to_legend(series) || continue nseries += 1 clims = get_clims(sp, series) + clims = clims.emin, clims.emax # add a line/marker and a label if series[:seriestype] === :shape || series[:fillrange] !== nothing lc = get_linecolor(series, clims) diff --git a/src/colorbars.jl b/src/colorbars.jl index 15d79a6c1..765da20a9 100644 --- a/src/colorbars.jl +++ b/src/colorbars.jl @@ -1,81 +1,14 @@ -# These functions return an operator for use in `get_clims(::Seres, op)` process_clims(lims::Tuple{<:Number,<:Number}) = - (zlims -> ifelse.(isfinite.(lims), lims, zlims)) ∘ ignorenan_extrema -process_clims(s::Union{Symbol,Nothing,Missing}) = ignorenan_extrema + (zlims -> ifelse.(isfinite.(lims), lims, zlims)) ∘ finite_extrema +process_clims(::Union{Symbol,Nothing,Missing}) = finite_extrema # don't specialize on ::Function otherwise python functions won't work process_clims(f) = f -get_clims(sp::Subplot)::Tuple{Float64,Float64} = - haskey(sp.attr, :clims_calculated) ? sp[:clims_calculated] : update_clims(sp) -get_clims(series::Series)::Tuple{Float64,Float64} = - haskey(series.plotattributes, :clims_calculated) ? - series[:clims_calculated]::Tuple{Float64,Float64} : update_clims(series) -get_clims(sp::Subplot, series::Series)::Tuple{Float64,Float64} = +get_clims(sp::Subplot) = sp.color_extrema +get_clims(series::Series) = series.color_extrema +get_clims(sp::Subplot, series::Series) = series[:colorbar_entry] ? get_clims(sp) : get_clims(series) -function update_clims(sp::Subplot, op = process_clims(sp[:clims]))::Tuple{Float64,Float64} - zmin, zmax = Inf, -Inf - for series in series_list(sp) - if series[:colorbar_entry]::Bool - zmin, zmax = _update_clims(zmin, zmax, update_clims(series, op)...) - else - update_clims(series, op) - end - end - return sp[:clims_calculated] = zmin <= zmax ? (zmin, zmax) : (NaN, NaN) -end - -function update_clims( - sp::Subplot, - series::Series, - op = process_clims(sp[:clims]), -)::Tuple{Float64,Float64} - zmin, zmax = get_clims(sp) - old_zmin, old_zmax = zmin, zmax - if series[:colorbar_entry]::Bool - zmin, zmax = _update_clims(zmin, zmax, update_clims(series, op)...) - else - update_clims(series, op) - end - isnan(zmin) && isnan(old_zmin) && isnan(zmax) && isnan(old_zmax) || - zmin == old_zmin && zmax == old_zmax || - update_clims(sp) - return zmin <= zmax ? (zmin, zmax) : (NaN, NaN) -end - -""" - update_clims(::Series, op=Plots.ignorenan_extrema) -Finds the limits for the colorbar by taking the "z-values" for the series and passing them into `op`, -which must return the tuple `(zmin, zmax)`. The default op is the extrema of the finite -values of the input. The value is stored as a series property, which is retrieved by `get_clims`. -""" -function update_clims(series::Series, op = ignorenan_extrema)::Tuple{Float64,Float64} - zmin, zmax = Inf, -Inf - - # keeping this unrolled has higher performance - if series[:seriestype] ∈ _z_colored_series && series[:z] !== nothing - zmin, zmax = update_clims(zmin, zmax, series[:z], op) - end - if series[:line_z] !== nothing - zmin, zmax = update_clims(zmin, zmax, series[:line_z], op) - end - if series[:marker_z] !== nothing - zmin, zmax = update_clims(zmin, zmax, series[:marker_z], op) - end - if series[:fill_z] !== nothing - zmin, zmax = update_clims(zmin, zmax, series[:fill_z], op) - end - return series[:clims_calculated] = zmin <= zmax ? (zmin, zmax) : (NaN, NaN) -end - -update_clims(zmin, zmax, vals::AbstractSurface, op)::Tuple{Float64,Float64} = - update_clims(zmin, zmax, vals.surf, op) -update_clims(zmin, zmax, vals::Any, op)::Tuple{Float64,Float64} = - _update_clims(zmin, zmax, op(vals)...) -update_clims(zmin, zmax, ::Nothing, ::Any)::Tuple{Float64,Float64} = zmin, zmax - -_update_clims(zmin, zmax, emin, emax) = NaNMath.min(zmin, emin), NaNMath.max(zmax, emax) - @enum ColorbarStyle cbar_gradient cbar_fill cbar_lines function colorbar_style(series::Series) @@ -109,6 +42,7 @@ function get_colorbar_ticks(sp::Subplot; update = true, formatter = sp[:colorbar cvals = sp[:colorbar_continuous_values] dvals = sp[:colorbar_discrete_values] clims = get_clims(sp) + clims = clims.emin, clims.emax scale = sp[:colorbar_scale] sp.attr[:colorbar_optimized_ticks] = get_ticks(ticks, cvals, dvals, clims, scale, formatter) @@ -117,5 +51,62 @@ function get_colorbar_ticks(sp::Subplot; update = true, formatter = sp[:colorbar end # Dynamic callback from the pipeline if needed -_update_subplot_colorbars(sp::Subplot) = update_clims(sp) -_update_subplot_colorbars(sp::Subplot, series::Series) = update_clims(sp, series) +function _update_subplot_colorbar_extrema(sp::Subplot, series::Series, op = process_clims(sp[:clims])) + ex = sp.color_extrema + old_emin = ex.emin + old_emax = ex.emax + seriesex = expand_colorbar_extrema!(series, op) + if series[:colorbar_entry]::Bool + expand_colorbar_extrema!(sp, (seriesex.emin, seriesex.emax)) + end + if ex.emin != old_emin || ex.emax != old_emax + # expanded, need to update other series + for s in series_list(sp) + s.color_extrema = ex + end + end + nothing +end + +function expand_colorbar_extrema!(series::Series, op) + if haskey(_z_colored_series, series[:seriestype]) && series[:z] !== nothing + expand_colorbar_extrema!(series, series[:z], op) + end + expand_colorbar_extrema!(series, series[:line_z], op) + expand_colorbar_extrema!(series, series[:marker_z], op) + expand_colorbar_extrema!(series, series[:fill_z], op) +end + +function expand_colorbar_extrema!(series::Series, v::AbstractArray{<:Number}, op) + vex = if length(v) > 1024 + vex = op(@view v[1:1000]) + stride = length(v) ÷ 1024 + 1 + vex2 = op(@view v[1001:stride:end]) + finitemin(vex[1], vex2[1]), finitemax(vex[2], vex2[2]) + else + op(v) + end + expand_colorbar_extrema!(series, vex) +end + +expand_colorbar_extrema!(series::Series, ::Nothing, ::Any) = series.color_extrema + +function expand_colorbar_extrema!(series::Series, v::Tuple{<:Number, <:Number}) + ex = series.color_extrema + ex.emin = finitemin(v[1], ex.emin) + ex.emax = finitemax(v[2], ex.emax) + ex +end + +function expand_colorbar_extrema!(sp::Subplot, v::Tuple{<:Number, <:Number}) + ex = sp.color_extrema + ex.emin = finitemin(v[1], ex.emin) + ex.emax = finitemax(v[2], ex.emax) + ex +end + +expand_colorbar_extrema!(series::Series, v::Number, ::Any) = expand_extrema!(series.color_extrema, v) + +expand_colorbar_extrema!(series::Series, surf::Surface, op) = + expand_colorbar_extrema!(series, surf.surf, op) + diff --git a/src/components.jl b/src/components.jl index 870cfb921..b89eff5db 100644 --- a/src/components.jl +++ b/src/components.jl @@ -715,11 +715,7 @@ locate_annotation(sp::Subplot, rel::NTuple{3,<:Number}, label::PlotText) = ( # ----------------------------------------------------------------------- -function expand_extrema!(a::Axis, surf::Surface) - ex = a[:extrema] - foreach(x -> expand_extrema!(ex, x), surf.surf) - ex -end +expand_extrema!(a::Axis, surf::Surface) = expand_extrema!(a, surf.surf) "For the case of representing a surface as a function of x/y... can possibly avoid allocations." struct SurfaceFunction <: AbstractSurface diff --git a/src/pipeline.jl b/src/pipeline.jl index 0c0a958ef..b0c3d7dba 100644 --- a/src/pipeline.jl +++ b/src/pipeline.jl @@ -460,5 +460,5 @@ function _add_the_series(plt, sp, plotattributes) @error "Wrong type $(typeof(z_order)) for attribute z_order" end _series_added(plt, series) - _update_subplot_colorbars(sp, series) + _update_subplot_colorbar_extrema(sp, series) end diff --git a/src/types.jl b/src/types.jl index 6dc6067e2..07b311719 100644 --- a/src/types.jl +++ b/src/types.jl @@ -15,8 +15,18 @@ struct InputWrapper{T} obj::T end +mutable struct Extrema + emin::Float64 + emax::Float64 +end + +Extrema() = Extrema(Inf, -Inf) + mutable struct Series plotattributes::DefaultsDict + color_extrema::Extrema # calculated colorbar limits + + Series(plotattributes) = new(plotattributes, Extrema()) end # a single subplot @@ -28,6 +38,7 @@ mutable struct Subplot{T<:AbstractBackend} <: AbstractLayout bbox::BoundingBox # the canvas area which is available to this subplot plotarea::BoundingBox # the part where the data goes attr::DefaultsDict # args specific to this subplot + color_extrema::Extrema # calculated colorbar limits o # can store backend-specific data... like a pyplot ax plt # the enclosing Plot object (can't give it a type because of no forward declarations) @@ -39,6 +50,7 @@ mutable struct Subplot{T<:AbstractBackend} <: AbstractLayout DEFAULT_BBOX[], DEFAULT_BBOX[], DefaultsDict(KW(), _subplot_defaults), + Extrema(), nothing, nothing, ) @@ -50,13 +62,6 @@ mutable struct Axis plotattributes::DefaultsDict end -mutable struct Extrema - emin::Float64 - emax::Float64 -end - -Extrema() = Extrema(Inf, -Inf) - const SubplotMap = Dict{Any,Subplot} mutable struct Plot{T<:AbstractBackend} <: AbstractPlot{T} diff --git a/src/utils.jl b/src/utils.jl index 780dd4f9f..9122d8a54 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -230,20 +230,34 @@ function __heatmap_edges(v::AVec, isedges::Bool, ispolar::Bool) isedges && return v # `isedges = true` means that v is a vector which already describes edges # and does not need to be extended. - vmin, vmax = ignorenan_extrema(v) + vmin, vmax = __heatmap_extrema(v, isedges, ispolar) + vcat(vmin, 0.5(v[1:(n - 1)] + v[2:n]), vmax) +end + +function __heatmap_extrema(v::AVec, isedges::Bool, ispolar::Bool) + isedges && return finite_extrema(v) + vmin, vmax = finite_extrema(v) extra_min = ispolar ? min(v[1], 0.5(v[2] - v[1])) : 0.5(v[2] - v[1]) - extra_max = 0.5(v[n] - v[n - 1]) - vcat(vmin - extra_min, 0.5(v[1:(n - 1)] + v[2:n]), vmax + extra_max) + extra_max = 0.5(v[end] - v[end - 1]) + vmin - extra_min, vmax + extra_max end _heatmap_edges(::Val{true}, v::AVec, ::Symbol, isedges::Bool, ispolar::Bool) = __heatmap_edges(v, isedges, ispolar) +_heatmap_extrema(::Val{true}, v::AVec, ::Symbol, isedges::Bool, ispolar::Bool) = + __heatmap_extrema(v, isedges, ispolar) + function _heatmap_edges(::Val{false}, v::AVec, scale::Symbol, isedges::Bool, ispolar::Bool) f, invf = scale_inverse_scale_func(scale) invf.(__heatmap_edges(f.(v), isedges, ispolar)) end +function _heatmap_extrema(::Val{false}, v::AVec, scale::Symbol, isedges::Bool, ispolar::Bool) + f, invf = scale_inverse_scale_func(scale) + invf.(__heatmap_extrema(f.(v), isedges, ispolar)) +end + "create an (n+1) list of the outsides of heatmap rectangles" heatmap_edges( v::AVec, @@ -252,6 +266,33 @@ heatmap_edges( ispolar::Bool = false, ) = _heatmap_edges(Val(scale === :identity), v, scale, isedges, ispolar) +# heatmap_extrema( +# v::AVec, +# scale::Symbol = :identity, +# isedges::Bool = false, +# ispolar::Bool = false, +# ) = _heatmap_extrema(Val(scale === :identity), v, scale, isedges, ispolar) + +# assumes v is ordered, which it should be for a heatmap +function heatmap_extrema( + # vmin::Float64, + # vmax::Float64, + v::AVec, +) + extra_min = 0.5(v[2] - v[1]) + extra_max = 0.5(v[end] - v[end - 1]) + v[1] - extra_min, v[end] + extra_max +end + +function heatmap_extrema( + v::AVec, + scale::Symbol, +) + f, invf = scale_inverse_scale_func(scale) + ex = heatmap_extrema(f.(v)) + invf(ex[1]), invf(ex[2]) +end + function heatmap_edges( x::AVec, xscale::Symbol, @@ -480,7 +521,8 @@ for comp in (:line, :fill, :marker) if series[$Symbol($comp_z)] === nothing $get_compcolor(series, 0, 1, i) else - $get_compcolor(series, get_clims(series[:subplot]), i) + lims = get_clims(series[:subplot]) + $get_compcolor(series, (lims.emin, lims.emax), i) end end