Merge pull request #3320 from yha/segments-nan-attr
Fix for "segmented" attributes with NaNs
This commit is contained in:
commit
f097549e3b
13
deps/SnoopCompile/precompile/precompile_Plots.jl
vendored
13
deps/SnoopCompile/precompile/precompile_Plots.jl
vendored
@ -59,9 +59,9 @@ function _precompile_()
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(Type)),NamedTuple{(:parent,),Tuple{Subplot{PlotlyBackend}}},Type{Subplot},PlotlyBackend})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(_make_hist)),NamedTuple{(:normed, :weights),Tuple{Bool,Array{Int64,1}}},typeof(_make_hist),Tuple{Array{Float64,1}},Symbol})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(_make_hist)),NamedTuple{(:normed, :weights),Tuple{Bool,Nothing}},typeof(_make_hist),Tuple{Array{Float64,1},Array{Float64,1}},Int64})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(_make_hist)),NamedTuple{(:normed, :weights),Tuple{Bool,Nothing}},typeof(_make_hist),Tuple{Array{Float64,1},Array{Float64,1}},Tuple{Int64,Int64}})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(_make_hist)),NamedTuple{(:normed, :weights),Tuple{Bool,Nothing}},typeof(_make_hist),Tuple{Array{Float64,1}},Symbol})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:flip,),Tuple{Bool}},typeof(attr!),Axis})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:formatter,),Tuple{Symbol}},typeof(attr!),Axis})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:formatter,),Tuple{typeof(datetimeformatter)}},typeof(attr!),Axis})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:grid, :lims),Tuple{Bool,Tuple{Float64,Float64}}},typeof(attr!),Axis})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:grid, :lims, :flip),Tuple{Bool,Tuple{Float64,Float64},Bool}},typeof(attr!),Axis})
|
||||
@ -74,7 +74,6 @@ function _precompile_()
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:lims,),Tuple{Tuple{Int64,Int64}}},typeof(attr!),Axis})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:rotation,),Tuple{Int64}},typeof(attr!),Axis})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:scale, :guide),Tuple{Symbol,String}},typeof(attr!),Axis})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:ticks,),Tuple{Nothing}},typeof(attr!),Axis})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(attr!)),NamedTuple{(:ticks,),Tuple{UnitRange{Int64}}},typeof(attr!),Axis})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(default)),NamedTuple{(:titlefont, :legendfontsize, :guidefont, :tickfont, :guide, :framestyle, :yminorgrid),Tuple{Tuple{Int64,String},Int64,Tuple{Int64,Symbol},Tuple{Int64,Symbol},String,Symbol,Bool}},typeof(default)})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(gr_polyline)),NamedTuple{(:arrowside, :arrowstyle),Tuple{Symbol,Symbol}},typeof(gr_polyline),Array{Int64,1},Array{Float64,1}})
|
||||
@ -141,6 +140,7 @@ function _precompile_()
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:m, :lab, :bg, :xlim, :ylim, :seriestype),Tuple{Tuple{Int64,Symbol},Array{String,2},Symbol,Tuple{Int64,Int64},Tuple{Int64,Int64},Symbol}},typeof(plot),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,2}})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:marker,),Tuple{Bool}},typeof(plot),Array{Union{Missing, Int64},1}})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:marker_z, :color, :legend, :seriestype),Tuple{typeof(+),Symbol,Bool,Symbol}},typeof(plot),Array{Float64,1},Array{Float64,1}})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:markershape, :markersize, :marker_z, :line_z, :linewidth),Tuple{Array{Symbol,2},Array{Int64,2},Array{Int64,2},Array{Int64,2},Array{Int64,2}}},typeof(plot),Array{Float64,2}})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:nbins, :seriestype),Tuple{Int64,Symbol}},typeof(plot),Array{Float64,1},Array{Float64,1}})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:nbins, :show_empty_bins, :normed, :aspect_ratio, :seriestype),Tuple{Tuple{Int64,Int64},Bool,Bool,Int64,Symbol}},typeof(plot),Array{Complex{Float64},1}})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:proj, :m),Tuple{Symbol,Int64}},typeof(plot),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,1}})
|
||||
@ -148,7 +148,6 @@ function _precompile_()
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:quiver, :seriestype),Tuple{Tuple{Array{Float64,1},Array{Float64,1},Array{Float64,1}},Symbol}},typeof(plot),Array{Float64,1},Array{Float64,1},Vararg{Array{Float64,1},N} where N})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:reg, :fill),Tuple{Bool,Tuple{Int64,Symbol}}},typeof(plot),Array{Float64,1}})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:ribbon,),Tuple{Int64}},typeof(plot),UnitRange{Int64}})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:ribbon,),Tuple{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}},typeof(plot),UnitRange{Int64}})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:ribbon,),Tuple{Tuple{LinRange{Float64},LinRange{Float64}}}},typeof(plot),UnitRange{Int64}})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:ribbon,),Tuple{typeof(sqrt)}},typeof(plot),UnitRange{Int64}})
|
||||
Base.precompile(Tuple{Core.kwftype(typeof(plot)),NamedTuple{(:seriestype, :markershape, :markersize, :color),Tuple{Array{Symbol,2},Array{Symbol,1},Int64,Array{Symbol,1}}},typeof(plot),Array{Float64,2}})
|
||||
@ -247,11 +246,12 @@ function _precompile_()
|
||||
Base.precompile(Tuple{typeof(gr_draw_markers),Series,Base.OneTo{Int64},Array{Float64,1},Tuple{Float64,Float64}})
|
||||
Base.precompile(Tuple{typeof(gr_draw_markers),Series,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Array{Float64,1},Tuple{Float64,Float64}})
|
||||
Base.precompile(Tuple{typeof(gr_draw_segments),Series,Array{Float64,1},Array{Float64,1},Int64,Tuple{Float64,Float64}})
|
||||
Base.precompile(Tuple{typeof(gr_draw_segments),Series,Array{Float64,1},Array{Float64,1},Nothing,Tuple{Float64,Float64}})
|
||||
Base.precompile(Tuple{typeof(gr_draw_segments),Series,Array{Int64,1},Array{Int64,1},Nothing,Tuple{Float64,Float64}})
|
||||
Base.precompile(Tuple{typeof(gr_draw_segments),Series,Base.OneTo{Int64},Array{Float64,1},Int64,Tuple{Float64,Float64}})
|
||||
Base.precompile(Tuple{typeof(gr_draw_segments),Series,Base.OneTo{Int64},Array{Float64,1},Nothing,Tuple{Float64,Float64}})
|
||||
Base.precompile(Tuple{typeof(gr_draw_segments),Series,Base.OneTo{Int64},UnitRange{Int64},Tuple{Array{Float64,1},Array{Float64,1}},Tuple{Float64,Float64}})
|
||||
Base.precompile(Tuple{typeof(gr_draw_segments),Series,StepRange{Int64,Int64},Array{Float64,1},Int64,Tuple{Float64,Float64}})
|
||||
Base.precompile(Tuple{typeof(gr_draw_shapes),Series,Array{Float64,1},Array{Float64,1},Tuple{Float64,Float64}})
|
||||
Base.precompile(Tuple{typeof(gr_draw_surface),Series,Array{Float64,1},Array{Float64,1},Array{Float64,1},Tuple{Float64,Float64}})
|
||||
Base.precompile(Tuple{typeof(gr_draw_surface),Series,Array{Float64,1},Array{Float64,1},Array{Float64,2},Tuple{Float64,Float64}})
|
||||
Base.precompile(Tuple{typeof(gr_get_ticks_size),Tuple{Array{Float64,1},Array{Any,1}},Int64})
|
||||
@ -262,15 +262,11 @@ function _precompile_()
|
||||
Base.precompile(Tuple{typeof(gr_polaraxes),Int64,Float64,Subplot{GRBackend}})
|
||||
Base.precompile(Tuple{typeof(gr_polyline),Array{Float64,1},Array{Float64,1},Function})
|
||||
Base.precompile(Tuple{typeof(gr_set_gradient),PlotUtils.ContinuousColorGradient})
|
||||
Base.precompile(Tuple{typeof(gr_viewport_from_bbox),Subplot{GRBackend},BoundingBox{Tuple{Length{:mm,Float64},Length{:mm,Float64}},Tuple{Length{:mm,Float64},Length{:mm,Float64}}},Length{:mm,Float64},Length{:mm,Float64},Array{Float64,1}})
|
||||
Base.precompile(Tuple{typeof(heatmap_edges),Array{Float64,1},Symbol})
|
||||
Base.precompile(Tuple{typeof(heatmap_edges),Base.OneTo{Int64},Symbol})
|
||||
Base.precompile(Tuple{typeof(heatmap_edges),StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},Symbol})
|
||||
Base.precompile(Tuple{typeof(heatmap_edges),UnitRange{Int64},Symbol})
|
||||
Base.precompile(Tuple{typeof(ignorenan_minimum),Array{Int64,1}})
|
||||
Base.precompile(Tuple{typeof(iter_segments),Array{Float64,1},Array{Float64,1},UnitRange{Int64}})
|
||||
Base.precompile(Tuple{typeof(iter_segments),Array{Float64,1}})
|
||||
Base.precompile(Tuple{typeof(iter_segments),Base.OneTo{Int64},Array{Float64,1}})
|
||||
Base.precompile(Tuple{typeof(layout_args),Int64})
|
||||
Base.precompile(Tuple{typeof(make_fillrange_side),UnitRange{Int64},LinRange{Float64}})
|
||||
Base.precompile(Tuple{typeof(optimal_ticks_and_labels),Subplot{GRBackend},Axis,StepRange{Int64,Int64}})
|
||||
@ -298,6 +294,7 @@ function _precompile_()
|
||||
Base.precompile(Tuple{typeof(quiver_using_arrows),DefaultsDict})
|
||||
Base.precompile(Tuple{typeof(quiver_using_hack),DefaultsDict})
|
||||
Base.precompile(Tuple{typeof(reset_axis_defaults_byletter!)})
|
||||
Base.precompile(Tuple{typeof(slice_arg),Array{Int64,2},Int64})
|
||||
Base.precompile(Tuple{typeof(slice_arg),Array{Length{:mm,Float64},2},Int64})
|
||||
Base.precompile(Tuple{typeof(slice_arg),Array{PlotUtils.ContinuousColorGradient,2},Int64})
|
||||
Base.precompile(Tuple{typeof(slice_arg),Array{RGBA{Float64},2},Int64})
|
||||
|
||||
@ -842,6 +842,7 @@ const _pgfplotsx_marker = [
|
||||
:rtriangle,
|
||||
:cross,
|
||||
:xcross,
|
||||
:x,
|
||||
:star5,
|
||||
:pentagon,
|
||||
:hline,
|
||||
|
||||
@ -1561,7 +1561,7 @@ function gr_add_series(sp, series)
|
||||
gr_draw_markers(series, x, y, clims)
|
||||
end
|
||||
elseif st === :shape
|
||||
gr_draw_shapes(series, x, y, clims)
|
||||
gr_draw_shapes(series, clims)
|
||||
elseif st in (:path3d, :scatter3d)
|
||||
gr_draw_segments_3d(series, x, y, z, clims)
|
||||
if st === :scatter3d || series[:markershape] !== :none
|
||||
@ -1613,12 +1613,13 @@ end
|
||||
function gr_draw_segments(series, x, y, fillrange, clims)
|
||||
st = series[:seriestype]
|
||||
if x !== nothing && length(x) > 1
|
||||
segments = iter_segments(series, st)
|
||||
segments = series_segments(series, st)
|
||||
# do area fill
|
||||
if fillrange !== nothing
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
fr_from, fr_to = (is_2tuple(fillrange) ? fillrange : (y, fillrange))
|
||||
for (i, rng) in enumerate(segments)
|
||||
for segment in segments
|
||||
i, rng = segment.attr_index, segment.range
|
||||
fc = get_fillcolor(series, clims, i)
|
||||
gr_set_fillcolor(fc)
|
||||
fx = _cycle(x, vcat(rng, reverse(rng)))
|
||||
@ -1630,7 +1631,8 @@ function gr_draw_segments(series, x, y, fillrange, clims)
|
||||
|
||||
# draw the line(s)
|
||||
if st in (:path, :straightline)
|
||||
for (i, rng) in enumerate(segments)
|
||||
for segment in segments
|
||||
i, rng = segment.attr_index, segment.range
|
||||
lc = get_linecolor(series, clims, i)
|
||||
gr_set_line(
|
||||
get_linewidth(series, i), get_linestyle(series, i), lc, series
|
||||
@ -1648,8 +1650,9 @@ end
|
||||
function gr_draw_segments_3d(series, x, y, z, clims)
|
||||
if series[:seriestype] === :path3d && length(x) > 1
|
||||
lz = series[:line_z]
|
||||
segments = iter_segments(series, :path3d)
|
||||
for (i, rng) in enumerate(segments)
|
||||
segments = series_segments(series, :path3d)
|
||||
for segment in segments
|
||||
i, rng = segment.attr_index, segment.range
|
||||
lc = get_linecolor(series, clims, i)
|
||||
gr_set_line(
|
||||
get_linewidth(series, i), get_linestyle(series, i), lc, series
|
||||
@ -1674,8 +1677,9 @@ function gr_draw_markers(
|
||||
|
||||
shapes = series[:markershape]
|
||||
if shapes != :none
|
||||
for (i, rng) in enumerate(iter_segments(series, :scatter))
|
||||
rng = intersect(eachindex(x), rng)
|
||||
for segment in series_segments(series, :scatter)
|
||||
i = segment.attr_index
|
||||
rng = intersect(eachindex(x), segment.range)
|
||||
if !isempty(rng)
|
||||
ms = get_thickness_scaling(series) * _cycle(msize, i)
|
||||
msw = get_thickness_scaling(series) * _cycle(strokewidth, i)
|
||||
@ -1688,9 +1692,10 @@ function gr_draw_markers(
|
||||
end
|
||||
end
|
||||
|
||||
function gr_draw_shapes(series, x, y, clims)
|
||||
function gr_draw_shapes(series, clims)
|
||||
x, y = shape_data(series)
|
||||
for (i,rng) in enumerate(iter_segments(x, y))
|
||||
for segment in series_segments(series, :shape)
|
||||
i, rng = segment.attr_index, segment.range
|
||||
if length(rng) > 1
|
||||
# connect to the beginning
|
||||
rng = vcat(rng, rng[1])
|
||||
|
||||
@ -339,9 +339,10 @@ end
|
||||
|
||||
function pgfx_add_series!(::Val{:path}, axis, series_opt, series, series_func, opt)
|
||||
# treat segments
|
||||
segments = iter_segments(series, series[:seriestype])
|
||||
segments = collect(series_segments(series, series[:seriestype]))
|
||||
sf = opt[:fillrange]
|
||||
for (i, rng) in enumerate(segments)
|
||||
for segment in segments
|
||||
i, rng = segment.attr_index, segment.range
|
||||
segment_opt = PGFPlotsX.Options()
|
||||
segment_opt = merge(segment_opt, pgfx_linestyle(opt, i))
|
||||
if opt[:markershape] != :none
|
||||
|
||||
@ -643,7 +643,7 @@ function plotly_series(plt::Plot, series::Series)
|
||||
end
|
||||
|
||||
function plotly_series_shapes(plt::Plot, series::Series, clims)
|
||||
segments = iter_segments(series)
|
||||
segments = collect(series_segments(series))
|
||||
plotattributes_outs = Vector{KW}(undef, length(segments))
|
||||
|
||||
# TODO: create a plotattributes_out for each polygon
|
||||
@ -662,7 +662,8 @@ function plotly_series_shapes(plt::Plot, series::Series, clims)
|
||||
for (letter, data) in zip((:x, :y), shape_data(series, 100))
|
||||
)
|
||||
|
||||
for (i,rng) in enumerate(segments)
|
||||
for segment in segments
|
||||
i, rng = segment.attr_index, segment.range
|
||||
length(rng) < 2 && continue
|
||||
|
||||
# to draw polygons, we actually draw lines with fill
|
||||
@ -705,14 +706,16 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
hasfillrange = st in (:path, :scatter, :scattergl, :straightline) &&
|
||||
(isa(series[:fillrange], AbstractVector) || isa(series[:fillrange], Tuple))
|
||||
|
||||
segments = iter_segments(series, st)
|
||||
segments = collect(series_segments(series, st))
|
||||
plotattributes_outs = fill(KW(), (hasfillrange ? 2 : 1 ) * length(segments))
|
||||
|
||||
needs_scatter_fix = !isscatter && hasmarker && !any(isnan,y) && length(segments) > 1
|
||||
|
||||
for (i,rng) in enumerate(segments)
|
||||
for (k, segment) in enumerate(segments)
|
||||
i, rng = segment.attr_index, segment.range
|
||||
|
||||
plotattributes_out = deepcopy(plotattributes_base)
|
||||
plotattributes_out[:showlegend] = i==1 ? should_add_to_legend(series) : false
|
||||
plotattributes_out[:showlegend] = k==1 ? should_add_to_legend(series) : false
|
||||
plotattributes_out[:legendgroup] = series[:label]
|
||||
|
||||
# set the type
|
||||
@ -805,9 +808,9 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
|
||||
delete!(plotattributes_out, :fillcolor)
|
||||
end
|
||||
|
||||
plotattributes_outs[(2 * i - 1):(2 * i)] = [plotattributes_out_fillrange, plotattributes_out]
|
||||
plotattributes_outs[(2k-1):(2k)] = [plotattributes_out_fillrange, plotattributes_out]
|
||||
else
|
||||
plotattributes_outs[i] = plotattributes_out
|
||||
plotattributes_outs[k] = plotattributes_out
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -440,9 +440,10 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
# end
|
||||
# push!(handles, handle)
|
||||
# else
|
||||
for (i, rng) in enumerate(iter_segments(series, st))
|
||||
for (k, segment) in enumerate(series_segments(series, st))
|
||||
i, rng = segment.attr_index, segment.range
|
||||
handle = ax."plot"((arg[rng] for arg in xyargs)...;
|
||||
label = i == 1 ? series[:label] : "",
|
||||
label = k == 1 ? series[:label] : "",
|
||||
zorder = series[:series_plotindex],
|
||||
color = py_color(single_color(get_linecolor(series, clims, i)), get_linealpha(series, i)),
|
||||
linewidth = py_thickness_scale(plt, get_linewidth(series, i)),
|
||||
@ -486,7 +487,8 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
if series[:markershape] != :none && st in (
|
||||
:path, :scatter, :path3d, :scatter3d, :steppre, :steppost, :bar
|
||||
)
|
||||
for (i, rng) in enumerate(iter_segments(series, :scatter))
|
||||
for segment in series_segments(series, :scatter)
|
||||
i, rng = segment.attr_index, segment.range
|
||||
xyargs = if st == :bar && !isvertical(series)
|
||||
if RecipesPipeline.is3d(sp)
|
||||
y[rng], x[rng], z[rng]
|
||||
@ -679,7 +681,8 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
if st == :shape
|
||||
handle = []
|
||||
for (i, rng) in enumerate(iter_segments(series))
|
||||
for segment in series_segments(series)
|
||||
i, rng = segment.attr_index, segment.range
|
||||
if length(rng) > 1
|
||||
path = pypath."Path"(hcat(x[rng], y[rng]))
|
||||
patches = pypatches."PathPatch"(
|
||||
@ -706,7 +709,8 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
# handle area filling
|
||||
fillrange = series[:fillrange]
|
||||
if fillrange !== nothing && st != :contour
|
||||
for (i, rng) in enumerate(iter_segments(series))
|
||||
for segment in series_segments(series)
|
||||
i, rng = segment.attr_index, segment.range
|
||||
f, dim1, dim2 = if isvertical(series)
|
||||
:fill_between, x[rng], y[rng]
|
||||
else
|
||||
|
||||
@ -42,15 +42,11 @@ function coords(shape::Shape)
|
||||
shape.x, shape.y
|
||||
end
|
||||
|
||||
#coords(shapes::AVec{Shape}) = unzip(map(coords, shapes))
|
||||
function coords(shapes::AVec{Shape})
|
||||
length(shapes) == 0 && return zeros(0), zeros(0)
|
||||
xs = map(get_xs, shapes)
|
||||
ys = map(get_ys, shapes)
|
||||
x, y = map(copy, coords(shapes[1]))
|
||||
for shape in shapes[2:end]
|
||||
nanappend!(x, shape.x)
|
||||
nanappend!(y, shape.y)
|
||||
end
|
||||
c = map(coords, shapes)
|
||||
x = [q[1] for q in c]
|
||||
y = [q[2] for q in c]
|
||||
x, y
|
||||
end
|
||||
|
||||
|
||||
@ -1031,13 +1031,26 @@ const _examples = PlotExample[
|
||||
[quote
|
||||
yv = ones(9)
|
||||
ys = [1; 1; NaN; ones(6)]
|
||||
plot(
|
||||
5 .- [yv 2ys 3yv 4ys],
|
||||
y = 5 .- [yv 2ys 3yv 4ys]
|
||||
|
||||
plt_color_rows = plot(
|
||||
y,
|
||||
seriestype = [:path :path :scatter :scatter],
|
||||
markershape = [:utriangle, :rect],
|
||||
markersize = 8,
|
||||
color = [:red, :black],
|
||||
)
|
||||
|
||||
plt_z_cols = plot(
|
||||
y,
|
||||
markershape = [:utriangle :x :circle :square],
|
||||
markersize = [5 10 10 5],
|
||||
marker_z = [5 4 3 2],
|
||||
line_z = [1 3 3 1],
|
||||
linewidth = [1 10 5 1]
|
||||
)
|
||||
|
||||
plot(plt_color_rows, plt_z_cols)
|
||||
end]
|
||||
),
|
||||
PlotExample( # 49
|
||||
|
||||
@ -1364,6 +1364,16 @@ end
|
||||
|
||||
@recipe function f(shapes::AVec{Shape})
|
||||
seriestype --> :shape
|
||||
# For backwards compatibility, column vectors of segmenting attributes are
|
||||
# interpreted as having one element per shape
|
||||
for attr in union(_segmenting_array_attributes, _segmenting_vector_attributes)
|
||||
v = get(plotattributes, attr, nothing)
|
||||
if v isa AVec || v isa AMat && size(v,2) == 1
|
||||
@warn "Column vector attribute `$attr` reinterpreted as row vector (one value per shape).\n" *
|
||||
"Pass a row vector instead (e.g. using `permutedims`) to suppress this warning."
|
||||
plotattributes[attr] = permutedims(v)
|
||||
end
|
||||
end
|
||||
coords(shapes)
|
||||
end
|
||||
|
||||
|
||||
96
src/utils.jl
96
src/utils.jl
@ -53,10 +53,16 @@ function Base.push!(segments::Segments{T}, vs::AVec) where T
|
||||
end
|
||||
|
||||
|
||||
struct SeriesSegment
|
||||
# indexes of this segement in series data vectors
|
||||
range::UnitRange
|
||||
# index into vector-valued attributes corresponding to this segment
|
||||
attr_index::Int
|
||||
end
|
||||
|
||||
# -----------------------------------------------------
|
||||
# helper to manage NaN-separated segments
|
||||
|
||||
mutable struct SegmentsIterator
|
||||
struct NaNSegmentsIterator
|
||||
args::Tuple
|
||||
n1::Int
|
||||
n2::Int
|
||||
@ -66,29 +72,47 @@ function iter_segments(args...)
|
||||
tup = Plots.wraptuple(args)
|
||||
n1 = minimum(map(firstindex, tup))
|
||||
n2 = maximum(map(lastindex, tup))
|
||||
SegmentsIterator(tup, n1, n2)
|
||||
NaNSegmentsIterator(tup, n1, n2)
|
||||
end
|
||||
|
||||
function iter_segments(series::Series, seriestype::Symbol = :path)
|
||||
function series_segments(series::Series, seriestype::Symbol = :path)
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
if x === nothing
|
||||
return UnitRange{Int}[]
|
||||
elseif has_attribute_segments(series)
|
||||
if any(isnan,y)
|
||||
return [iter_segments(y)...]
|
||||
elseif seriestype in (:scatter, :scatter3d)
|
||||
return [[i] for i in eachindex(y)]
|
||||
else
|
||||
return [i:(i + 1) for i in firstindex(y):lastindex(y)-1]
|
||||
end
|
||||
else
|
||||
segs = UnitRange{Int}[]
|
||||
x === nothing && return UnitRange{Int}[]
|
||||
|
||||
args = RecipesPipeline.is3d(series) ? (x, y, z) : (x, y)
|
||||
for seg in iter_segments(args...)
|
||||
push!(segs, seg)
|
||||
nan_segments = collect(iter_segments(args...))
|
||||
|
||||
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
|
||||
(SeriesSegment(i:i+1, i) for i in first(r):last(r)-1)
|
||||
end
|
||||
return segs
|
||||
end)
|
||||
else
|
||||
(SeriesSegment(r, 1) for r in nan_segments)
|
||||
end
|
||||
|
||||
seg_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_range
|
||||
@warn "Indices $(eachindex(v)) of attribute `$attr` does not match data indices $seg_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
|
||||
@ -97,7 +121,7 @@ anynan(args::Tuple) = i -> anynan(i,args)
|
||||
anynan(istart::Int, iend::Int, args::Tuple) = any(anynan(args), istart:iend)
|
||||
allnan(istart::Int, iend::Int, args::Tuple) = all(anynan(args), istart:iend)
|
||||
|
||||
function Base.iterate(itr::SegmentsIterator, nextidx::Int = itr.n1)
|
||||
function Base.iterate(itr::NaNSegmentsIterator, nextidx::Int = itr.n1)
|
||||
i = findfirst(!anynan(itr.args), nextidx:itr.n2)
|
||||
i === nothing && return nothing
|
||||
nextval = nextidx + i - 1
|
||||
@ -107,6 +131,7 @@ function Base.iterate(itr::SegmentsIterator, nextidx::Int = itr.n1)
|
||||
|
||||
nextval:nextnan-1, nextnan
|
||||
end
|
||||
Base.IteratorSize(::NaNSegmentsIterator) = Base.SizeUnknown()
|
||||
|
||||
# Find minimal type that can contain NaN and x
|
||||
# To allow use of NaN separated segments with categorical x axis
|
||||
@ -572,20 +597,7 @@ function get_markerstrokewidth(series, i::Int = 1)
|
||||
_cycle(series[:markerstrokewidth], i)
|
||||
end
|
||||
|
||||
function has_attribute_segments(series::Series)
|
||||
# we want to check if a series needs to be split into segments just because
|
||||
# of its attributes
|
||||
for letter in (:x, :y, :z)
|
||||
# If we have NaNs in the data they define the segments and
|
||||
# SegmentsIterator is used
|
||||
series[letter] !== nothing && NaN in collect(series[letter]) && return false
|
||||
end
|
||||
series[:seriestype] == :shape && return false
|
||||
# ... else we check relevant attributes if they have multiple inputs
|
||||
return any(
|
||||
(typeof(series[attr]) <: AbstractVector && length(series[attr]) > 1)
|
||||
for
|
||||
attr in [
|
||||
const _segmenting_vector_attributes = (
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linecolor,
|
||||
@ -601,10 +613,18 @@ function has_attribute_segments(series::Series)
|
||||
:markerstrokealpha,
|
||||
:markerstrokewidth,
|
||||
:markershape,
|
||||
]
|
||||
) || any(
|
||||
typeof(series[attr]) <: AbstractArray for attr in (:line_z, :fill_z, :marker_z)
|
||||
)
|
||||
)
|
||||
|
||||
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 attr in _segmenting_vector_attributes
|
||||
) || any(series[attr] isa AbstractArray for attr in _segmenting_array_attributes)
|
||||
end
|
||||
|
||||
function get_aspect_ratio(sp)
|
||||
|
||||
@ -175,14 +175,8 @@ end
|
||||
@test_throws ArgumentError gif(anim)
|
||||
end
|
||||
|
||||
@testset "Segments" begin
|
||||
function segments(args...)
|
||||
segs = UnitRange{Int}[]
|
||||
for seg in iter_segments(args...)
|
||||
push!(segs,seg)
|
||||
end
|
||||
segs
|
||||
end
|
||||
@testset "NaN-separated Segments" begin
|
||||
segments(args...) = collect(iter_segments(args...))
|
||||
|
||||
nan10 = fill(NaN,10)
|
||||
@test segments(11:20) == [1:10]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user