From 6af91c7f71cbe9bb0f310be2845873cb16ace845 Mon Sep 17 00:00:00 2001 From: yha Date: Mon, 1 Mar 2021 03:23:19 +0200 Subject: [PATCH] warning and hint on mismatched attr lengths --- src/utils.jl | 72 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index 67fe5d61..8c4f8481 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -82,8 +82,8 @@ function series_segments(series::Series, seriestype::Symbol = :path) args = RecipesPipeline.is3d(series) ? (x, y, z) : (x, y) nan_segments = collect(iter_segments(args...)) - if has_attribute_segments(series) - return Iterators.flatten(map(nan_segments) do r + result = if has_attribute_segments(series) + Iterators.flatten(map(nan_segments) do r if seriestype in (:scatter, :scatter3d) (SeriesSegment(i:i, i) for i in r) else @@ -91,8 +91,28 @@ function series_segments(series::Series, seriestype::Symbol = :path) end end) else - return (SeriesSegment(r, 1) for r in nan_segments) + (SeriesSegment(r, 1) for r in nan_segments) + end + + seg_attr_range = UnitRange(minimum(first(seg.range) for seg in result), + maximum(last(seg.range) for seg in result)) + for attr in _segmenting_vector_attributes + v = get(series, attr, nothing) + if v isa AVec && eachindex(v) != seg_attr_range + @warn "Indices $(eachindex(v)) of attribute `$attr` does not match data indices $seg_attr_range." + if any(v -> !isnothing(v) && any(isnan, v), (x,y,z)) + @info """Data contains NaNs or missing values, and indices of `$attr` vector do not match data indices. + If you intend elements of `$attr` to apply to individual NaN-separated segements in the data, + pass each segment in a separate vector instead, and use a row vector for `$attr`. Legend entries + may be suppressed by passing an empty label. + For example, + plot([1:2,1:3], [[4,5],[3,4,5]], label=["y" ""], $attr=[1 2]) + """ + end + end end + + return result end # helpers to figure out if there are NaN values in a list of array types @@ -577,34 +597,34 @@ function get_markerstrokewidth(series, i::Int = 1) _cycle(series[:markerstrokewidth], i) end +const _segmenting_vector_attributes = ( + :seriescolor, + :seriesalpha, + :linecolor, + :linealpha, + :linewidth, + :linestyle, + :fillcolor, + :fillalpha, + :markercolor, + :markeralpha, + :markersize, + :markerstrokecolor, + :markerstrokealpha, + :markerstrokewidth, + :markershape, +) + +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( - (typeof(series[attr]) <: AbstractVector && length(series[attr]) > 1) - for - attr in [ - :seriescolor, - :seriesalpha, - :linecolor, - :linealpha, - :linewidth, - :linestyle, - :fillcolor, - :fillalpha, - :markercolor, - :markeralpha, - :markersize, - :markerstrokecolor, - :markerstrokealpha, - :markerstrokewidth, - :markershape, - ] - ) || any( - typeof(series[attr]) <: AbstractArray for attr in (:line_z, :fill_z, :marker_z) - ) + return any(series[attr] isa AbstractVector && length(series[attr]) > 1 + for attr in _segmenting_vector_attributes + ) || any(series[attr] isa AbstractArray for attr in _segmenting_array_attributes) end function get_aspect_ratio(sp)