From dc98495514b46fed35b49de1ac13a42acceb6ec3 Mon Sep 17 00:00:00 2001 From: Yuval Date: Tue, 19 Oct 2021 22:21:20 +0300 Subject: [PATCH] Fix vector attributes to bar plots (#3751) --- src/examples.jl | 17 +++++++++++++++++ src/recipes.jl | 12 ++++++++++++ src/utils.jl | 16 ++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/examples.jl b/src/examples.jl index 049e5172..25dcf1c6 100644 --- a/src/examples.jl +++ b/src/examples.jl @@ -1232,6 +1232,23 @@ const _examples = PlotExample[ ), ], ), + PlotExample( # 56 + "Bar plot customizations", + """ + Width of bars may be specified as `bar_width`. + The bars' baseline may be specified as `fillto`. + Each may be scalar, or a vector spcifying one value per bar. + """, + [:( + begin + plot(bar([-1,0,2,3], [1,3,6,2], + fill_z = 4:-1:1, alpha = [1, 0.2, 0.8, 0.5], label = "", bar_width = 1:4), + bar(rand(5), bar_width=1.2, alpha=0.8, + color=[:lightsalmon, :tomato, :crimson, :firebrick, :darkred], + fillto=0:-0.1:-0.4, label="reds")) + end + )], + ), ] # Some constants for PlotDocs and PlotReferenceImages diff --git a/src/recipes.jl b/src/recipes.jl index ef045446..60960475 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -508,6 +508,18 @@ end primary := true x := xseg.pts y := yseg.pts + # expand attributes to match indices in new series data + for k in _segmenting_vector_attributes ∪ _segmenting_array_attributes + v = get(plotattributes, k, nothing) + if v isa AVec + if eachindex(v) != eachindex(y) + @warn "Indices $(eachindex(v)) of attribute `$k` do not match data indices $(eachindex(y))." + end + # Each segment is 6 elements long, including the NaN separator. + # There is no trailing NaN, so the last repetition is dropped. + plotattributes[k] = @view repeat(v; inner=6)[1:end-1] + end + end () end diff --git a/src/utils.jl b/src/utils.jl index 5f6d3c59..55dddf8d 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -103,7 +103,10 @@ function series_segments(series::Series, seriestype::Symbol = :path; check = fal segments = if has_attribute_segments(series) Iterators.flatten(map(nan_segments) do r - if seriestype in (:scatter, :scatter3d) + if seriestype == :shape + warn_on_inconsistent_shape_attr(series, x, y, z, r) + (SeriesSegment(r, first(r)),) + elseif seriestype in (:scatter, :scatter3d) (SeriesSegment(i:i, i) for i in r) else (SeriesSegment(i:(i + 1), i) for i in first(r):(last(r) - 1)) @@ -140,6 +143,16 @@ function warn_on_attr_dim_mismatch(series, x, y, z, segments) end end +function warn_on_inconsistent_shape_attr(series, x, y, z, r) + for attr in _segmenting_vector_attributes + v = get(series, attr, nothing) + if v isa AVec && length(unique(v[r])) > 1 + @warn "Different values of `$attr` specified for different shape vertices. Only first one will be used." + break + end + end +end + # helpers to figure out if there are NaN values in a list of array types anynan(i::Int, args::Tuple) = any(a -> try isnan(_cycle(a, i)) @@ -573,7 +586,6 @@ const _segmenting_array_attributes = :line_z, :fill_z, :marker_z function has_attribute_segments(series::Series) # we want to check if a series needs to be split into segments just because # of its attributes - series[:seriestype] == :shape && return false # check relevant attributes if they have multiple inputs return any( series[attr] isa AbstractVector && length(series[attr]) > 1 for