Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
a301366ef6
@ -20,6 +20,10 @@ addons:
|
||||
- xauth
|
||||
- xvfb
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.julia/artifacts
|
||||
|
||||
sudo: required
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then pwd ; fi
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
name = "Plots"
|
||||
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||
author = ["Tom Breloff (@tbreloff)"]
|
||||
version = "0.29.3"
|
||||
version = "0.29.6"
|
||||
|
||||
[deps]
|
||||
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||
@ -35,7 +35,7 @@ Contour = "0.5"
|
||||
FFMPEG = "0.2, 0.3"
|
||||
FixedPointNumbers = "0.6, 0.7, 0.8"
|
||||
GR = "0.46, 0.47"
|
||||
GeometryTypes = "0.7"
|
||||
GeometryTypes = "0.7, 0.8"
|
||||
JSON = "0.21"
|
||||
Measures = "0.3"
|
||||
NaNMath = "0.3"
|
||||
|
||||
@ -20,6 +20,9 @@ branches:
|
||||
- master
|
||||
- /release-.*/
|
||||
|
||||
cache:
|
||||
- '%USERPROFILE%\.julia\artifacts'
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
on_build_success: false
|
||||
|
||||
@ -345,7 +345,7 @@ const _subplot_defaults = KW(
|
||||
:legendtitlefontcolor => :match,
|
||||
:annotations => [], # annotation tuples... list of (x,y,annotation)
|
||||
:projection => :none, # can also be :polar or :3d
|
||||
:aspect_ratio => :none, # choose from :none or :equal
|
||||
:aspect_ratio => :auto, # choose from :none or :equal
|
||||
:margin => 1mm,
|
||||
:left_margin => :match,
|
||||
:top_margin => :match,
|
||||
|
||||
138
src/backends.jl
138
src/backends.jl
@ -673,37 +673,86 @@ const _inspectdr_scale = [:identity, :ln, :log2, :log10]
|
||||
|
||||
const _pgfplotsx_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
:background_color_legend, :background_color_inside, :background_color_outside,
|
||||
:foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
|
||||
:foreground_color_text, :foreground_color_border,
|
||||
:background_color_legend,
|
||||
:background_color_inside,
|
||||
:background_color_outside,
|
||||
:foreground_color_legend,
|
||||
:foreground_color_grid,
|
||||
:foreground_color_axis,
|
||||
:foreground_color_text,
|
||||
:foreground_color_border,
|
||||
:label,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
:markerstrokewidth,
|
||||
:markerstrokecolor,
|
||||
:markerstrokealpha,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
:bins,
|
||||
:layout,
|
||||
:title, :window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip,
|
||||
:title,
|
||||
:window_title,
|
||||
:guide,
|
||||
:lims,
|
||||
:ticks,
|
||||
:scale,
|
||||
:flip,
|
||||
:match_dimensions,
|
||||
:titlefontfamily, :titlefontsize, :titlefonthalign, :titlefontvalign,
|
||||
:titlefontrotation, :titlefontcolor,
|
||||
:legendfontfamily, :legendfontsize, :legendfonthalign, :legendfontvalign,
|
||||
:legendfontrotation, :legendfontcolor,
|
||||
:tickfontfamily, :tickfontsize, :tickfonthalign, :tickfontvalign,
|
||||
:tickfontrotation, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefonthalign, :guidefontvalign,
|
||||
:guidefontrotation, :guidefontcolor,
|
||||
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
||||
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:fill_z, :line_z, :marker_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:titlefontfamily,
|
||||
:titlefontsize,
|
||||
:titlefonthalign,
|
||||
:titlefontvalign,
|
||||
:titlefontrotation,
|
||||
:titlefontcolor,
|
||||
:legendfontfamily,
|
||||
:legendfontsize,
|
||||
:legendfonthalign,
|
||||
:legendfontvalign,
|
||||
:legendfontrotation,
|
||||
:legendfontcolor,
|
||||
:tickfontfamily,
|
||||
:tickfontsize,
|
||||
:tickfonthalign,
|
||||
:tickfontvalign,
|
||||
:tickfontrotation,
|
||||
:tickfontcolor,
|
||||
:guidefontfamily,
|
||||
:guidefontsize,
|
||||
:guidefonthalign,
|
||||
:guidefontvalign,
|
||||
:guidefontrotation,
|
||||
:guidefontcolor,
|
||||
:grid,
|
||||
:gridalpha,
|
||||
:gridstyle,
|
||||
:gridlinewidth,
|
||||
:legend,
|
||||
:legendtitle,
|
||||
:colorbar,
|
||||
:colorbar_title,
|
||||
:colorbar_entry,
|
||||
:fill_z,
|
||||
:line_z,
|
||||
:marker_z,
|
||||
:levels,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
:aspect_ratio,
|
||||
:normalize, :weights,
|
||||
:normalize,
|
||||
:weights,
|
||||
:inset_subplots,
|
||||
:bar_width,
|
||||
:arrow,
|
||||
@ -712,13 +761,42 @@ const _pgfplotsx_attr = merge_with_base_supported([
|
||||
:camera,
|
||||
:contour_labels,
|
||||
])
|
||||
const _pgfplotsx_seriestype =
|
||||
[:path, :scatter, :straightline,
|
||||
:path3d, :scatter3d, :surface, :wireframe,
|
||||
:heatmap, :contour, :contour3d,
|
||||
:shape,
|
||||
:steppre, :stepmid, :steppost, :ysticks, :xsticks]
|
||||
const _pgfplotsx_seriestype = [
|
||||
:path,
|
||||
:scatter,
|
||||
:straightline,
|
||||
:path3d,
|
||||
:scatter3d,
|
||||
:surface,
|
||||
:wireframe,
|
||||
:heatmap,
|
||||
:contour,
|
||||
:contour3d,
|
||||
:shape,
|
||||
:steppre,
|
||||
:stepmid,
|
||||
:steppost,
|
||||
:ysticks,
|
||||
:xsticks,
|
||||
]
|
||||
const _pgfplotsx_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
const _pgfplotsx_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :ltriangle, :rtriangle, :cross, :xcross, :star5, :pentagon, :hline, :vline, Shape]
|
||||
const _pgfplotsx_marker = [
|
||||
:none,
|
||||
:auto,
|
||||
:circle,
|
||||
:rect,
|
||||
:diamond,
|
||||
:utriangle,
|
||||
:dtriangle,
|
||||
:ltriangle,
|
||||
:rtriangle,
|
||||
:cross,
|
||||
:xcross,
|
||||
:star5,
|
||||
:pentagon,
|
||||
:hline,
|
||||
:vline,
|
||||
Shape,
|
||||
]
|
||||
const _pgfplotsx_scale = [:identity, :ln, :log2, :log10]
|
||||
is_marker_supported(::PGFPlotsXBackend, shape::Shape) = true
|
||||
|
||||
@ -305,15 +305,27 @@ end
|
||||
# ---------------------------------------------------------
|
||||
|
||||
# draw ONE Shape
|
||||
function gr_draw_marker(xi, yi, msize, shape::Shape)
|
||||
function gr_draw_marker(series, xi, yi, clims, i, msize, shape::Shape)
|
||||
sx, sy = coords(shape)
|
||||
# convert to ndc coords (percentages of window)
|
||||
GR.selntran(0)
|
||||
w, h = gr_plot_size
|
||||
f = msize / (w + h)
|
||||
xi, yi = GR.wctondc(xi, yi)
|
||||
GR.fillarea(xi .+ sx .* f,
|
||||
yi .+ sy .* f)
|
||||
xs = xi .+ sx .* f
|
||||
ys = yi .+ sy .* f
|
||||
|
||||
# draw the interior
|
||||
mc = get_markercolor(series, clims, i)
|
||||
gr_set_fill(mc)
|
||||
gr_set_transparency(mc, get_markeralpha(series, i))
|
||||
GR.fillarea(xs, ys)
|
||||
|
||||
# draw the shapes
|
||||
msc = get_markerstrokecolor(series, i)
|
||||
gr_set_line(get_markerstrokewidth(series, i), :solid, msc)
|
||||
gr_set_transparency(msc, get_markerstrokealpha(series, i))
|
||||
GR.polyline(xs, ys)
|
||||
GR.selntran(1)
|
||||
end
|
||||
|
||||
@ -323,7 +335,11 @@ function nominal_size()
|
||||
end
|
||||
|
||||
# draw ONE symbol marker
|
||||
function gr_draw_marker(xi, yi, msize::Number, shape::Symbol)
|
||||
function gr_draw_marker(series, xi, yi, clims, i, msize::Number, shape::Symbol)
|
||||
GR.setborderwidth(series[:markerstrokewidth]);
|
||||
gr_set_bordercolor(get_markerstrokecolor(series, i));
|
||||
gr_set_markercolor(get_markercolor(series, clims, i));
|
||||
gr_set_transparency(get_markeralpha(series, i))
|
||||
GR.setmarkertype(gr_markertype[shape])
|
||||
GR.setmarkersize(0.3msize / nominal_size())
|
||||
GR.polymarker([xi], [yi])
|
||||
@ -341,13 +357,7 @@ function gr_draw_markers(series::Series, x, y, clims, msize = series[:markersize
|
||||
for i=eachindex(x)
|
||||
msi = _cycle(msize, i)
|
||||
shape = _cycle(shapes, i)
|
||||
i
|
||||
GR.setborderwidth(series[:markerstrokewidth]);
|
||||
gr_set_bordercolor(get_markerstrokecolor(series, i));
|
||||
gr_set_markercolor(get_markercolor(series, clims, i));
|
||||
gr_set_transparency(get_markeralpha(series, i))
|
||||
|
||||
gr_draw_marker(x[i], y[i], msi, shape)
|
||||
gr_draw_marker(series, x[i], y[i], clims, i, msi, shape)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -971,7 +981,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
data_lims = gr_xy_axislims(sp)
|
||||
xy_lims = data_lims
|
||||
|
||||
ratio = sp[:aspect_ratio]
|
||||
ratio = get_aspect_ratio(sp)
|
||||
if ratio != :none
|
||||
if ratio == :equal
|
||||
ratio = 1
|
||||
@ -1050,11 +1060,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
if st == :pie
|
||||
draw_axes = false
|
||||
end
|
||||
if st == :heatmap
|
||||
if st in (:heatmap, :image)
|
||||
outside_ticks = true
|
||||
for ax in (sp[:xaxis], sp[:yaxis])
|
||||
v = series[ax[:letter]]
|
||||
end
|
||||
x, y = heatmap_edges(series[:x], sp[:xaxis][:scale], series[:y], sp[:yaxis][:scale], size(series[:z]))
|
||||
xy_lims = x[1], x[end], y[1], y[end]
|
||||
expand_extrema!(sp[:xaxis], x)
|
||||
@ -1776,8 +1783,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
elseif st == :image
|
||||
z = transpose_z(series, series[:z].surf, true)'
|
||||
x, y = heatmap_edges(series[:x], sp[:xaxis][:scale], series[:y], sp[:yaxis][:scale], size(z))
|
||||
w, h = size(z)
|
||||
xmin, xmax = ignorenan_extrema(series[:x]); ymin, ymax = ignorenan_extrema(series[:y])
|
||||
xmin, xmax = ignorenan_extrema(x)
|
||||
ymin, ymax = ignorenan_extrema(y)
|
||||
rgba = gr_color.(z)
|
||||
GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
|
||||
end
|
||||
|
||||
@ -470,7 +470,7 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
push!(style, string("title style = {font = ", pgf_font(sp[:titlefontsize], pgf_thickness_scaling(sp)), ", color = ", cstr, ", draw opacity = ", α, ", rotate = ", sp[:titlefontrotation], "}"))
|
||||
end
|
||||
|
||||
if sp[:aspect_ratio] in (1, :equal)
|
||||
if get_aspect_ratio(sp) in (1, :equal)
|
||||
kw[:axisEqual] = "true"
|
||||
end
|
||||
|
||||
|
||||
@ -51,7 +51,7 @@ function pgfx_preamble(pgfx_plot::Plot{PGFPlotsXBackend})
|
||||
old_flag = pgfx_plot.attr[:tex_output_standalone]
|
||||
pgfx_plot.attr[:tex_output_standalone] = true
|
||||
fulltext = String(repr("application/x-tex", pgfx_plot))
|
||||
preamble = fulltext[1:first(findfirst("\\begin{document}", fulltext))-1]
|
||||
preamble = fulltext[1:(first(findfirst("\\begin{document}", fulltext)) - 1)]
|
||||
pgfx_plot.attr[:tex_output_standalone] = old_flag
|
||||
preamble
|
||||
end
|
||||
@ -78,7 +78,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
if !pgfx_plot.is_created
|
||||
the_plot = PGFPlotsX.TikzPicture(PGFPlotsX.Options())
|
||||
bgc = plt.attr[:background_color_outside] == :match ?
|
||||
plt.attr[:background_color] : plt.attr[:background_color_outside]
|
||||
plt.attr[:background_color] : plt.attr[:background_color_outside]
|
||||
if bgc isa Colors.Colorant
|
||||
cstr = plot_color(bgc)
|
||||
a = alpha(cstr)
|
||||
@ -98,7 +98,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
sp_width = width(bb)
|
||||
sp_height = height(bb)
|
||||
dx, dy = bb.x0
|
||||
# TODO: does this hold at every scale?
|
||||
# TODO: does this hold at every scale?
|
||||
if sp[:legend] in (:outertopright, nothing)
|
||||
dx *= 1.2
|
||||
end
|
||||
@ -129,7 +129,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
) => nothing,
|
||||
"fill" => cstr,
|
||||
"fill opacity" => a,
|
||||
"text opacity" => alpha(plot_color(sp[:legendfontcolor])),
|
||||
"text opacity" =>
|
||||
alpha(plot_color(sp[:legendfontcolor])),
|
||||
"font" => pgfx_font(
|
||||
sp[:legendfontsize],
|
||||
pgfx_thickness_scaling(sp),
|
||||
@ -157,11 +158,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
else
|
||||
push!(
|
||||
axis_opt,
|
||||
"legend pos" => get(
|
||||
_pgfplotsx_legend_pos,
|
||||
sp[:legend],
|
||||
"outer north east",
|
||||
),
|
||||
"legend pos" =>
|
||||
get(_pgfplotsx_legend_pos, sp[:legend], "outer north east"),
|
||||
)
|
||||
end
|
||||
for letter in (:x, :y, :z)
|
||||
@ -185,8 +183,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
PGFPlotsX.push_preamble!(
|
||||
pgfx_plot.the_plot,
|
||||
"""\\pgfplotsset{
|
||||
colormap={plots$(sp.attr[:subplot_index])}{$(pgfx_colormap(series.plotattributes[col]))},
|
||||
}""",
|
||||
colormap={plots$(sp.attr[:subplot_index])}{$(pgfx_colormap(series.plotattributes[col]))},
|
||||
}""",
|
||||
)
|
||||
push!(
|
||||
axis_opt,
|
||||
@ -213,8 +211,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
push!(axis_opt, "view" => (azim, elev))
|
||||
end
|
||||
axisf = if sp[:projection] == :polar
|
||||
# push!(axis_opt, "xmin" => 90)
|
||||
# push!(axis_opt, "xmax" => 450)
|
||||
# push!(axis_opt, "xmin" => 90)
|
||||
# push!(axis_opt, "xmax" => 450)
|
||||
PGFPlotsX.PolarAxis
|
||||
else
|
||||
PGFPlotsX.Axis
|
||||
@ -229,20 +227,21 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
sf = series[:fillrange]
|
||||
series_opt = PGFPlotsX.Options(
|
||||
"color" => single_color(opt[:linecolor]),
|
||||
"name path" => string(series_id)
|
||||
"name path" => string(series_id),
|
||||
)
|
||||
if is3d(series) || st == :heatmap
|
||||
series_func = PGFPlotsX.Plot3
|
||||
else
|
||||
series_func = PGFPlotsX.Plot
|
||||
end
|
||||
if sf !== nothing && !isfilledcontour(series) && series[:ribbon] === nothing
|
||||
if sf !== nothing &&
|
||||
!isfilledcontour(series) && series[:ribbon] === nothing
|
||||
push!(series_opt, "area legend" => nothing)
|
||||
end
|
||||
if st == :heatmap
|
||||
push!(axis.options, "view" => "{0}{90}")
|
||||
end
|
||||
# treat segments
|
||||
# treat segments
|
||||
segments =
|
||||
if st in (:wireframe, :heatmap, :contour, :surface, :contour3d)
|
||||
iter_segments(surface_to_vecs(
|
||||
@ -255,6 +254,9 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
end
|
||||
for (i, rng) in enumerate(segments)
|
||||
segment_opt = PGFPlotsX.Options()
|
||||
if opt[:label] == ""
|
||||
push!(segment_opt, "forget plot" => nothing)
|
||||
end
|
||||
segment_opt = merge(segment_opt, pgfx_linestyle(opt, i))
|
||||
if opt[:markershape] != :none
|
||||
marker = opt[:markershape]
|
||||
@ -264,7 +266,10 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
scale_factor = 0.025
|
||||
mark_size = opt[:markersize] * scale_factor
|
||||
path = join(
|
||||
["($(x[i] * mark_size), $(y[i] * mark_size))" for i in eachindex(x)],
|
||||
[
|
||||
"($(x[i] * mark_size), $(y[i] * mark_size))"
|
||||
for i in eachindex(x)
|
||||
],
|
||||
" -- ",
|
||||
)
|
||||
c = get_markercolor(series, i)
|
||||
@ -285,38 +290,45 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
segment_opt = merge(segment_opt, pgfx_fillstyle(opt, i))
|
||||
end
|
||||
# add fillrange
|
||||
if sf !== nothing && !isfilledcontour(series) && series[:ribbon] === nothing
|
||||
if sf !== nothing &&
|
||||
!isfilledcontour(series) && series[:ribbon] === nothing
|
||||
if sf isa Number || sf isa AVec
|
||||
pgfx_fillrange_series!( axis, series, series_func, i, _cycle(sf, rng), rng)
|
||||
pgfx_fillrange_series!(
|
||||
axis,
|
||||
series,
|
||||
series_func,
|
||||
i,
|
||||
_cycle(sf, rng),
|
||||
rng,
|
||||
)
|
||||
end
|
||||
if i == 1 && opt[:label] != "" && sp[:legend] != :none && should_add_to_legend(series)
|
||||
if i == 1 && sp[:legend] != :none && pgfx_should_add_to_legend(series)
|
||||
pgfx_filllegend!(series_opt, opt)
|
||||
end
|
||||
end
|
||||
# series
|
||||
#
|
||||
coordinates = pgfx_series_coordinates!(
|
||||
sp,
|
||||
series,
|
||||
segment_opt,
|
||||
opt,
|
||||
rng,
|
||||
)
|
||||
segment_plot = series_func(
|
||||
merge(series_opt, segment_opt),
|
||||
coordinates,
|
||||
)
|
||||
# series
|
||||
#
|
||||
coordinates =
|
||||
pgfx_series_coordinates!(sp, series, segment_opt, opt, rng)
|
||||
segment_plot =
|
||||
series_func(merge(series_opt, segment_opt), coordinates)
|
||||
push!(axis, segment_plot)
|
||||
# fill between functions
|
||||
if sf isa Tuple && series[:ribbon] === nothing
|
||||
sf1, sf2 = sf
|
||||
@assert sf1 == series_index "First index of the tuple has to match the current series index."
|
||||
push!(axis, series_func(
|
||||
merge(pgfx_fillstyle(opt, series_index), PGFPlotsX.Options("forget plot" => nothing)),
|
||||
"fill between [of=$series_id and $(_pgfplotsx_series_ids[Symbol(string(sf2))])]"
|
||||
))
|
||||
push!(
|
||||
axis,
|
||||
series_func(
|
||||
merge(
|
||||
pgfx_fillstyle(opt, series_index),
|
||||
PGFPlotsX.Options("forget plot" => nothing),
|
||||
),
|
||||
"fill between [of=$series_id and $(_pgfplotsx_series_ids[Symbol(string(sf2))])]",
|
||||
),
|
||||
)
|
||||
end
|
||||
# add ribbons?
|
||||
# add ribbons?
|
||||
ribbon = series[:ribbon]
|
||||
if ribbon !== nothing
|
||||
pgfx_add_ribbons!(
|
||||
@ -327,18 +339,16 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
series_index,
|
||||
)
|
||||
end
|
||||
# add to legend?
|
||||
if i == 1 &&
|
||||
opt[:label] != "" &&
|
||||
sp[:legend] != :none && should_add_to_legend(series)
|
||||
# add to legend?
|
||||
if i == 1 && sp[:legend] != :none && pgfx_should_add_to_legend(series)
|
||||
leg_opt = PGFPlotsX.Options()
|
||||
if ribbon !== nothing
|
||||
pgfx_filllegend!(axis.contents[end-3].options, opt)
|
||||
pgfx_filllegend!(axis.contents[end - 3].options, opt)
|
||||
end
|
||||
legend = PGFPlotsX.LegendEntry(leg_opt, opt[:label], false)
|
||||
push!(axis, legend)
|
||||
end
|
||||
# add series annotations
|
||||
# add series annotations
|
||||
anns = series[:series_annotations]
|
||||
for (xi, yi, str, fnt) in EachAnn(anns, series[:x], series[:y])
|
||||
pgfx_add_annotation!(
|
||||
@ -352,7 +362,11 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
|
||||
end
|
||||
# add subplot annotations
|
||||
for ann in sp[:annotations]
|
||||
pgfx_add_annotation!(axis, locate_annotation(sp, ann...)..., pgfx_thickness_scaling(sp))
|
||||
pgfx_add_annotation!(
|
||||
axis,
|
||||
locate_annotation(sp, ann...)...,
|
||||
pgfx_thickness_scaling(sp),
|
||||
)
|
||||
end
|
||||
end # for series
|
||||
push!(the_plot, axis)
|
||||
@ -495,7 +509,7 @@ function pgfx_series_coordinates!(
|
||||
)
|
||||
push!(
|
||||
segment_opt,
|
||||
"contour prepared" => PGFPlotsX.Options("labels" => opt[:contour_labels],),
|
||||
"contour prepared" => PGFPlotsX.Options("labels" => opt[:contour_labels]),
|
||||
)
|
||||
return PGFPlotsX.Table(Contour.contours(args..., opt[:levels]))
|
||||
end
|
||||
@ -508,7 +522,7 @@ function pgfx_series_coordinates!(
|
||||
xs, ys, zs = collect(args)
|
||||
push!(
|
||||
segment_opt,
|
||||
"contour filled" => PGFPlotsX.Options("labels" => opt[:contour_labels],),
|
||||
"contour filled" => PGFPlotsX.Options("labels" => opt[:contour_labels]),
|
||||
"point meta" => "explicit",
|
||||
"shader" => "flat",
|
||||
)
|
||||
@ -519,10 +533,10 @@ function pgfx_series_coordinates!(
|
||||
end
|
||||
|
||||
cs = join(
|
||||
[join(["($x, $y) [$(zs[j, i])]" for (j, x) in enumerate(xs)], " ") for (
|
||||
i,
|
||||
y,
|
||||
) in enumerate(ys)],
|
||||
[
|
||||
join(["($x, $y) [$(zs[j, i])]" for (j, x) in enumerate(xs)], " ")
|
||||
for (i, y) in enumerate(ys)
|
||||
],
|
||||
"\n\n",
|
||||
)
|
||||
"""
|
||||
@ -575,11 +589,8 @@ const _pgfx_framestyle_defaults = Dict(:semi => :box)
|
||||
|
||||
# we use the anchors to define orientations for example to align left
|
||||
# one needs to use the right edge as anchor
|
||||
const _pgfx_annotation_halign = KW(
|
||||
:center => "",
|
||||
:left => "right",
|
||||
:right => "left",
|
||||
)
|
||||
const _pgfx_annotation_halign =
|
||||
KW(:center => "", :left => "right", :right => "left")
|
||||
## --------------------------------------------------------------------------------------
|
||||
# Generates a colormap for pgfplots based on a ColorGradient
|
||||
pgfx_arrow(::Nothing) = "every arrow/.append style={-}"
|
||||
@ -607,12 +618,9 @@ function pgfx_filllegend!(series_opt, opt)
|
||||
io = IOBuffer()
|
||||
PGFPlotsX.print_tex(io, pgfx_fillstyle(opt))
|
||||
style = strip(String(take!(io)), ['[', ']', ' '])
|
||||
push!(
|
||||
series_opt,
|
||||
"legend image code/.code" => """{
|
||||
\\draw[$style] (0cm,-0.1cm) rectangle (0.6cm,0.1cm);
|
||||
}""",
|
||||
)
|
||||
push!(series_opt, "legend image code/.code" => """{
|
||||
\\draw[$style] (0cm,-0.1cm) rectangle (0.6cm,0.1cm);
|
||||
}""")
|
||||
end
|
||||
|
||||
function pgfx_colormap(grad::ColorGradient)
|
||||
@ -631,9 +639,7 @@ function pgfx_framestyle(style::Symbol)
|
||||
return style
|
||||
else
|
||||
default_style = get(_pgfx_framestyle_defaults, style, :axes)
|
||||
@warn(
|
||||
"Framestyle :$style is not (yet) supported by the PGFPlotsX backend. :$default_style was cosen instead.",
|
||||
)
|
||||
@warn( "Framestyle :$style is not (yet) supported by the PGFPlotsX backend. :$default_style was cosen instead.",)
|
||||
default_style
|
||||
end
|
||||
end
|
||||
@ -675,6 +681,25 @@ function pgfx_font(fontsize, thickness_scaling = 1, font = "\\selectfont")
|
||||
return string("{\\fontsize{", fs, " pt}{", 1.3fs, " pt}", font, "}")
|
||||
end
|
||||
|
||||
function pgfx_should_add_to_legend(series::Series)
|
||||
series.plotattributes[:primary] && series.plotattributes[:label] != "" &&
|
||||
!(
|
||||
series.plotattributes[:seriestype] in (
|
||||
:hexbin,
|
||||
:bins2d,
|
||||
:histogram2d,
|
||||
:hline,
|
||||
:vline,
|
||||
:contour,
|
||||
:contourf,
|
||||
:contour3d,
|
||||
:heatmap,
|
||||
:pie,
|
||||
:image,
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
function pgfx_marker(plotattributes, i = 1)
|
||||
shape = _cycle(plotattributes[:markershape], i)
|
||||
cstr = plot_color(
|
||||
@ -687,19 +712,22 @@ function pgfx_marker(plotattributes, i = 1)
|
||||
get_markerstrokealpha(plotattributes, i),
|
||||
)
|
||||
a_stroke = alpha(cstr_stroke)
|
||||
mark_size = pgfx_thickness_scaling(plotattributes) * 0.5 *
|
||||
_cycle(plotattributes[:markersize], i)
|
||||
mark_size =
|
||||
pgfx_thickness_scaling(plotattributes) *
|
||||
0.5 *
|
||||
_cycle(plotattributes[:markersize], i)
|
||||
return PGFPlotsX.Options(
|
||||
"mark" => shape isa Shape ? "PlotsShape$i" :
|
||||
get(_pgfplotsx_markers, shape, "*"),
|
||||
"mark" =>
|
||||
shape isa Shape ? "PlotsShape$i" : get(_pgfplotsx_markers, shape, "*"),
|
||||
"mark size" => "$mark_size pt",
|
||||
"mark options" => PGFPlotsX.Options(
|
||||
"color" => cstr_stroke,
|
||||
"draw opacity" => a_stroke,
|
||||
"fill" => cstr,
|
||||
"fill opacity" => a,
|
||||
"line width" => pgfx_thickness_scaling(plotattributes) *
|
||||
_cycle(plotattributes[:markerstrokewidth], i),
|
||||
"line width" =>
|
||||
pgfx_thickness_scaling(plotattributes) *
|
||||
_cycle(plotattributes[:markerstrokewidth], i),
|
||||
"rotate" => if shape == :dtriangle
|
||||
180
|
||||
elseif shape == :rtriangle
|
||||
@ -725,15 +753,15 @@ function pgfx_add_annotation!(o, x, y, val, thickness_scaling = 1)
|
||||
push!(
|
||||
o,
|
||||
[
|
||||
"\\node",
|
||||
PGFPlotsX.Options(
|
||||
get(_pgfx_annotation_halign, val.font.halign, "") => nothing,
|
||||
"color" => cstr,
|
||||
"draw opacity" => convert(Float16, a),
|
||||
"rotate" => val.font.rotation,
|
||||
"font" => pgfx_font(val.font.pointsize, thickness_scaling),
|
||||
),
|
||||
" at (axis cs:$x, $y) {$(val.str)};",
|
||||
"\\node",
|
||||
PGFPlotsX.Options(
|
||||
get(_pgfx_annotation_halign, val.font.halign, "") => nothing,
|
||||
"color" => cstr,
|
||||
"draw opacity" => convert(Float16, a),
|
||||
"rotate" => val.font.rotation,
|
||||
"font" => pgfx_font(val.font.pointsize, thickness_scaling),
|
||||
),
|
||||
" at (axis cs:$x, $y) {$(val.str)};",
|
||||
],
|
||||
)
|
||||
end
|
||||
@ -803,7 +831,7 @@ function pgfx_fillrange_series!(axis, series, series_func, i, fillrange, rng)
|
||||
push!(fillrange_opt, "forget plot" => nothing)
|
||||
opt = series.plotattributes
|
||||
args = is3d(series) ? (opt[:x][rng], opt[:y][rng], opt[:z][rng]) :
|
||||
(opt[:x][rng], opt[:y][rng])
|
||||
(opt[:x][rng], opt[:y][rng])
|
||||
push!(
|
||||
axis,
|
||||
PGFPlotsX.PlotInc(fillrange_opt, pgfx_fillrange_args(fillrange, args...)),
|
||||
@ -854,7 +882,8 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
opt,
|
||||
string(letter, "label style") => PGFPlotsX.Options(
|
||||
labelpos => nothing,
|
||||
"font" => pgfx_font(axis[:guidefontsize], pgfx_thickness_scaling(sp)),
|
||||
"font" =>
|
||||
pgfx_font(axis[:guidefontsize], pgfx_thickness_scaling(sp)),
|
||||
"color" => cstr,
|
||||
"draw opacity" => α,
|
||||
"rotate" => axis[:guidefontrotation],
|
||||
@ -868,10 +897,8 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
scale = axis[:scale]
|
||||
if scale in (:log2, :ln, :log10)
|
||||
push!(opt, string(letter, :mode) => "log")
|
||||
scale == :ln || push!(
|
||||
opt,
|
||||
"log basis $letter" => "$(scale == :log2 ? 2 : 10)",
|
||||
)
|
||||
scale == :ln ||
|
||||
push!(opt, "log basis $letter" => "$(scale == :log2 ? 2 : 10)")
|
||||
end
|
||||
|
||||
# ticks on or off
|
||||
@ -888,14 +915,15 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
|
||||
# limits
|
||||
lims = ispolar(sp) && letter == :x ? rad2deg.(axis_limits(sp, :x)) :
|
||||
axis_limits(sp, letter)
|
||||
axis_limits(sp, letter)
|
||||
push!(opt, string(letter, :min) => lims[1], string(letter, :max) => lims[2])
|
||||
|
||||
if !(axis[:ticks] in (nothing, false, :none, :native)) && framestyle != :none
|
||||
ticks = get_ticks(sp, axis)
|
||||
#pgf plot ignores ticks with angle below 90 when xmin = 90 so shift values
|
||||
tick_values = ispolar(sp) && letter == :x ?
|
||||
[rad2deg.(ticks[1])[3:end]..., 360, 405] : ticks[1]
|
||||
tick_values =
|
||||
ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] :
|
||||
ticks[1]
|
||||
push!(
|
||||
opt,
|
||||
string(letter, "tick") => string("{", join(tick_values, ","), "}"),
|
||||
@ -911,44 +939,37 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
end
|
||||
push!(
|
||||
opt,
|
||||
string(letter, "ticklabels") => string(
|
||||
"{\$",
|
||||
join(tick_labels, "\$,\$"),
|
||||
"\$}",
|
||||
),
|
||||
string(letter, "ticklabels") =>
|
||||
string("{\$", join(tick_labels, "\$,\$"), "\$}"),
|
||||
)
|
||||
elseif axis[:showaxis]
|
||||
tick_labels = ispolar(sp) && letter == :x ?
|
||||
[ticks[2][3:end]..., "0", "45"] : ticks[2]
|
||||
tick_labels =
|
||||
ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] :
|
||||
ticks[2]
|
||||
if axis[:formatter] in (:scientific, :auto)
|
||||
tick_labels = string.("\$", convert_sci_unicode.(tick_labels), "\$")
|
||||
tick_labels = replace.(tick_labels, Ref("×" => "\\times"))
|
||||
end
|
||||
push!(
|
||||
opt,
|
||||
string(letter, "ticklabels") => string(
|
||||
"{",
|
||||
join(tick_labels, ","),
|
||||
"}",
|
||||
),
|
||||
string(letter, "ticklabels") =>
|
||||
string("{", join(tick_labels, ","), "}"),
|
||||
)
|
||||
else
|
||||
push!(opt, string(letter, "ticklabels") => "{}")
|
||||
end
|
||||
push!(
|
||||
opt,
|
||||
string(letter, "tick align") => (axis[:tick_direction] == :out ?
|
||||
"outside" : "inside"),
|
||||
string(letter, "tick align") =>
|
||||
(axis[:tick_direction] == :out ? "outside" : "inside"),
|
||||
)
|
||||
cstr = plot_color(axis[:tickfontcolor])
|
||||
α = alpha(cstr)
|
||||
push!(
|
||||
opt,
|
||||
string(letter, "ticklabel style") => PGFPlotsX.Options(
|
||||
"font" => pgfx_font(
|
||||
axis[:tickfontsize],
|
||||
pgfx_thickness_scaling(sp),
|
||||
),
|
||||
"font" =>
|
||||
pgfx_font(axis[:tickfontsize], pgfx_thickness_scaling(sp)),
|
||||
"color" => cstr,
|
||||
"draw opacity" => α,
|
||||
"rotate" => axis[:tickfontrotation],
|
||||
@ -967,7 +988,9 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
|
||||
|
||||
# framestyle
|
||||
if framestyle in (:axes, :origin)
|
||||
axispos = framestyle == :axes ? "left" : "middle"
|
||||
axispos = axis[:mirror] ? "right" :
|
||||
framestyle == :axes ? "left" : "middle"
|
||||
|
||||
if axis[:draw_arrow]
|
||||
push!(opt, string("axis ", letter, " line") => axispos)
|
||||
else
|
||||
|
||||
@ -108,7 +108,7 @@ function shrink_by(lo, sz, ratio)
|
||||
end
|
||||
|
||||
function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
|
||||
aspect_ratio = sp[:aspect_ratio]
|
||||
aspect_ratio = get_aspect_ratio(sp)
|
||||
if aspect_ratio != :none
|
||||
if aspect_ratio == :equal
|
||||
aspect_ratio = 1.0
|
||||
|
||||
@ -1155,7 +1155,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
end
|
||||
|
||||
# aspect ratio
|
||||
aratio = sp[:aspect_ratio]
|
||||
aratio = get_aspect_ratio(sp)
|
||||
if aratio != :none
|
||||
ax."set_aspect"(isa(aratio, Symbol) ? string(aratio) : aratio, anchor = "C")
|
||||
end
|
||||
|
||||
1420
src/examples.jl
1420
src/examples.jl
File diff suppressed because it is too large
Load Diff
434
src/recipes.jl
434
src/recipes.jl
@ -44,7 +44,7 @@ end
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
num_series(x::AMat) = size(x,2)
|
||||
num_series(x::AMat) = size(x, 2)
|
||||
num_series(x) = 1
|
||||
|
||||
RecipesBase.apply_recipe(plotattributes::AKW, ::Type{T}, plt::AbstractPlot) where {T} = throw(MethodError(T, "Unmatched plot recipe: $T"))
|
||||
@ -55,13 +55,26 @@ RecipesBase.apply_recipe(plotattributes::AKW, ::Type{T}, plt::AbstractPlot) wher
|
||||
# for seriestype `line`, need to sort by x values
|
||||
|
||||
const POTENTIAL_VECTOR_ARGUMENTS = [
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linealpha, :linewidth, :linestyle, :line_z,
|
||||
:fillcolor, :fillalpha, :fill_z,
|
||||
:markercolor, :markeralpha, :markershape, :marker_z,
|
||||
:markerstrokecolor, :markerstrokealpha,
|
||||
:yerror, :yerror,
|
||||
:series_annotations, :fillrange
|
||||
:seriescolor,
|
||||
:seriesalpha,
|
||||
:linecolor,
|
||||
:linealpha,
|
||||
:linewidth,
|
||||
:linestyle,
|
||||
:line_z,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
:fill_z,
|
||||
:markercolor,
|
||||
:markeralpha,
|
||||
:markershape,
|
||||
:marker_z,
|
||||
:markerstrokecolor,
|
||||
:markerstrokealpha,
|
||||
:yerror,
|
||||
:yerror,
|
||||
:series_annotations,
|
||||
:fillrange,
|
||||
]
|
||||
|
||||
@recipe function f(::Type{Val{:line}}, x, y, z)
|
||||
@ -99,7 +112,7 @@ end
|
||||
@recipe function f(::Type{Val{:hline}}, x, y, z)
|
||||
n = length(y)
|
||||
newx = repeat(Float64[-1, 1, NaN], n)
|
||||
newy = vec(Float64[yi for i=1:3,yi=y])
|
||||
newy = vec(Float64[yi for i = 1:3, yi in y])
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :straightline
|
||||
@ -109,7 +122,7 @@ end
|
||||
|
||||
@recipe function f(::Type{Val{:vline}}, x, y, z)
|
||||
n = length(y)
|
||||
newx = vec(Float64[yi for i=1:3,yi=y])
|
||||
newx = vec(Float64[yi for i = 1:3, yi in y])
|
||||
newy = repeat(Float64[-1, 1, NaN], n)
|
||||
x := newx
|
||||
y := newy
|
||||
@ -121,7 +134,7 @@ end
|
||||
@recipe function f(::Type{Val{:hspan}}, x, y, z)
|
||||
n = div(length(y), 2)
|
||||
newx = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
|
||||
newy = vcat([[y[2i-1], y[2i-1], y[2i], y[2i], NaN] for i in 1:n]...)
|
||||
newy = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i = 1:n]...)
|
||||
linewidth --> 0
|
||||
x := newx
|
||||
y := newy
|
||||
@ -132,7 +145,7 @@ end
|
||||
|
||||
@recipe function f(::Type{Val{:vspan}}, x, y, z)
|
||||
n = div(length(y), 2)
|
||||
newx = vcat([[y[2i-1], y[2i-1], y[2i], y[2i], NaN] for i in 1:n]...)
|
||||
newx = vcat([[y[2i - 1], y[2i - 1], y[2i], y[2i], NaN] for i = 1:n]...)
|
||||
newy = repeat([-Inf, Inf, Inf, -Inf, NaN], outer = n)
|
||||
linewidth --> 0
|
||||
x := newx
|
||||
@ -156,7 +169,7 @@ end
|
||||
primary := false
|
||||
()
|
||||
end
|
||||
()
|
||||
()
|
||||
end
|
||||
@deps scatterpath path scatter
|
||||
|
||||
@ -169,7 +182,7 @@ function make_steps(x::AbstractArray, st)
|
||||
n = length(x)
|
||||
n == 0 && return zeros(0)
|
||||
newx = zeros(2n - 1)
|
||||
for i in 1:n
|
||||
for i = 1:n
|
||||
idx = 2i - 1
|
||||
newx[idx] = x[i]
|
||||
if i > 1
|
||||
@ -249,10 +262,10 @@ end
|
||||
end
|
||||
end
|
||||
newx, newy = zeros(3n), zeros(3n)
|
||||
for i=1:n
|
||||
rng = 3i-2:3i
|
||||
for i = 1:n
|
||||
rng = (3i - 2):(3i)
|
||||
newx[rng] = [x[i], x[i], NaN]
|
||||
newy[rng] = [_cycle(fr,i), y[i], NaN]
|
||||
newy[rng] = [_cycle(fr, i), y[i], NaN]
|
||||
end
|
||||
x := newx
|
||||
y := newy
|
||||
@ -282,16 +295,16 @@ end
|
||||
# get the value of the curve point at position t
|
||||
function bezier_value(pts::AVec, t::Real)
|
||||
val = 0.0
|
||||
n = length(pts)-1
|
||||
for (i,p) in enumerate(pts)
|
||||
val += p * binomial(n, i-1) * (1-t)^(n-i+1) * t^(i-1)
|
||||
n = length(pts) - 1
|
||||
for (i, p) in enumerate(pts)
|
||||
val += p * binomial(n, i - 1) * (1 - t)^(n - i + 1) * t^(i - 1)
|
||||
end
|
||||
val
|
||||
end
|
||||
|
||||
# create segmented bezier curves in place of line segments
|
||||
@recipe function f(::Type{Val{:curves}}, x, y, z; npoints = 30)
|
||||
args = z !== nothing ? (x,y,z) : (x,y)
|
||||
args = z !== nothing ? (x, y, z) : (x, y)
|
||||
newx, newy = zeros(0), zeros(0)
|
||||
fr = plotattributes[:fillrange]
|
||||
newfr = fr !== nothing ? zeros(0) : nothing
|
||||
@ -304,13 +317,13 @@ end
|
||||
for rng in iter_segments(args...)
|
||||
length(rng) < 2 && continue
|
||||
ts = range(0, stop = 1, length = npoints)
|
||||
nanappend!(newx, map(t -> bezier_value(_cycle(x,rng), t), ts))
|
||||
nanappend!(newy, map(t -> bezier_value(_cycle(y,rng), t), ts))
|
||||
nanappend!(newx, map(t -> bezier_value(_cycle(x, rng), t), ts))
|
||||
nanappend!(newy, map(t -> bezier_value(_cycle(y, rng), t), ts))
|
||||
if z !== nothing
|
||||
nanappend!(newz, map(t -> bezier_value(_cycle(z,rng), t), ts))
|
||||
nanappend!(newz, map(t -> bezier_value(_cycle(z, rng), t), ts))
|
||||
end
|
||||
if fr !== nothing
|
||||
nanappend!(newfr, map(t -> bezier_value(_cycle(fr,rng), t), ts))
|
||||
nanappend!(newfr, map(t -> bezier_value(_cycle(fr, rng), t), ts))
|
||||
end
|
||||
# if lz !== nothing
|
||||
# lzrng = _cycle(lz, rng) # the line_z's for this segment
|
||||
@ -343,14 +356,15 @@ end
|
||||
|
||||
# create a bar plot as a filled step function
|
||||
@recipe function f(::Type{Val{:bar}}, x, y, z)
|
||||
procx, procy, xscale, yscale, baseline = _preprocess_barlike(plotattributes, x, y)
|
||||
procx, procy, xscale, yscale, baseline =
|
||||
_preprocess_barlike(plotattributes, x, y)
|
||||
nx, ny = length(procx), length(procy)
|
||||
axis = plotattributes[:subplot][isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
cv = [discrete_value!(axis, xi)[1] for xi=procx]
|
||||
cv = [discrete_value!(axis, xi)[1] for xi in procx]
|
||||
procx = if nx == ny
|
||||
cv
|
||||
elseif nx == ny + 1
|
||||
0.5diff(cv) + cv[1:end-1]
|
||||
0.5 * diff(cv) + cv[1:(end - 1)]
|
||||
else
|
||||
error("bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))")
|
||||
end
|
||||
@ -359,12 +373,12 @@ end
|
||||
bw = plotattributes[:bar_width]
|
||||
hw = if bw === nothing
|
||||
if nx > 1
|
||||
0.5*_bar_width*ignorenan_minimum(filter(x->x>0, diff(procx)))
|
||||
0.5 * _bar_width * ignorenan_minimum(filter(x -> x > 0, diff(procx)))
|
||||
else
|
||||
0.5 * _bar_width
|
||||
end
|
||||
else
|
||||
Float64[0.5_cycle(bw,i) for i=eachindex(procx)]
|
||||
Float64[0.5 * _cycle(bw, i) for i in eachindex(procx)]
|
||||
end
|
||||
|
||||
# make fillto a vector... default fills to 0
|
||||
@ -378,13 +392,20 @@ end
|
||||
|
||||
# create the bar shapes by adding x/y segments
|
||||
xseg, yseg = Segments(), Segments()
|
||||
for i=1:ny
|
||||
for i = 1:ny
|
||||
yi = procy[i]
|
||||
if !isnan(yi)
|
||||
center = procx[i]
|
||||
hwi = _cycle(hw,i)
|
||||
fi = _cycle(fillto,i)
|
||||
push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi)
|
||||
hwi = _cycle(hw, i)
|
||||
fi = _cycle(fillto, i)
|
||||
push!(
|
||||
xseg,
|
||||
center - hwi,
|
||||
center - hwi,
|
||||
center + hwi,
|
||||
center + hwi,
|
||||
center - hwi,
|
||||
)
|
||||
push!(yseg, yi, fi, fi, yi, yi)
|
||||
end
|
||||
end
|
||||
@ -415,8 +436,8 @@ end
|
||||
m, n = size(z.surf)
|
||||
x_pts, y_pts = fill(NaN, 6 * m * n), fill(NaN, 6 * m * n)
|
||||
fz = zeros(m * n)
|
||||
for i in 1:m # y
|
||||
for j in 1:n # x
|
||||
for i = 1:m # y
|
||||
for j = 1:n # x
|
||||
k = (j - 1) * m + i
|
||||
inds = (6 * (k - 1) + 1):(6 * k - 1)
|
||||
x_pts[inds] .= [xe[j], xe[j + 1], xe[j + 1], xe[j], xe[j]]
|
||||
@ -440,13 +461,17 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histograms
|
||||
|
||||
_bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2
|
||||
_bin_centers(v::AVec) = (v[1:(end - 1)] + v[2:end]) / 2
|
||||
|
||||
_is_positive(x) = (x > 0) && !(x ≈ 0)
|
||||
|
||||
_positive_else_nan(::Type{T}, x::Real) where {T} = _is_positive(x) ? T(x) : T(NaN)
|
||||
|
||||
function _scale_adjusted_values(::Type{T}, V::AbstractVector, scale::Symbol) where T<:AbstractFloat
|
||||
function _scale_adjusted_values(
|
||||
::Type{T},
|
||||
V::AbstractVector,
|
||||
scale::Symbol,
|
||||
) where {T<:AbstractFloat}
|
||||
if scale in _logScales
|
||||
[_positive_else_nan(T, x) for x in V]
|
||||
else
|
||||
@ -455,7 +480,7 @@ function _scale_adjusted_values(::Type{T}, V::AbstractVector, scale::Symbol) whe
|
||||
end
|
||||
|
||||
|
||||
function _binbarlike_baseline(min_value::T, scale::Symbol) where T<:Real
|
||||
function _binbarlike_baseline(min_value::T, scale::Symbol) where {T<:Real}
|
||||
if (scale in _logScales)
|
||||
!isnan(min_value) ? min_value / T(_logScaleBases[scale]^log10(2)) : T(1E-3)
|
||||
else
|
||||
@ -464,7 +489,11 @@ function _binbarlike_baseline(min_value::T, scale::Symbol) where T<:Real
|
||||
end
|
||||
|
||||
|
||||
function _preprocess_binbarlike_weights(::Type{T}, w, wscale::Symbol) where T<:AbstractFloat
|
||||
function _preprocess_binbarlike_weights(
|
||||
::Type{T},
|
||||
w,
|
||||
wscale::Symbol,
|
||||
) where {T<:AbstractFloat}
|
||||
w_adj = _scale_adjusted_values(T, w, wscale)
|
||||
w_min = ignorenan_minimum(w_adj)
|
||||
w_max = ignorenan_maximum(w_adj)
|
||||
@ -490,7 +519,8 @@ end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:barbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, x, y)
|
||||
if (plotattributes[:bar_width] === nothing)
|
||||
bar_width := diff(edge)
|
||||
end
|
||||
@ -503,8 +533,9 @@ end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
xerror := diff(edge)/2
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, x, y)
|
||||
xerror := diff(edge) / 2
|
||||
x := _bin_centers(edge)
|
||||
y := weights
|
||||
seriestype := :scatter
|
||||
@ -513,7 +544,13 @@ end
|
||||
@deps scatterbins scatter
|
||||
|
||||
|
||||
function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::Symbol)
|
||||
function _stepbins_path(
|
||||
edge,
|
||||
weights,
|
||||
baseline::Real,
|
||||
xscale::Symbol,
|
||||
yscale::Symbol,
|
||||
)
|
||||
log_scale_x = xscale in _logScales
|
||||
log_scale_y = yscale in _logScales
|
||||
|
||||
@ -538,7 +575,7 @@ function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::S
|
||||
w, it_state_w = it_tuple_w
|
||||
|
||||
if (log_scale_x && a ≈ 0)
|
||||
a = oftype(a, b/_logScaleBases[xscale]^3)
|
||||
a = oftype(a, b / _logScaleBases[xscale]^3)
|
||||
end
|
||||
|
||||
if isnan(w)
|
||||
@ -575,9 +612,11 @@ end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:stepbins}}, x, y, z)
|
||||
axis = plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
axis =
|
||||
plotattributes[:subplot][Plots.isvertical(plotattributes) ? :xaxis : :yaxis]
|
||||
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, x, y)
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, x, y)
|
||||
|
||||
xpts, ypts = _stepbins_path(edge, weights, baseline, xscale, yscale)
|
||||
if !isvertical(plotattributes)
|
||||
@ -607,9 +646,19 @@ end
|
||||
end
|
||||
Plots.@deps stepbins path
|
||||
|
||||
wand_edges(x...) = (@warn("Load the StatsPlots package in order to use :wand bins. Defaulting to :auto", once = true); :auto)
|
||||
wand_edges(x...) = (
|
||||
@warn(
|
||||
"Load the StatsPlots package in order to use :wand bins. Defaulting to :auto",
|
||||
once = true
|
||||
);
|
||||
:auto
|
||||
)
|
||||
|
||||
function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::Symbol = :auto) where N
|
||||
function _auto_binning_nbins(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
dim::Integer;
|
||||
mode::Symbol = :auto,
|
||||
) where {N}
|
||||
max_bins = 10_000
|
||||
_cl(x) = min(ceil(Int, max(x, one(x))), max_bins)
|
||||
_iqr(v) = (q = quantile(v, 0.75) - quantile(v, 0.25); q > 0 ? q : oftype(q, 1))
|
||||
@ -618,8 +667,13 @@ function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::S
|
||||
n_samples = length(LinearIndices(first(vs)))
|
||||
|
||||
# The nd estimator is the key to most automatic binning methods, and is modified for twodimensional histograms to include correlation
|
||||
nd = n_samples^(1/(2+N))
|
||||
nd = N == 2 ? min(n_samples^(1/(2+N)), nd / (1-cor(first(vs), last(vs))^2)^(3//8)) : nd # the >2-dimensional case does not have a nice solution to correlations
|
||||
nd = n_samples^(1 / (2 + N))
|
||||
nd = N == 2 ?
|
||||
min(
|
||||
n_samples^(1 / (2 + N)),
|
||||
nd / (1 - cor(first(vs), last(vs))^2)^(3 // 8),
|
||||
) :
|
||||
nd # the >2-dimensional case does not have a nice solution to correlations
|
||||
|
||||
v = vs[dim]
|
||||
|
||||
@ -644,32 +698,52 @@ function _auto_binning_nbins(vs::NTuple{N,AbstractVector}, dim::Integer; mode::S
|
||||
end
|
||||
end
|
||||
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) where {N} = StatsBase.histrange(vs[dim], binning, :left)
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) where {N} = _hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) where {N} = binning
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) where {N} =
|
||||
StatsBase.histrange(vs[dim], binning, :left)
|
||||
_hist_edge(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) where {N} =
|
||||
_hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
|
||||
_hist_edge(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
dim::Integer,
|
||||
binning::AbstractVector,
|
||||
) where {N} = binning
|
||||
|
||||
_hist_edges(vs::NTuple{N,AbstractVector}, binning::NTuple{N, Any}) where {N} =
|
||||
_hist_edges(vs::NTuple{N,AbstractVector}, binning::NTuple{N,Any}) where {N} =
|
||||
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...,))
|
||||
|
||||
_hist_edges(vs::NTuple{N,AbstractVector}, binning::Union{Integer, Symbol, AbstractVector}) where {N} =
|
||||
map(dim -> _hist_edge(vs, dim, binning), (1:N...,))
|
||||
_hist_edges(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
binning::Union{Integer,Symbol,AbstractVector},
|
||||
) where {N} = map(dim -> _hist_edge(vs, dim, binning), (1:N...,))
|
||||
|
||||
_hist_norm_mode(mode::Symbol) = mode
|
||||
_hist_norm_mode(mode::Bool) = mode ? :pdf : :none
|
||||
|
||||
_filternans(vs::NTuple{1,AbstractVector}) = filter!.(isfinite, vs)
|
||||
function _filternans(vs::NTuple{N,AbstractVector}) where N
|
||||
_invertedindex(v, not) = [j for (i,j) in enumerate(v) if !(i ∈ not)]
|
||||
function _filternans(vs::NTuple{N,AbstractVector}) where {N}
|
||||
_invertedindex(v, not) = [j for (i, j) in enumerate(v) if !(i ∈ not)]
|
||||
nots = union(Set.(findall.(!isfinite, vs))...)
|
||||
_invertedindex.(vs, Ref(nots))
|
||||
end
|
||||
|
||||
function _make_hist(vs::NTuple{N,AbstractVector}, binning; normed = false, weights = nothing) where N
|
||||
function _make_hist(
|
||||
vs::NTuple{N,AbstractVector},
|
||||
binning;
|
||||
normed = false,
|
||||
weights = nothing,
|
||||
) where {N}
|
||||
localvs = _filternans(vs)
|
||||
edges = _hist_edges(localvs, binning)
|
||||
h = float( weights === nothing ?
|
||||
StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) :
|
||||
StatsBase.fit(StatsBase.Histogram, localvs, StatsBase.Weights(weights), edges, closed = :left)
|
||||
h = float(
|
||||
weights === nothing ?
|
||||
StatsBase.fit(StatsBase.Histogram, localvs, edges, closed = :left) :
|
||||
StatsBase.fit(
|
||||
StatsBase.Histogram,
|
||||
localvs,
|
||||
StatsBase.Weights(weights),
|
||||
edges,
|
||||
closed = :left,
|
||||
),
|
||||
)
|
||||
normalize!(h, mode = _hist_norm_mode(normed))
|
||||
end
|
||||
@ -682,7 +756,12 @@ end
|
||||
@deps histogram barhist
|
||||
|
||||
@recipe function f(::Type{Val{:barhist}}, x, y, z)
|
||||
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
h = _make_hist(
|
||||
(y,),
|
||||
plotattributes[:bins],
|
||||
normed = plotattributes[:normalize],
|
||||
weights = plotattributes[:weights],
|
||||
)
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :barbins
|
||||
@ -691,7 +770,12 @@ end
|
||||
@deps barhist barbins
|
||||
|
||||
@recipe function f(::Type{Val{:stephist}}, x, y, z)
|
||||
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
h = _make_hist(
|
||||
(y,),
|
||||
plotattributes[:bins],
|
||||
normed = plotattributes[:normalize],
|
||||
weights = plotattributes[:weights],
|
||||
)
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :stepbins
|
||||
@ -700,7 +784,12 @@ end
|
||||
@deps stephist stepbins
|
||||
|
||||
@recipe function f(::Type{Val{:scatterhist}}, x, y, z)
|
||||
h = _make_hist((y,), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
h = _make_hist(
|
||||
(y,),
|
||||
plotattributes[:bins],
|
||||
normed = plotattributes[:normalize],
|
||||
weights = plotattributes[:weights],
|
||||
)
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :scatterbins
|
||||
@ -709,19 +798,23 @@ end
|
||||
@deps scatterhist scatterbins
|
||||
|
||||
|
||||
@recipe function f(h::StatsBase.Histogram{T, 1, E}) where {T, E}
|
||||
@recipe function f(h::StatsBase.Histogram{T,1,E}) where {T,E}
|
||||
seriestype --> :barbins
|
||||
|
||||
st_map = Dict(
|
||||
:bar => :barbins, :scatter => :scatterbins, :step => :stepbins,
|
||||
:steppost => :stepbins # :step can be mapped to :steppost in pre-processing
|
||||
:bar => :barbins,
|
||||
:scatter => :scatterbins,
|
||||
:step => :stepbins,
|
||||
:steppost => :stepbins, # :step can be mapped to :steppost in pre-processing
|
||||
)
|
||||
seriestype := get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
|
||||
seriestype :=
|
||||
get(st_map, plotattributes[:seriestype], plotattributes[:seriestype])
|
||||
|
||||
if plotattributes[:seriestype] == :scatterbins
|
||||
# Workaround, error bars currently not set correctly by scatterbins
|
||||
edge, weights, xscale, yscale, baseline = _preprocess_binlike(plotattributes, h.edges[1], h.weights)
|
||||
xerror --> diff(h.edges[1])/2
|
||||
edge, weights, xscale, yscale, baseline =
|
||||
_preprocess_binlike(plotattributes, h.edges[1], h.weights)
|
||||
xerror --> diff(h.edges[1]) / 2
|
||||
seriestype := :scatter
|
||||
(Plots._bin_centers(edge), weights)
|
||||
else
|
||||
@ -730,7 +823,7 @@ end
|
||||
end
|
||||
|
||||
|
||||
@recipe function f(hv::AbstractVector{H}) where H <: StatsBase.Histogram
|
||||
@recipe function f(hv::AbstractVector{H}) where {H<:StatsBase.Histogram}
|
||||
for h in hv
|
||||
@series begin
|
||||
h
|
||||
@ -769,7 +862,12 @@ Plots.@deps bins2d heatmap
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
|
||||
h = _make_hist((x, y), plotattributes[:bins], normed = plotattributes[:normalize], weights = plotattributes[:weights])
|
||||
h = _make_hist(
|
||||
(x, y),
|
||||
plotattributes[:bins],
|
||||
normed = plotattributes[:normalize],
|
||||
weights = plotattributes[:weights],
|
||||
)
|
||||
x := h.edges[1]
|
||||
y := h.edges[2]
|
||||
z := Surface(h.weights)
|
||||
@ -779,7 +877,7 @@ end
|
||||
@deps histogram2d bins2d
|
||||
|
||||
|
||||
@recipe function f(h::StatsBase.Histogram{T, 2, E}) where {T, E}
|
||||
@recipe function f(h::StatsBase.Histogram{T,2,E}) where {T,E}
|
||||
seriestype --> :bins2d
|
||||
(h.edges[1], h.edges[2], Surface(h.weights))
|
||||
end
|
||||
@ -801,6 +899,91 @@ end
|
||||
# note: don't add dependencies because this really isn't a drop-in replacement
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# lens! - magnify a region of a plot
|
||||
lens!(args...;kwargs...) = plot!(args...; seriestype=:lens, kwargs...)
|
||||
export lens!
|
||||
@recipe function f(::Type{Val{:lens}}, plt::AbstractPlot)
|
||||
sp_index, inset_bbox = plotattributes[:inset_subplots]
|
||||
if !(width(inset_bbox) isa Measures.Length{:w,<:Real})
|
||||
throw(ArgumentError("Inset bounding box needs to in relative coordinates."))
|
||||
end
|
||||
sp = plt.subplots[sp_index]
|
||||
xl1, xl2 = xlims(plt.subplots[sp_index])
|
||||
bbx1 = xl1 + left(inset_bbox).value * (xl2 - xl1)
|
||||
bbx2 = bbx1 + width(inset_bbox).value * (xl2 - xl1)
|
||||
yl1, yl2 = ylims(plt.subplots[sp_index])
|
||||
bby1 = yl1 + (1 - bottom(inset_bbox).value) * (yl2 - yl1)
|
||||
bby2 = bby1 + height(inset_bbox).value * (yl2 - yl1)
|
||||
bbx = bbx1 + width(inset_bbox).value * (xl2 - xl1) / 2
|
||||
bby = bby1 + height(inset_bbox).value * (yl2 - yl1) / 2
|
||||
lens_index = last(plt.subplots)[:subplot_index] + 1
|
||||
x1, x2 = plotattributes[:x]
|
||||
y1, y2 = plotattributes[:y]
|
||||
seriestype := :path
|
||||
label := ""
|
||||
linecolor := :lightgray
|
||||
bbx_mag = (x1 + x2) / 2
|
||||
bby_mag = (y1 + y2) / 2
|
||||
xi_lens, yi_lens = intersection_point(bbx_mag, bby_mag, bbx, bby, abs(bby2 - bby1), abs(bbx2 - bbx1))
|
||||
xi_mag, yi_mag = intersection_point(bbx, bby, bbx_mag, bby_mag, abs(y2 - y1), abs(x2 - x1))
|
||||
# add lines
|
||||
if xl1 < xi_lens < xl2 &&
|
||||
yl1 < yi_lens < yl2
|
||||
@series begin
|
||||
subplot := sp_index
|
||||
x := [xi_mag, xi_lens]
|
||||
y := [yi_mag, yi_lens]
|
||||
()
|
||||
end
|
||||
end
|
||||
# add magnification shape
|
||||
@series begin
|
||||
subplot := sp_index
|
||||
x := [x1, x1, x2, x2, x1]
|
||||
y := [y1, y2, y2, y1, y1]
|
||||
()
|
||||
end
|
||||
# add subplot
|
||||
for series in sp.series_list
|
||||
@series begin
|
||||
plotattributes = merge(plotattributes, copy(series.plotattributes))
|
||||
subplot := lens_index
|
||||
label := ""
|
||||
xlims := (x1, x2)
|
||||
ylims := (y1, y2)
|
||||
()
|
||||
end
|
||||
end
|
||||
backup = copy(plotattributes)
|
||||
empty!(plotattributes)
|
||||
seriestype := :path
|
||||
series_plotindex := backup[:series_plotindex]
|
||||
end
|
||||
|
||||
function intersection_point(xA, yA, xB, yB, h, w)
|
||||
s = (yA - yB) / (xA - xB)
|
||||
hh = h / 2
|
||||
hw = w / 2
|
||||
# left or right?
|
||||
if -hh <= s * hw <= hh
|
||||
if xA > xB
|
||||
# right
|
||||
return xB + hw, yB + s * hw
|
||||
else # left
|
||||
return xB - hw, yB - s * hw
|
||||
end
|
||||
# top or bot?
|
||||
elseif -hw <= hh/s <= hw
|
||||
if yA > yB
|
||||
# top
|
||||
return xB + hh/s, yB + hh
|
||||
else
|
||||
# bottom
|
||||
return xB - hh/s, yB - hh
|
||||
end
|
||||
end
|
||||
end
|
||||
# ---------------------------------------------------------------------------
|
||||
# contourf - filled contours
|
||||
|
||||
@ -855,7 +1038,11 @@ end
|
||||
@recipe function f(::Type{Val{:yerror}}, x, y, z)
|
||||
error_style!(plotattributes)
|
||||
markershape := :hline
|
||||
plotattributes[:x], plotattributes[:y] = error_coords(plotattributes[:x], plotattributes[:y], error_zipit(plotattributes[:yerror]))
|
||||
plotattributes[:x], plotattributes[:y] = error_coords(
|
||||
plotattributes[:x],
|
||||
plotattributes[:y],
|
||||
error_zipit(plotattributes[:yerror]),
|
||||
)
|
||||
()
|
||||
end
|
||||
@deps yerror path
|
||||
@ -863,7 +1050,11 @@ end
|
||||
@recipe function f(::Type{Val{:xerror}}, x, y, z)
|
||||
error_style!(plotattributes)
|
||||
markershape := :vline
|
||||
plotattributes[:y], plotattributes[:x] = error_coords(plotattributes[:y], plotattributes[:x], error_zipit(plotattributes[:xerror]))
|
||||
plotattributes[:y], plotattributes[:x] = error_coords(
|
||||
plotattributes[:y],
|
||||
plotattributes[:x],
|
||||
error_zipit(plotattributes[:xerror]),
|
||||
)
|
||||
()
|
||||
end
|
||||
@deps xerror path
|
||||
@ -898,15 +1089,15 @@ function quiver_using_arrows(plotattributes::AKW)
|
||||
first(vi), last(vi)
|
||||
elseif isscalar(vi)
|
||||
vi, vi
|
||||
elseif isa(vi,Function)
|
||||
elseif isa(vi, Function)
|
||||
vi(xi, yi)
|
||||
else
|
||||
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
|
||||
end
|
||||
|
||||
# add the points
|
||||
nanappend!(x, [xi, xi+vx, NaN])
|
||||
nanappend!(y, [yi, yi+vy, NaN])
|
||||
nanappend!(x, [xi, xi + vx, NaN])
|
||||
nanappend!(y, [yi, yi + vy, NaN])
|
||||
end
|
||||
|
||||
plotattributes[:x], plotattributes[:y] = x, y
|
||||
@ -936,7 +1127,7 @@ function quiver_using_hack(plotattributes::AKW)
|
||||
first(vi), last(vi)
|
||||
elseif isscalar(vi)
|
||||
vi, vi
|
||||
elseif isa(vi,Function)
|
||||
elseif isa(vi, Function)
|
||||
vi(xi, yi)
|
||||
else
|
||||
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
|
||||
@ -951,8 +1142,11 @@ function quiver_using_hack(plotattributes::AKW)
|
||||
U1 *= arrow_h
|
||||
U2 *= arrow_w
|
||||
|
||||
ppv = p+v
|
||||
nanappend!(pts, P2[p, ppv-U1, ppv-U1+U2, ppv, ppv-U1-U2, ppv-U1])
|
||||
ppv = p + v
|
||||
nanappend!(
|
||||
pts,
|
||||
P2[p, ppv - U1, ppv - U1 + U2, ppv, ppv - U1 - U2, ppv - U1],
|
||||
)
|
||||
end
|
||||
|
||||
plotattributes[:x], plotattributes[:y] = Plots.unzip(pts[2:end])
|
||||
@ -977,32 +1171,28 @@ end
|
||||
|
||||
"Represent Open High Low Close data (used in finance)"
|
||||
mutable struct OHLC{T<:Real}
|
||||
open::T
|
||||
high::T
|
||||
low::T
|
||||
close::T
|
||||
open::T
|
||||
high::T
|
||||
low::T
|
||||
close::T
|
||||
end
|
||||
Base.convert(::Type{OHLC}, tup::Tuple) = OHLC(tup...)
|
||||
# Base.tuple(ohlc::OHLC) = (ohlc.open, ohlc.high, ohlc.low, ohlc.close)
|
||||
|
||||
# get one OHLC path
|
||||
function get_xy(o::OHLC, x, xdiff)
|
||||
xl, xm, xr = x-xdiff, x, x+xdiff
|
||||
ox = [xl, xm, NaN,
|
||||
xm, xm, NaN,
|
||||
xm, xr]
|
||||
oy = [o.open, o.open, NaN,
|
||||
o.low, o.high, NaN,
|
||||
o.close, o.close]
|
||||
xl, xm, xr = x - xdiff, x, x + xdiff
|
||||
ox = [xl, xm, NaN, xm, xm, NaN, xm, xr]
|
||||
oy = [o.open, o.open, NaN, o.low, o.high, NaN, o.close, o.close]
|
||||
ox, oy
|
||||
end
|
||||
|
||||
# get the joined vector
|
||||
function get_xy(v::AVec{OHLC}, x = eachindex(v))
|
||||
xdiff = 0.3ignorenan_mean(abs.(diff(x)))
|
||||
xdiff = 0.3 * ignorenan_mean(abs.(diff(x)))
|
||||
x_out, y_out = zeros(0), zeros(0)
|
||||
for (i,ohlc) in enumerate(v)
|
||||
ox,oy = get_xy(ohlc, x[i], xdiff)
|
||||
for (i, ohlc) in enumerate(v)
|
||||
ox, oy = get_xy(ohlc, x[i], xdiff)
|
||||
nanappend!(x_out, ox)
|
||||
nanappend!(y_out, oy)
|
||||
end
|
||||
@ -1015,10 +1205,17 @@ end
|
||||
|
||||
# to squash ambiguity warnings...
|
||||
@recipe f(x::AVec{Function}, v::AVec{OHLC}) = error()
|
||||
@recipe f(x::AVec{Function}, v::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = error()
|
||||
@recipe f(
|
||||
x::AVec{Function},
|
||||
v::AVec{Tuple{R1,R2,R3,R4}},
|
||||
) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = error()
|
||||
|
||||
# this must be OHLC?
|
||||
@recipe f(x::AVec, ohlc::AVec{Tuple{R1,R2,R3,R4}}) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} = x, OHLC[OHLC(t...) for t in ohlc]
|
||||
@recipe f(
|
||||
x::AVec,
|
||||
ohlc::AVec{Tuple{R1,R2,R3,R4}},
|
||||
) where {R1<:Number,R2<:Number,R3<:Number,R4<:Number} =
|
||||
x, OHLC[OHLC(t...) for t in ohlc]
|
||||
|
||||
@recipe function f(x::AVec, v::AVec{OHLC})
|
||||
seriestype := :path
|
||||
@ -1056,11 +1253,11 @@ end
|
||||
@assert length(g.args) == 1 && typeof(g.args[1]) <: AbstractMatrix
|
||||
seriestype := :spy
|
||||
mat = g.args[1]
|
||||
n,m = axes(mat)
|
||||
n, m = axes(mat)
|
||||
Plots.SliceIt, m, n, Surface(mat)
|
||||
end
|
||||
|
||||
@recipe function f(::Type{Val{:spy}}, x,y,z)
|
||||
@recipe function f(::Type{Val{:spy}}, x, y, z)
|
||||
yflip := true
|
||||
aspect_ratio := 1
|
||||
rs, cs, zs = findnz(z.surf)
|
||||
@ -1086,7 +1283,8 @@ end
|
||||
# -------------------------------------------------
|
||||
|
||||
"Adds ax+b... straight line over the current plot, without changing the axis limits"
|
||||
abline!(plt::Plot, a, b; kw...) = plot!(plt, [0, 1], [b, b+a]; seriestype = :straightline, kw...)
|
||||
abline!(plt::Plot, a, b; kw...) =
|
||||
plot!(plt, [0, 1], [b, b + a]; seriestype = :straightline, kw...)
|
||||
|
||||
abline!(args...; kw...) = abline!(current(), args...; kw...)
|
||||
|
||||
@ -1099,9 +1297,11 @@ datetimeformatter(dt) = string(DateTime(Dates.UTM(dt)))
|
||||
timeformatter(t) = string(Dates.Time(Dates.Nanosecond(t)))
|
||||
|
||||
@recipe f(::Type{Date}, dt::Date) = (dt -> Dates.value(dt), dateformatter)
|
||||
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> Dates.value(dt), datetimeformatter)
|
||||
@recipe f(::Type{DateTime}, dt::DateTime) =
|
||||
(dt -> Dates.value(dt), datetimeformatter)
|
||||
@recipe f(::Type{Dates.Time}, t::Dates.Time) = (t -> Dates.value(t), timeformatter)
|
||||
@recipe f(::Type{P}, t::P) where P <: Dates.Period = (t -> Dates.value(t), t -> string(P(t)))
|
||||
@recipe f(::Type{P}, t::P) where {P<:Dates.Period} =
|
||||
(t -> Dates.value(t), t -> string(P(t)))
|
||||
|
||||
# -------------------------------------------------
|
||||
# Characters
|
||||
@ -1111,7 +1311,7 @@ timeformatter(t) = string(Dates.Time(Dates.Nanosecond(t)))
|
||||
# -------------------------------------------------
|
||||
# Complex Numbers
|
||||
|
||||
@recipe function f(A::Array{Complex{T}}) where T<:Number
|
||||
@recipe function f(A::Array{Complex{T}}) where {T<:Number}
|
||||
xguide --> "Re(x)"
|
||||
yguide --> "Im(x)"
|
||||
real.(A), imag.(A)
|
||||
@ -1120,10 +1320,10 @@ end
|
||||
# Splits a complex matrix to its real and complex parts
|
||||
# Reals defaults solid, imaginary defaults dashed
|
||||
# Label defaults are changed to match the real-imaginary reference / indexing
|
||||
@recipe function f(x::AbstractArray{T},y::Array{Complex{T2}}) where {T<:Real,T2}
|
||||
ylabel --> "Re(y)"
|
||||
zlabel --> "Im(y)"
|
||||
x,real.(y),imag.(y)
|
||||
@recipe function f(x::AbstractArray{T}, y::Array{Complex{T2}}) where {T<:Real,T2}
|
||||
ylabel --> "Re(y)"
|
||||
zlabel --> "Im(y)"
|
||||
x, real.(y), imag.(y)
|
||||
end
|
||||
|
||||
|
||||
@ -1137,7 +1337,7 @@ end
|
||||
end
|
||||
|
||||
library = PlotUtils.color_libraries[cl.args[1]]
|
||||
z = sqrt.((1:15)*reshape(1:20,1,:))
|
||||
z = sqrt.((1:15) * reshape(1:20, 1, :))
|
||||
|
||||
seriestype := :heatmap
|
||||
ticks := nothing
|
||||
@ -1161,7 +1361,7 @@ end
|
||||
if !(length(grad.args) == 1 && isa(grad.args[1], Symbol))
|
||||
error("showgradient takes the name of a color gradient as a Symbol")
|
||||
end
|
||||
z = sqrt.((1:15)*reshape(1:20,1,:))
|
||||
z = sqrt.((1:15) * reshape(1:20, 1, :))
|
||||
seriestype := :heatmap
|
||||
ticks := nothing
|
||||
legend := false
|
||||
@ -1184,9 +1384,9 @@ end
|
||||
weights = cumsum(weights, dims = 2)
|
||||
seriestype := :shape
|
||||
|
||||
# create a filled polygon for each item
|
||||
for c=axes(weights,2)
|
||||
sx = vcat(weights[:,c], c==1 ? zeros(n) : reverse(weights[:,c-1]))
|
||||
# create a filled polygon for each item
|
||||
for c in axes(weights, 2)
|
||||
sx = vcat(weights[:, c], c == 1 ? zeros(n) : reverse(weights[:, c - 1]))
|
||||
sy = vcat(returns, reverse(returns))
|
||||
@series Plots.isvertical(plotattributes) ? (sx, sy) : (sy, sx)
|
||||
end
|
||||
@ -1205,13 +1405,13 @@ julia> areaplot(1:3, [1 2 3; 7 8 9; 4 5 6], seriescolor = [:red :green :blue], f
|
||||
@userplot AreaPlot
|
||||
|
||||
@recipe function f(a::AreaPlot)
|
||||
data = cumsum(a.args[end], dims=2)
|
||||
data = cumsum(a.args[end], dims = 2)
|
||||
x = length(a.args) == 1 ? (axes(data, 1)) : a.args[1]
|
||||
seriestype := :line
|
||||
for i in axes(data, 2)
|
||||
@series begin
|
||||
fillrange := i > 1 ? data[:,i-1] : 0
|
||||
x, data[:,i]
|
||||
fillrange := i > 1 ? data[:, i - 1] : 0
|
||||
x, data[:, i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -347,6 +347,7 @@ end
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
cbar --> false
|
||||
aspect_ratio --> :equal
|
||||
z, plotattributes[:fillcolor] = replace_image_with_heatmap(mat)
|
||||
SliceIt, m, n, Surface(z)
|
||||
end
|
||||
|
||||
30
src/utils.jl
30
src/utils.jl
@ -116,24 +116,11 @@ end
|
||||
|
||||
function replace_image_with_heatmap(z::Array{T}) where T<:Colorant
|
||||
n, m = size(z)
|
||||
# idx = 0
|
||||
colors = ColorGradient(vec(z))
|
||||
newz = reshape(range(0, stop=1, length=n*m), n, m)
|
||||
newz, colors
|
||||
# newz = zeros(n, m)
|
||||
# for i=1:n, j=1:m
|
||||
# push!(colors, T(z[i,j]...))
|
||||
# newz[i,j] = idx / (n*m-1)
|
||||
# idx += 1
|
||||
# end
|
||||
# newz, ColorGradient(colors)
|
||||
end
|
||||
|
||||
function imageHack(plotattributes::AKW)
|
||||
is_seriestype_supported(:heatmap) || error("Neither :image or :heatmap are supported!")
|
||||
plotattributes[:seriestype] = :heatmap
|
||||
plotattributes[:z], plotattributes[:fillcolor] = replace_image_with_heatmap(plotattributes[:z].surf)
|
||||
end
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
"Build line segments for plotting"
|
||||
@ -693,6 +680,10 @@ function get_markerstrokealpha(series, i::Int = 1)
|
||||
_cycle(series[:markerstrokealpha], i)
|
||||
end
|
||||
|
||||
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
|
||||
@ -706,6 +697,19 @@ function has_attribute_segments(series::Series)
|
||||
return any((typeof(series[attr]) <: AbstractVector && length(series[attr]) > 1) for attr in [:seriescolor, :seriesalpha, :linecolor, :linealpha, :linewidth, :linestyle, :fillcolor, :fillalpha, :markercolor, :markeralpha, :markerstrokecolor, :markerstrokealpha]) || any(typeof(series[attr]) <: AbstractArray for attr in (:line_z, :fill_z, :marker_z))
|
||||
end
|
||||
|
||||
function get_aspect_ratio(sp)
|
||||
aspect_ratio = sp[:aspect_ratio]
|
||||
if aspect_ratio == :auto
|
||||
aspect_ratio = :none
|
||||
for series in series_list(sp)
|
||||
if series[:seriestype] == :image
|
||||
aspect_ratio = :equal
|
||||
end
|
||||
end
|
||||
end
|
||||
return aspect_ratio
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
makekw(; kw...) = KW(kw)
|
||||
|
||||
@ -42,7 +42,31 @@ include("imgcomp.jl")
|
||||
Random.seed!(1234)
|
||||
default(show=false, reuse=true)
|
||||
is_ci() = get(ENV, "CI", "false") == "true"
|
||||
img_tol = is_ci() ? 10e-2 : 10e-2
|
||||
img_tol = is_ci() ? 1e-2 : 1e-3
|
||||
|
||||
## Uncomment the following lines to update reference images for different backends
|
||||
|
||||
#=
|
||||
@testset "GR" begin
|
||||
image_comparison_facts(:gr, tol=img_tol, skip = Plots._backend_skips[:gr])
|
||||
end
|
||||
|
||||
plotly()
|
||||
@testset "Plotly" begin
|
||||
image_comparison_facts(:plotly, tol=img_tol, skip = Plots._backend_skips[:plotlyjs])
|
||||
end
|
||||
|
||||
pyplot()
|
||||
@testset "PyPlot" begin
|
||||
image_comparison_facts(:pyplot, tol=img_tol, skip = Plots._backend_skips[:pyplot])
|
||||
end
|
||||
|
||||
pgfplots()
|
||||
@testset "PGFPlots" begin
|
||||
image_comparison_facts(:pgfplots, tol=img_tol, skip = Plots._backend_skips[:pgfplots])
|
||||
end
|
||||
=#
|
||||
##
|
||||
|
||||
@testset "Backends" begin
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user