diff --git a/src/backends/gr.jl b/src/backends/gr.jl index 6a53756a..9dc305f9 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -348,12 +348,15 @@ function gr_draw_markers( shapes = series[:markershape] if shapes != :none - for (i, rng) in enumerate(iter_segments(series)) - ms = get_thickness_scaling(series) * _cycle(msize, i) - msw = get_thickness_scaling(series) * _cycle(strokewidth, i) - shape = _cycle(shapes, i) - for j in rng - gr_draw_marker(series, _cycle(x, j), _cycle(y, j), clims, i, ms, msw, shape) + for (i, rng) in enumerate(iter_segments(series, :scatter)) + rng = intersect(eachindex(x), rng) + if !isempty(rng) + ms = get_thickness_scaling(series) * _cycle(msize, i) + msw = get_thickness_scaling(series) * _cycle(strokewidth, i) + shape = _cycle(shapes, i) + for j in rng + gr_draw_marker(series, _cycle(x, j), _cycle(y, j), clims, i, ms, msw, shape) + end end end end @@ -1634,7 +1637,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) if st in (:path, :scatter, :straightline) if x !== nothing && length(x) > 1 lz = series[:line_z] - segments = iter_segments(series) + segments = iter_segments(series, st) # do area fill if frng !== nothing GR.setfillintstyle(GR.INTSTYLE_SOLID) @@ -1759,7 +1762,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) if st == :path3d if length(x) > 1 lz = series[:line_z] - segments = iter_segments(series) + segments = iter_segments(series, st) for (i, rng) in enumerate(segments) lc = get_linecolor(series, clims, i) gr_set_line( diff --git a/src/backends/pgfplotsx.jl b/src/backends/pgfplotsx.jl index d9476914..bcdec6b5 100644 --- a/src/backends/pgfplotsx.jl +++ b/src/backends/pgfplotsx.jl @@ -299,13 +299,13 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend}) series[:z], )...) else - iter_segments(series) + iter_segments(series, st) end for (i, rng) in enumerate(segments) segment_opt = PGFPlotsX.Options() segment_opt = merge(segment_opt, pgfx_linestyle(opt, i)) if opt[:markershape] != :none - marker = opt[:markershape] + marker = _cycle(opt[:markershape], i) if marker isa Shape x = marker.x y = marker.y diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl index c03f9b30..4847f15f 100644 --- a/src/backends/plotly.jl +++ b/src/backends/plotly.jl @@ -694,9 +694,11 @@ 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) + segments = iter_segments(series, st) plotattributes_outs = fill(KW(), (hasfillrange ? 2 : 1 ) * length(segments)) + needs_scatter_fix = !isscatter && hasmarker && !any(isnan,y) + for (i,rng) in enumerate(segments) plotattributes_out = deepcopy(plotattributes_base) plotattributes_out[:showlegend] = i==1 ? should_add_to_legend(series) : false @@ -733,13 +735,15 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z # add "marker" if hasmarker + mcolor = rgba_string(plot_color(get_markercolor(series, clims, i), get_markeralpha(series, i))) + lcolor = rgba_string(plot_color(get_markerstrokecolor(series, i), get_markerstrokealpha(series, i))) plotattributes_out[:marker] = KW( :symbol => get_plotly_marker(_cycle(series[:markershape], i), string(_cycle(series[:markershape], i))), - # :opacity => series[:markeralpha], + # :opacity => needs_scatter_fix ? [1, 0] : 1, :size => 2 * _cycle(series[:markersize], i), - :color => rgba_string(plot_color(get_markercolor(series, clims, i), get_markeralpha(series, i))), + :color => needs_scatter_fix ? [mcolor, "rgba(0, 0, 0, 0.000)"] : mcolor, :line => KW( - :color => rgba_string(plot_color(get_markerstrokecolor(series, i), get_markerstrokealpha(series, i))), + :color => needs_scatter_fix ? [lcolor, "rgba(0, 0, 0, 0.000)"] : lcolor, :width => _cycle(series[:markerstrokewidth], i), ), ) diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index b056291d..10e1bd51 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -398,7 +398,6 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series) # line plot if st in (:path, :path3d, :steppre, :steppost, :straightline) if maximum(series[:linewidth]) > 0 - segments = iter_segments(series) # TODO: check LineCollection alternative for speed # if length(segments) > 1 && (any(typeof(series[attr]) <: AbstractVector for attr in (:fillcolor, :fillalpha)) || series[:fill_z] !== nothing) && !(typeof(series[:linestyle]) <: AbstractVector) # # multicolored line segments @@ -429,7 +428,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series) # end # push!(handles, handle) # else - for (i, rng) in enumerate(iter_segments(series)) + for (i, rng) in enumerate(iter_segments(series, st)) handle = ax."plot"((arg[rng] for arg in xyargs)...; label = i == 1 ? series[:label] : "", zorder = series[:series_plotindex], @@ -472,130 +471,24 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series) end # add markers? - if series[:markershape] != :none && st in (:path, :scatter, :path3d, - :scatter3d, :steppre, :steppost, - :bar) - markercolor = if any(typeof(series[arg]) <: AVec for arg in (:markercolor, :markeralpha)) || series[:marker_z] !== nothing - # py_color(plot_color.(get_markercolor.(series, clims, eachindex(x)), get_markeralpha.(series, eachindex(x)))) - [py_color(plot_color(get_markercolor(series, clims, i), get_markeralpha(series, i))) for i in eachindex(x)] - else - py_color(plot_color(series[:markercolor], series[:markeralpha])) - end - extrakw[:c] = if markercolor isa Array - permutedims(hcat([[m...] for m in markercolor]...),[2,1]) - elseif markercolor isa Tuple - reshape([markercolor...], 1, length(markercolor)) - else - error("This case is not handled. Please file an issue.") - end - xyargs = if st == :bar && !isvertical(series) - (y, x) - else - xyargs - end - - if isa(series[:markershape], AbstractVector{Shape}) - # this section will create one scatter per data point to accommodate the - # vector of shapes - handle = [] - x,y = xyargs - shapes = series[:markershape] - msc = py_color(get_markerstrokecolor(series), get_markerstrokealpha(series)) - lw = py_thickness_scale(plt, series[:markerstrokewidth]) - for i=eachindex(y) - if series[:marker_z] !== nothing - extrakw[:c] = [py_color(get_markercolor(series, i), get_markercoloralpha(series, i))] - end - - push!(handle, ax."scatter"(_cycle(x,i), _cycle(y,i); - label = series[:label], - zorder = series[:series_plotindex] + 0.5, - marker = py_marker(_cycle(shapes,i)), - s = py_thickness_scale(plt, _cycle(series[:markersize],i)).^ 2, - facecolors = py_color(get_markercolor(series, i), get_markercoloralpha(series, i)), - edgecolors = msc, - linewidths = lw, - extrakw... - )) - end - push!(handles, handle) - elseif isa(series[:markershape], AbstractVector{Symbol}) - handle = [] - x,y = xyargs - shapes = series[:markershape] - - prev_marker = py_marker(_cycle(shapes,1)) - - cur_x_list = [] - cur_y_list = [] - - cur_color_list = [] - cur_scale_list = [] - - delete!(extrakw, :c) - - for i=eachindex(y) - cur_marker = py_marker(_cycle(shapes,i)) - - if ( cur_marker == prev_marker ) - push!(cur_x_list, _cycle(x,i)) - push!(cur_y_list, _cycle(y,i)) - - push!(cur_color_list, _cycle(markercolor, i)) - push!(cur_scale_list, py_thickness_scale(plt, _cycle(series[:markersize],i)).^ 2) - - continue - end - - push!(handle, ax."scatter"(cur_x_list, cur_y_list; - label = series[:label], - zorder = series[:series_plotindex] + 0.5, - marker = prev_marker, - s = cur_scale_list, - edgecolors = py_color(get_markerstrokecolor(series), get_markerstrokealpha(series)), - linewidths = py_thickness_scale(plt, series[:markerstrokewidth]), - facecolors = cur_color_list, - extrakw... - )) - - cur_x_list = [_cycle(x,i)] - cur_y_list = [_cycle(y,i)] - - cur_color_list = [_cycle(markercolor, i)] - cur_scale_list = [py_thickness_scale(plt, _cycle(series[:markersize],i)) .^ 2] - - prev_marker = cur_marker - end - - if !isempty(cur_color_list) - push!(handle, ax."scatter"(cur_x_list, cur_y_list; - label = series[:label], - zorder = series[:series_plotindex] + 0.5, - marker = prev_marker, - s = cur_scale_list, - edgecolors = py_color(get_markerstrokecolor(series), get_markerstrokealpha(series)), - linewidths = py_thickness_scale(plt, series[:markerstrokewidth]), - facecolors = cur_color_list, - extrakw... - )) - end - - push!(handles, handle) - else - # do a normal scatter plot - - # Add depthshade option for 3d plots - if RecipesPipeline.is3d(sp) - extrakw[:depthshade] = get(series[:extra_kwargs], :depthshade, false) + if series[:markershape] != :none && st in ( + :path, :scatter, :path3d, :scatter3d, :steppre, :steppost, :bar + ) + for (i, rng) in enumerate(iter_segments(series, :scatter)) + xyargs = if st == :bar && !isvertical(series) + y[rng], x[rng] + else + x[rng], y[rng] end handle = ax."scatter"(xyargs...; label = series[:label], zorder = series[:series_plotindex] + 0.5, - marker = py_marker(series[:markershape]), - s = py_thickness_scale(plt, series[:markersize]) .^2, - edgecolors = py_color(get_markerstrokecolor(series), get_markerstrokealpha(series)), - linewidths = py_thickness_scale(plt, series[:markerstrokewidth]), + marker = py_marker(_cycle(series[:markershape], i)), + s = py_thickness_scale(plt, _cycle(series[:markersize], i)).^ 2, + facecolors = py_color(get_markercolor(series, i), get_markeralpha(series, i)), + edgecolors = py_color(get_markerstrokecolor(series, i), get_markerstrokealpha(series, i)), + linewidths = py_thickness_scale(plt, get_markerstrokewidth(series, i)), extrakw... ) push!(handles, handle) diff --git a/src/utils.jl b/src/utils.jl index 59141965..a446eac9 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -67,19 +67,17 @@ function iter_segments(args...) SegmentsIterator(tup, n1, n2) end -function iter_segments(series::Series) +function iter_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 series[:seriestype] in (:scatter, :scatter3d) + if any(isnan,y) + return [iter_segments(y)...] + elseif seriestype in (:scatter, :scatter3d) return [[i] for i in eachindex(y)] else - if any(isnan,y) - return [iter_segments(y)...] - else - return [i:(i + 1) for i in firstindex(y):lastindex(y)-1] - end + return [i:(i + 1) for i in firstindex(y):lastindex(y)-1] end else segs = UnitRange{Int}[] @@ -617,6 +615,7 @@ function has_attribute_segments(series::Series) :markerstrokecolor, :markerstrokealpha, :markerstrokewidth, + :markershape, ] ) || any( typeof(series[attr]) <: AbstractArray for attr in (:line_z, :fill_z, :marker_z)