Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Josef Heinen 2020-03-07 10:53:02 +01:00
commit a301366ef6
15 changed files with 1539 additions and 811 deletions

View File

@ -20,6 +20,10 @@ addons:
- xauth
- xvfb
cache:
directories:
- $HOME/.julia/artifacts
sudo: required
before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then pwd ; fi

View File

@ -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"

View File

@ -20,6 +20,9 @@ branches:
- master
- /release-.*/
cache:
- '%USERPROFILE%\.julia\artifacts'
notifications:
- provider: Email
on_build_success: false

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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