plotly redesign for splitting shapes

This commit is contained in:
Thomas Breloff 2016-07-09 13:30:39 -04:00
parent 1cb0c0071b
commit 5407fa73f8
2 changed files with 104 additions and 61 deletions

View File

@ -336,8 +336,12 @@ plotly_data{R<:Rational}(v::AbstractArray{R}) = float(v)
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict) # get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
function plotly_series(plt::Plot, series::Series) function plotly_series(plt::Plot, series::Series)
d = series.d st = series[:seriestype]
sp = d[:subplot] if st == :shape
return plotly_series_shapes(plt, series)
end
sp = series[:subplot]
d_out = KW() d_out = KW()
# these are the axes that the series should be mapped to # these are the axes that the series should be mapped to
@ -346,12 +350,11 @@ function plotly_series(plt::Plot, series::Series)
d_out[:yaxis] = "y$spidx" d_out[:yaxis] = "y$spidx"
d_out[:showlegend] = should_add_to_legend(series) d_out[:showlegend] = should_add_to_legend(series)
x, y = plotly_data(d[:x]), plotly_data(d[:y]) x, y = plotly_data(series[:x]), plotly_data(series[:y])
d_out[:name] = d[:label] d_out[:name] = series[:label]
st = d[:seriestype]
isscatter = st in (:scatter, :scatter3d, :scattergl) isscatter = st in (:scatter, :scatter3d, :scattergl)
hasmarker = isscatter || d[:markershape] != :none hasmarker = isscatter || series[:markershape] != :none
# hasline = !isscatter
hasline = st in (:path, :path3d) hasline = st in (:path, :path3d)
# set the "type" # set the "type"
@ -362,51 +365,36 @@ function plotly_series(plt::Plot, series::Series)
else else
hasline ? "lines" : "none" hasline ? "lines" : "none"
end end
if d[:fillrange] == true || d[:fillrange] == 0 if series[:fillrange] == true || series[:fillrange] == 0
d_out[:fill] = "tozeroy" d_out[:fill] = "tozeroy"
d_out[:fillcolor] = rgba_string(d[:fillcolor]) d_out[:fillcolor] = rgba_string(series[:fillcolor])
elseif !(d[:fillrange] in (false, nothing)) elseif !(series[:fillrange] in (false, nothing))
warn("fillrange ignored... plotly only supports filling to zero. fillrange: $(d[:fillrange])") warn("fillrange ignored... plotly only supports filling to zero. fillrange: $(series[:fillrange])")
end end
d_out[:x], d_out[:y] = x, y d_out[:x], d_out[:y] = x, y
elseif st == :shape
# to draw polygons, we actually draw lines with fill
d_out[:type] = "scatter"
d_out[:mode] = "lines"
d_out[:x], d_out[:y] = plotly_close_shapes(x, y)
d_out[:fill] = "tozeroy"
d_out[:fillcolor] = rgba_string(d[:fillcolor])
if d[:markerstrokewidth] > 0
d_out[:line] = KW(
:color => rgba_string(d[:linecolor]),
:width => d[:linewidth],
:dash => string(d[:linestyle]),
)
end
elseif st == :bar elseif st == :bar
d_out[:type] = "bar" d_out[:type] = "bar"
d_out[:x], d_out[:y] = x, y d_out[:x], d_out[:y] = x, y
d_out[:orientation] = isvertical(d) ? "v" : "h" d_out[:orientation] = isvertical(series) ? "v" : "h"
elseif st == :heatmap elseif st == :heatmap
d_out[:type] = "heatmap" d_out[:type] = "heatmap"
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false) d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha]) d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
elseif st == :contour elseif st == :contour
d_out[:type] = "contour" d_out[:type] = "contour"
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false) d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
# d_out[:showscale] = d[:colorbar] != :none # d_out[:showscale] = series[:colorbar] != :none
d_out[:ncontours] = d[:levels] d_out[:ncontours] = series[:levels]
d_out[:contours] = KW(:coloring => d[:fillrange] != nothing ? "fill" : "lines") d_out[:contours] = KW(:coloring => series[:fillrange] != nothing ? "fill" : "lines")
d_out[:colorscale] = plotly_colorscale(d[:linecolor], d[:linealpha]) d_out[:colorscale] = plotly_colorscale(series[:linecolor], series[:linealpha])
elseif st in (:surface, :wireframe) elseif st in (:surface, :wireframe)
d_out[:type] = "surface" d_out[:type] = "surface"
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false) d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha]) d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
elseif st == :pie elseif st == :pie
d_out[:type] = "pie" d_out[:type] = "pie"
@ -422,7 +410,7 @@ function plotly_series(plt::Plot, series::Series)
hasline ? "lines" : "none" hasline ? "lines" : "none"
end end
d_out[:x], d_out[:y] = x, y d_out[:x], d_out[:y] = x, y
d_out[:z] = plotly_data(d[:z]) d_out[:z] = plotly_data(series[:z])
else else
warn("Plotly: seriestype $st isn't supported.") warn("Plotly: seriestype $st isn't supported.")
@ -432,32 +420,32 @@ function plotly_series(plt::Plot, series::Series)
# add "marker" # add "marker"
if hasmarker if hasmarker
d_out[:marker] = KW( d_out[:marker] = KW(
:symbol => get(_plotly_markers, d[:markershape], string(d[:markershape])), :symbol => get(_plotly_markers, series[:markershape], string(series[:markershape])),
# :opacity => d[:markeralpha], # :opacity => series[:markeralpha],
:size => 2 * d[:markersize], :size => 2 * series[:markersize],
# :color => rgba_string(d[:markercolor]), # :color => rgba_string(series[:markercolor]),
:line => KW( :line => KW(
:color => rgba_string(d[:markerstrokecolor]), :color => rgba_string(series[:markerstrokecolor]),
:width => d[:markerstrokewidth], :width => series[:markerstrokewidth],
), ),
) )
# gotta hack this (for now?) since plotly can't handle rgba values inside the gradient # gotta hack this (for now?) since plotly can't handle rgba values inside the gradient
d_out[:marker][:color] = if d[:marker_z] == nothing d_out[:marker][:color] = if series[:marker_z] == nothing
rgba_string(d[:markercolor]) rgba_string(series[:markercolor])
else else
# grad = ColorGradient(d[:markercolor], alpha=d[:markeralpha]) # grad = ColorGradient(series[:markercolor], alpha=series[:markeralpha])
grad = d[:markercolor] grad = series[:markercolor]
zmin, zmax = extrema(d[:marker_z]) zmin, zmax = extrema(series[:marker_z])
[rgba_string(grad[(zi - zmin) / (zmax - zmin)]) for zi in d[:marker_z]] [rgba_string(grad[(zi - zmin) / (zmax - zmin)]) for zi in series[:marker_z]]
end end
end end
# add "line" # add "line"
if hasline if hasline
d_out[:line] = KW( d_out[:line] = KW(
:color => rgba_string(d[:linecolor]), :color => rgba_string(series[:linecolor]),
:width => d[:linewidth], :width => series[:linewidth],
:shape => if st == :steppre :shape => if st == :steppre
"vh" "vh"
elseif st == :steppost elseif st == :steppost
@ -465,32 +453,85 @@ function plotly_series(plt::Plot, series::Series)
else else
"linear" "linear"
end, end,
:dash => string(d[:linestyle]), :dash => string(series[:linestyle]),
# :dash => "solid", # :dash => "solid",
) )
end end
plotly_polar!(d_out, series)
plotly_hover!(d_out, series[:hover])
[d_out]
end
function plotly_series_shapes(plt::Plot, series::Series)
d_outs = []
# TODO: create a d_out for each polygon
# x, y = series[:x], series[:y]
# these are the axes that the series should be mapped to
spidx = plotly_subplot_index(series[:subplot])
base_d = KW()
base_d[:xaxis] = "x$spidx"
base_d[:yaxis] = "y$spidx"
base_d[:name] = series[:label]
# base_d[:legendgroup] = series[:label]
x, y = plotly_data(series[:x]), plotly_data(series[:y])
for (i,rng) in enumerate(iter_segments(x,y))
length(rng) < 2 && continue
# to draw polygons, we actually draw lines with fill
d_out = merge(base_d, KW(
:type => "scatter",
:mode => "lines",
:x => vcat(x[rng], x[rng[1]]),
:y => vcat(y[rng], y[rng[1]]),
:fill => "tozeroy",
:fillcolor => rgba_string(cycle(series[:fillcolor], i)),
))
if series[:markerstrokewidth] > 0
d_out[:line] = KW(
:color => rgba_string(cycle(series[:linecolor], i)),
:width => series[:linewidth],
:dash => string(series[:linestyle]),
)
end
d_out[:showlegend] = i==1 ? should_add_to_legend(series) : false
plotly_polar!(d_out, series)
plotly_hover!(d_out, cycle(series[:hover], i))
push!(d_outs, d_out)
end
d_outs
end
function plotly_polar!(d_out::KW, series::Series)
# convert polar plots x/y to theta/radius # convert polar plots x/y to theta/radius
if ispolar(d[:subplot]) if ispolar(series[:subplot])
d_out[:t] = rad2deg(pop!(d_out, :x)) d_out[:t] = rad2deg(pop!(d_out, :x))
d_out[:r] = pop!(d_out, :y) d_out[:r] = pop!(d_out, :y)
end end
end
function plotly_hover!(d_out::KW, hover)
# hover text # hover text
hover = d[:hover]
if hover in (:none, false) if hover in (:none, false)
d_out[:hoverinfo] = "none" d_out[:hoverinfo] = "none"
elseif hover != nothing elseif hover != nothing
d_out[:hoverinfo] = "text" d_out[:hoverinfo] = "text"
d_out[:text] = hover d_out[:text] = hover
end end
d_out
end end
# get a list of dictionaries, each representing the series params # get a list of dictionaries, each representing the series params
function plotly_series_json(plt::Plot) function plotly_series_json(plt::Plot)
JSON.json(map(series -> plotly_series(plt, series), plt.series_list)) slist = []
for series in plt.series_list
append!(slist, plotly_series(plt, series))
end
JSON.json(slist)
# JSON.json(map(series -> plotly_series(plt, series), plt.series_list))
end end
# ---------------------------------------------------------------- # ----------------------------------------------------------------

View File

@ -33,10 +33,12 @@ end
function _series_added(plt::Plot{PlotlyJSBackend}, series::Series) function _series_added(plt::Plot{PlotlyJSBackend}, series::Series)
syncplot = plt.o syncplot = plt.o
pdict = plotly_series(plt, series) pdicts = plotly_series(plt, series)
typ = pop!(pdict, :type) for pdict in pdicts
gt = PlotlyJS.GenericTrace(typ; pdict...) typ = pop!(pdict, :type)
PlotlyJS.addtraces!(syncplot, gt) gt = PlotlyJS.GenericTrace(typ; pdict...)
PlotlyJS.addtraces!(syncplot, gt)
end
end end
function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series) function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)