Compare commits

...

42 Commits

Author SHA1 Message Date
Michael Krabbe Borregaard 8eac99a389 Merge pull request #2469 from JuliaPlots/bbs/remove-defaults
remove default recipes
2020-03-16 22:48:38 +01:00
Simon Christ a65d41ca39 move Volume 2020-03-15 21:48:51 +01:00
Simon Christ dd25893a70 move Surface and trueOrAllTrue 2020-03-15 21:22:59 +01:00
Simon Christ 52c081758d move Formatted and import 2020-03-15 21:14:16 +01:00
Simon Christ 7ed9fbd7fc factor out SliceIt dependencies 2020-03-15 18:11:46 +01:00
Simon Christ 6bb53b5181 remove default recipes 2020-03-15 16:53:54 +01:00
Simon Christ 9d57817bdf Update plot.jl (#2467)
* implement the recipe_pipeline

* delete _plot!

* Update plot.jl

fix: https://github.com/mkborregaard/RecipePipeline.jl/issues/7
2020-03-15 15:40:49 +01:00
Anshul Singhvi 4b94a8c2f6 Fix un-imported symbol (#2468)
ensure that the pipeline works
2020-03-15 13:36:51 +01:00
Anshul Singhvi aafc1e1989 change the is_st_supported signature (#2466) 2020-03-15 13:36:51 +01:00
Sebastian Micluța-Câmpeanu 4e6c64ebcc Add finalize_subplot! 2020-03-15 13:36:51 +01:00
Sebastian Micluța-Câmpeanu cb103598d7 Fix typo
_process_userrecipes now calls the correct _process_userrecipe
2020-03-15 13:36:51 +01:00
Sebastian Micluța-Câmpeanu 5728e9fb46 rename 2020-03-15 13:36:51 +01:00
Sebastian Micluța-Câmpeanu 74a6520960 Rename _typeAliases to type_aliases 2020-03-15 13:36:51 +01:00
Sebastian Micluța-Câmpeanu f557ca3b74 Disable precompile file for now 2020-03-15 13:36:51 +01:00
Sebastian Micluța-Câmpeanu bb08d61709 Add _recipe_before_series 2020-03-15 13:36:51 +01:00
Simon Christ 857d37c380 [skip ci]
more bangs
2020-03-15 13:36:51 +01:00
Simon Christ 261f47fb6c delete _plot! 2020-03-15 13:36:51 +01:00
Simon Christ 476769fab6 add bangs 2020-03-15 13:36:51 +01:00
Simon Christ f7282dd49a implement the recipe_pipeline 2020-03-15 13:36:51 +01:00
Michael Krabbe Borregaard 5f09d97ca0 add function stubs from recipeutils 2020-03-15 13:36:51 +01:00
Michael Krabbe Borregaard e4754f3aeb Move out three functions 2020-03-15 13:36:51 +01:00
Anshul Singhvi 43fd0e76be Manually add the unregistered package in Travis 2020-03-15 13:36:51 +01:00
Michael Krabbe Borregaard 3ed781ac2d depend on recipeutils 2020-03-15 13:36:51 +01:00
Michael Krabbe Borregaard 6821148e8e cap fixedpointnumbers to 0.7 2020-03-15 09:30:07 +01:00
Simon Christ 3255c5a122 Allow legend vectors (#2449)
* allow vector legends

* format file pgfplotsx.jl

* handle unmatching vectors

* add test

* format test_pgfplotsx.jl
2020-03-14 15:44:07 +01:00
Josef Heinen bb9d36eb7a Update Project.toml 2020-03-13 16:40:49 +01:00
Simon Christ 50984b3bd2 bump version 2020-03-13 14:41:56 +01:00
Simon Christ 7520c1dced Merge pull request #2454 from JuliaPlots/bbs/check-examples
examples to skip
2020-03-13 14:19:10 +01:00
Simon Christ bfd468fc3a Merge pull request #2453 from JuliaPlots/bbs/fix-rib-example
fix ribbon-example
2020-03-13 14:18:45 +01:00
Simon Christ 3ab0078818 add skipping examples 2020-03-13 13:53:12 +01:00
Simon Christ 1fc587559c fix ribbon-example 2020-03-13 13:50:26 +01:00
Simon Christ 4821e04145 Merge pull request #2450 from BeastyBlacksmith/pgfplotsx
recreate plot if it was shown
2020-03-12 13:13:01 +01:00
Simon Christ bb8e62769f recreate plot if it was shown 2020-03-11 16:30:51 +01:00
Yuval 15dc61feb5 Merge pull request #2444 from yha/tests-windows
Allow image comparison tests on Windows.
2020-03-09 01:39:23 +02:00
yha d0d72242cb Allow image comparison tests on Windows. 2020-03-08 18:24:39 +02:00
Daniel Schwabeneder bece5a2620 Merge pull request #2442 from JuliaPlots/release-0.29.6
Release a new version
2020-03-06 21:33:34 +01:00
Anshul Singhvi d972702d3e Update Project.toml 2020-03-06 15:01:42 -05:00
Simon Christ d08b33e696 Merge pull request #2438 from BeastyBlacksmith/pgfplotsx
Fix twinx()
2020-03-06 12:41:44 +01:00
Simon Christ da60b47af5 format backend code 2020-03-06 12:17:42 +01:00
Simon Christ 0ac63857c4 fix twinx 2020-03-06 12:17:42 +01:00
Simon Christ d08422811e format file 2020-03-06 12:17:42 +01:00
Simon Christ d5788fc1e7 Allow legend for surfaces (#2439)
* allow legends for surfaces

* remove unnecessary checks

* redo necessary checks
2020-03-06 12:12:01 +01:00
13 changed files with 592 additions and 666 deletions
+1 -1
View File
@@ -35,4 +35,4 @@ notifications:
script:
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- if [[ `uname` = "Linux" ]]; then TESTCMD="xvfb-run julia"; else TESTCMD="julia"; fi
- $TESTCMD -e 'using Pkg; Pkg.build(); Pkg.test(coverage=true)'
- $TESTCMD -e 'using Pkg; pkg"add https://github.com/mkborregaard/RecipeUtils.jl"; Pkg.build(); Pkg.test(coverage=true)'
+4 -3
View File
@@ -1,7 +1,7 @@
name = "Plots"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
author = ["Tom Breloff (@tbreloff)"]
version = "0.29.5"
version = "0.29.7"
[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
@@ -21,6 +21,7 @@ PlotUtils = "995b91a9-d308-5afd-9ec6-746e21dbc043"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
RecipePipeline = "01d81517-befc-4cb6-b9ec-a95719d0359c"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
@@ -33,8 +34,8 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
[compat]
Contour = "0.5"
FFMPEG = "0.2, 0.3"
FixedPointNumbers = "0.6, 0.7, 0.8"
GR = "0.46, 0.47"
FixedPointNumbers = "0.6, 0.7"
GR = "0.46, 0.47, 0.48"
GeometryTypes = "0.7, 0.8"
JSON = "0.21"
Measures = "0.3"
+27 -2
View File
@@ -18,6 +18,31 @@ using Base.Meta
import Showoff
import StatsBase
import JSON
import RecipePipeline:
_process_userrecipe,
_process_plotrecipe,
_process_seriesrecipe,
_preprocess_args,
preprocessArgs!,
is_st_supported,
finalize_subplot!,
recipe_pipeline!,
_recipe_init!,
_recipe_after_user!,
_recipe_after_plot!,
_recipe_before_series!,
_recipe_finish!,
is_st_supported,
Formatted,
SliceIt,
FuncOrFuncs,
MaybeNumber,
MaybeString,
DataPoint,
trueOrAllTrue,
Surface,
AbstractSurface,
Volume
using Requires
@@ -222,7 +247,7 @@ end
const CURRENT_BACKEND = CurrentBackend(:none)
include("precompile.jl")
_precompile_()
# include("precompile.jl")
# _precompile_()
end # module
+108 -30
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
+149 -119
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
@@ -75,10 +75,10 @@ function Base.push!(pgfx_plot::PGFPlotsXPlot, item)
end
function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
if !pgfx_plot.is_created
if !pgfx_plot.is_created || pgfx_plot.was_shown
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,7 @@ 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 +157,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 +182,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 +210,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 +226,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,9 +253,6 @@ 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]
@@ -267,7 +262,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)
@@ -288,38 +286,44 @@ 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,
)
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!(
@@ -330,18 +334,7 @@ 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)
leg_opt = PGFPlotsX.Options()
if ribbon !== nothing
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,10 +345,38 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
pgfx_thickness_scaling(series),
)
end
end
# add to legend?
if sp[:legend] != :none && pgfx_should_add_to_legend(series)
leg_entry = if opt[:label] isa AVec
get(opt[:label], i, "")
elseif opt[:label] isa AbstractString
if i == 1
opt[:label]
else
""
end
else
throw(ArgumentError("Malformed label. label = $(opt[:label])"))
end
if leg_entry == ""
push!(segment_plot.options, "forget plot" => nothing)
continue
end
leg_opt = PGFPlotsX.Options()
if ribbon !== nothing
pgfx_filllegend!(axis.contents[end - 3].options, opt)
end
legend = PGFPlotsX.LegendEntry(leg_opt, leg_entry, false)
push!(axis, legend)
end
end # for segments
# 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)
@@ -498,7 +519,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
@@ -511,7 +532,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",
)
@@ -522,10 +543,9 @@ 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",
)
"""
@@ -578,11 +598,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={-}"
@@ -610,12 +627,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)
@@ -634,9 +648,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
@@ -678,6 +690,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(
@@ -690,19 +721,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
@@ -728,15 +762,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
@@ -806,7 +840,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...)),
@@ -857,7 +891,7 @@ 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],
@@ -871,10 +905,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
@@ -891,14 +923,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, ","), "}"),
@@ -914,44 +947,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],
@@ -970,7 +996,8 @@ 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
@@ -1042,6 +1069,7 @@ for mime in ("application/pdf", "image/png", "image/svg+xml")
mime::MIME{Symbol($mime)},
plt::Plot{PGFPlotsXBackend},
)
plt.o.was_shown = true
show(io, mime, plt.o.the_plot)
end
end
@@ -1051,6 +1079,7 @@ function _show(
mime::MIME{Symbol("application/x-tex")},
plt::Plot{PGFPlotsXBackend},
)
plt.o.was_shown = true
PGFPlotsX.print_tex(
io,
plt.o.the_plot,
@@ -1059,5 +1088,6 @@ function _show(
end
function _display(plt::Plot{PGFPlotsXBackend})
plt.o.was_shown = true
display(PGFPlotsX.PGFPlotsXDisplay(), plt.o.the_plot)
end
-48
View File
@@ -650,23 +650,6 @@ end
# -----------------------------------------------------------------------
abstract type AbstractSurface end
"represents a contour or surface mesh"
struct Surface{M<:AMat} <: AbstractSurface
surf::M
end
Surface(f::Function, x, y) = Surface(Float64[f(xi,yi) for yi in y, xi in x])
Base.Array(surf::Surface) = surf.surf
for f in (:length, :size)
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
end
Base.copy(surf::Surface) = Surface(copy(surf.surf))
Base.eltype(surf::Surface{T}) where {T} = eltype(T)
function expand_extrema!(a::Axis, surf::Surface)
ex = a[:extrema]
for vi in surf.surf
@@ -686,30 +669,6 @@ end
# # I don't want to clash with ValidatedNumerics, but this would be nice:
# ..(a::T, b::T) = (a,b)
struct Volume{T}
v::Array{T,3}
x_extents::Tuple{T,T}
y_extents::Tuple{T,T}
z_extents::Tuple{T,T}
end
default_extents(::Type{T}) where {T} = (zero(T), one(T))
function Volume(v::Array{T,3},
x_extents = default_extents(T),
y_extents = default_extents(T),
z_extents = default_extents(T)) where T
Volume(v, x_extents, y_extents, z_extents)
end
Base.Array(vol::Volume) = vol.v
for f in (:length, :size)
@eval Base.$f(vol::Volume, args...) = $f(vol.v, args...)
end
Base.copy(vol::Volume{T}) where {T} = Volume{T}(copy(vol.v), vol.x_extents, vol.y_extents, vol.z_extents)
Base.eltype(vol::Volume{T}) where {T} = T
# -----------------------------------------------------------------------
# style is :open or :closed (for now)
struct Arrow
@@ -771,13 +730,6 @@ function add_arrows(func::Function, x::AVec, y::AVec)
end
end
# -----------------------------------------------------------------------
"Represents data values with formatting that should apply to the tick labels."
struct Formatted{T}
data::T
formatter::Function
end
# -----------------------------------------------------------------------
"create a BezierCurve for plotting"
+9 -2
View File
@@ -800,7 +800,7 @@ const _examples = PlotExample[
plot(
plot(
0:10;
ribbon = (LinRange(0, 2, 10), LinRange(0, 1, 10)),
ribbon = (LinRange(0, 2, 11), LinRange(0, 1, 11)),
),
plot(0:10; ribbon = 0:0.5:5),
plot(0:10; ribbon = sqrt),
@@ -885,7 +885,14 @@ _backend_skips = Dict(
:plotlyjs => [2, 21, 24, 25, 30, 31],
:plotly => [2, 21, 24, 25, 30, 31],
:pgfplots => [2, 5, 6, 10, 16, 20, 22, 23, 25, 28, 30, 31, 34, 37, 38, 39],
)
:pgfplotsx => [ 6, # images
10, # histogram2d
20, # annotations due to missing sanitation
22, # contourf
23, # pie
32, # spy
38, # histogram2d
] )
+9 -111
View File
@@ -1,5 +1,13 @@
function finalize_subplot!(plt::Plot, st, plotattributes::AKW)
sp = _prepare_subplot(plt, plotattributes)
_prepare_annotations(sp, plotattributes)
_expand_subplot_extrema(sp, plotattributes, st)
_update_series_attributes!(plotattributes, plt, sp)
_add_the_series(plt, sp, plotattributes)
end
# Override the RecipesPipeline `is_st_supported` for Plots.
is_st_supported(::Plot, st::Symbol) = is_seriestype_supported(st)
# ------------------------------------------------------------------
# preprocessing
@@ -59,39 +67,6 @@ end
# user recipes
function _process_userrecipes(plt::Plot, plotattributes::AKW, args)
still_to_process = RecipeData[]
args = _preprocess_args(plotattributes, args, still_to_process)
# for plotting recipes, swap out the args and update the parameter dictionary
# we are keeping a stack of series that still need to be processed.
# each pass through the loop, we pop one off and apply the recipe.
# the recipe will return a list a Series objects... the ones that are
# finished (no more args) get added to the kw_list, the ones that are not
# are placed on top of the stack and are then processed further.
kw_list = KW[]
while !isempty(still_to_process)
# grab the first in line to be processed and either add it to the kw_list or
# pass it through apply_recipe to generate a list of RecipeData objects (data + attributes)
# for further processing.
next_series = popfirst!(still_to_process)
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
if !(typeof(next_series) <: RecipeData)
error("Inputs couldn't be processed... expected RecipeData but got: $next_series")
end
if isempty(next_series.args)
_process_userrecipe(plt, kw_list, next_series)
else
rd_list = RecipesBase.apply_recipe(next_series.plotattributes, next_series.args...)
prepend!(still_to_process,rd_list)
end
end
# don't allow something else to handle it
plotattributes[:smooth] = false
kw_list
end
function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeData)
# when the arg tuple is empty, that means there's nothing left to recursively
# process... finish up and add to the kw_list
@@ -168,39 +143,6 @@ function _add_smooth_kw(kw_list::Vector{KW}, kw::AKW)
end
# ------------------------------------------------------------------
# plot recipes
# Grab the first in line to be processed and pass it through apply_recipe
# to generate a list of RecipeData objects (data + attributes).
# If we applied a "plot recipe" without error, then add the returned datalist's KWs,
# otherwise we just add the original KW.
function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_process::Vector{KW})
if !isa(get(kw, :seriestype, nothing), Symbol)
# seriestype was never set, or it's not a Symbol, so it can't be a plot recipe
push!(kw_list, kw)
return
end
try
st = kw[:seriestype]
st = kw[:seriestype] = get(_typeAliases, st, st)
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
for data in datalist
preprocessArgs!(data.plotattributes)
if data.plotattributes[:seriestype] == st
error("Plot recipe $st returned the same seriestype: $(data.plotattributes)")
end
push!(still_to_process, data.plotattributes)
end
catch err
if isa(err, MethodError)
push!(kw_list, kw)
else
rethrow()
end
end
return
end
# ------------------------------------------------------------------
# setup plot and subplot
@@ -382,47 +324,3 @@ function _add_the_series(plt, sp, plotattributes)
push!(sp.series_list, series)
_series_added(plt, series)
end
# -------------------------------------------------------------------------------
# this method recursively applies series recipes when the seriestype is not supported
# natively by the backend
function _process_seriesrecipe(plt::Plot, plotattributes::AKW)
#println("process $(typeof(plotattributes))")
# replace seriestype aliases
st = Symbol(plotattributes[:seriestype])
st = plotattributes[:seriestype] = get(_typeAliases, st, st)
# shapes shouldn't have fillrange set
if plotattributes[:seriestype] == :shape
plotattributes[:fillrange] = nothing
end
# if it's natively supported, finalize processing and pass along to the backend, otherwise recurse
if is_seriestype_supported(st)
sp = _prepare_subplot(plt, plotattributes)
_prepare_annotations(sp, plotattributes)
_expand_subplot_extrema(sp, plotattributes, st)
_update_series_attributes!(plotattributes, plt, sp)
_add_the_series(plt, sp, plotattributes)
else
# get a sub list of series for this seriestype
datalist = RecipesBase.apply_recipe(plotattributes, Val{st}, plotattributes[:x], plotattributes[:y], plotattributes[:z])
# assuming there was no error, recursively apply the series recipes
for data in datalist
if isa(data, RecipeData)
preprocessArgs!(data.plotattributes)
if data.plotattributes[:seriestype] == st
error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.")
end
_process_seriesrecipe(plt, data.plotattributes)
else
@warn("Unhandled recipe: $(data)")
break
end
end
end
nothing
end
+20 -58
View File
@@ -54,7 +54,7 @@ function plot(args...; kw...)
# create an empty Plot then process
plt = Plot()
# plt.user_attr = plotattributes
_plot!(plt, plotattributes, args)
recipe_pipeline!(plt, plotattributes, args, type_aliases=_typeAliases)
end
# build a new plot from existing plots
@@ -155,7 +155,7 @@ function plot!(plt::Plot, args...; kw...)
plotattributes = KW(kw)
preprocessArgs!(plotattributes)
# merge!(plt.user_attr, plotattributes)
_plot!(plt, plotattributes, args)
recipe_pipeline!(plt, plotattributes, args, type_aliases=_typeAliases)
end
# -------------------------------------------------------------------------------
@@ -163,78 +163,41 @@ end
# this is the core plotting function. recursively apply recipes to build
# a list of series KW dicts.
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
function _plot!(plt::Plot, plotattributes::AKW, args::Tuple)
## here we implement the recipe interface
function _recipe_init!(plt::Plot, plotattributes::AKW, args::Tuple)
plotattributes[:plot_object] = plt
if !isempty(args) && !isdefined(Main, :StatsPlots) &&
first(split(string(typeof(args[1])), ".")) == "DataFrames"
@warn("You're trying to plot a DataFrame, but this functionality is provided by StatsPlots")
end
end
# --------------------------------
# "USER RECIPES"
# --------------------------------
kw_list = _process_userrecipes(plt, plotattributes, args)
# @info(1)
# map(DD, kw_list)
# --------------------------------
# "PLOT RECIPES"
# --------------------------------
# "plot recipe", which acts like a series type, and is processed before
# the plot layout is created, which allows for setting layouts and other plot-wide attributes.
# we get inputs which have been fully processed by "user recipes" and "type recipes",
# so we can expect standard vectors, surfaces, etc. No defaults have been set yet.
still_to_process = kw_list
kw_list = KW[]
while !isempty(still_to_process)
next_kw = popfirst!(still_to_process)
_process_plotrecipe(plt, next_kw, kw_list, still_to_process)
end
# @info(2)
# map(DD, kw_list)
function _recipe_after_plot!(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
# --------------------------------
# Plot/Subplot/Layout setup
# --------------------------------
_plot_setup(plt, plotattributes, kw_list)
_subplot_setup(plt, plotattributes, kw_list)
end
# !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!!
# !!! The next step is to recursively apply series recipes until the backend supports that series type !!!
function _recipe_after_user!(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
# don't allow something else to handle it
plotattributes[:smooth] = false
end
# --------------------------------
# "SERIES RECIPES"
# --------------------------------
function _recipe_before_series!(plt::Plot, kw, kw_list)
sp::Subplot = kw[:subplot]
# @info(3)
# map(DD, kw_list)
# in series attributes given as vector with one element per series,
# select the value for current series
_slice_series_args!(kw, plt, sp, series_idx(kw_list,kw))
for kw in kw_list
sp::Subplot = kw[:subplot]
# in series attributes given as vector with one element per series,
# select the value for current series
_slice_series_args!(kw, plt, sp, series_idx(kw_list,kw))
series_attr = Attr(kw, _series_defaults)
# now we have a fully specified series, with colors chosen. we must recursively handle
# series recipes, which dispatch on seriestype. If a backend does not natively support a seriestype,
# we check for a recipe that will convert that series type into one made up of lower-level components.
# For example, a histogram is just a bar plot with binned data, a bar plot is really a filled step plot,
# and a step plot is really just a path. So any backend that supports drawing a path will implicitly
# be able to support step, bar, and histogram plots (and any recipes that use those components).
_process_seriesrecipe(plt, series_attr)
end
# --------------------------------
series_attr = Attr(kw, _series_defaults)
end
function _recipe_finish!(plt::Plot, plotattributes::AKW, args::Tuple)
current(plt)
# do we want to force display?
@@ -246,7 +209,6 @@ function _plot!(plt::Plot, plotattributes::AKW, args::Tuple)
plt
end
# we're getting ready to display/output. prep for layout calcs, then update
# the plot object after
function prepare_output(plt::Plot)
-132
View File
@@ -1,71 +1,4 @@
# create a new "build_series_args" which converts all inputs into xs = Any[xitems], ys = Any[yitems].
# Special handling for: no args, xmin/xmax, parametric, dataframes
# Then once inputs have been converted, build the series args, map functions, etc.
# This should cut down on boilerplate code and allow more focused dispatch on type
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
const FuncOrFuncs{F} = Union{F, Vector{F}, Matrix{F}}
const MaybeNumber = Union{Number, Missing}
const MaybeString = Union{AbstractString, Missing}
const DataPoint = Union{MaybeNumber, MaybeString}
prepareSeriesData(x) = error("Cannot convert $(typeof(x)) to series data for plotting")
prepareSeriesData(::Nothing) = nothing
prepareSeriesData(t::Tuple{T, T}) where {T<:Number} = t
prepareSeriesData(f::Function) = f
prepareSeriesData(a::AbstractArray{<:MaybeNumber}) = replace!(
x -> ismissing(x) || isinf(x) ? NaN : x,
map(float,a))
prepareSeriesData(a::AbstractArray{<:MaybeString}) = replace(x -> ismissing(x) ? "" : x, a)
prepareSeriesData(s::Surface{<:AMat{<:MaybeNumber}}) = Surface(prepareSeriesData(s.surf))
prepareSeriesData(s::Surface) = s # non-numeric Surface, such as an image
prepareSeriesData(v::Volume) = Volume(prepareSeriesData(v.v), v.x_extents, v.y_extents, v.z_extents)
# default: assume x represents a single series
convertToAnyVector(x, plotattributes) = Any[prepareSeriesData(x)]
# fixed number of blank series
convertToAnyVector(n::Integer, plotattributes) = Any[zeros(0) for i in 1:n]
# vector of data points is a single series
convertToAnyVector(v::AVec{<:DataPoint}, plotattributes) = Any[prepareSeriesData(v)]
# list of things (maybe other vectors, functions, or something else)
function convertToAnyVector(v::AVec, plotattributes)
if all(x -> x isa MaybeNumber, v)
convertToAnyVector(Vector{MaybeNumber}(v), plotattributes)
elseif all(x -> x isa MaybeString, v)
convertToAnyVector(Vector{MaybeString}(v), plotattributes)
else
vcat((convertToAnyVector(vi, plotattributes) for vi in v)...)
end
end
# Matrix is split into columns
function convertToAnyVector(v::AMat{<:DataPoint}, plotattributes)
if all3D(plotattributes)
Any[prepareSeriesData(Surface(v))]
else
Any[prepareSeriesData(v[:, i]) for i in axes(v, 2)]
end
end
# --------------------------------------------------------------------
# Fillranges & ribbons
process_fillrange(range::Number, plotattributes) = [range]
process_fillrange(range, plotattributes) = convertToAnyVector(range, plotattributes)
process_ribbon(ribbon::Number, plotattributes) = [ribbon]
process_ribbon(ribbon, plotattributes) = convertToAnyVector(ribbon, plotattributes)
# ribbon as a tuple: (lower_ribbons, upper_ribbons)
process_ribbon(ribbon::Tuple{Any,Any}, plotattributes) = collect(zip(convertToAnyVector(ribbon[1], plotattributes),
convertToAnyVector(ribbon[2], plotattributes)))
# --------------------------------------------------------------------
# TODO: can we avoid the copy here? one error that crops up is that mapping functions over the same array
@@ -107,70 +40,6 @@ compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all no
# we are going to build recipes to do the processing and splitting of the args
# ensure we dispatch to the slicer
struct SliceIt end
# the catch-all recipes
@recipe function f(::Type{SliceIt}, x, y, z)
# handle data with formatting attached
if typeof(x) <: Formatted
xformatter := x.formatter
x = x.data
end
if typeof(y) <: Formatted
yformatter := y.formatter
y = y.data
end
if typeof(z) <: Formatted
zformatter := z.formatter
z = z.data
end
xs = convertToAnyVector(x, plotattributes)
ys = convertToAnyVector(y, plotattributes)
zs = convertToAnyVector(z, plotattributes)
fr = pop!(plotattributes, :fillrange, nothing)
fillranges = process_fillrange(fr, plotattributes)
mf = length(fillranges)
rib = pop!(plotattributes, :ribbon, nothing)
ribbons = process_ribbon(rib, plotattributes)
mr = length(ribbons)
# @show zs
mx = length(xs)
my = length(ys)
mz = length(zs)
if mx > 0 && my > 0 && mz > 0
for i in 1:max(mx, my, mz)
# add a new series
di = copy(plotattributes)
xi, yi, zi = xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)]
di[:x], di[:y], di[:z] = compute_xyz(xi, yi, zi)
# handle fillrange
fr = fillranges[mod1(i,mf)]
di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr
# handle ribbons
rib = ribbons[mod1(i,mr)]
di[:ribbon] = isa(rib, Function) ? map(rib, di[:x]) : rib
push!(series_list, RecipeData(di, ()))
end
end
nothing # don't add a series for the main block
end
# this is the default "type recipe"... just pass the object through
@recipe f(::Type{T}, v::T) where {T<:Any} = v
# this should catch unhandled "series recipes" and error with a nice message
@recipe f(::Type{V}, x, y, z) where {V<:Val} = error("The backend must not support the series type $V, and there isn't a series recipe defined.")
_apply_type_recipe(plotattributes, v) = RecipesBase.apply_recipe(plotattributes, typeof(v), v)[1].args[1]
@@ -278,7 +147,6 @@ end
@recipe f(n::Integer) = is3d(get(plotattributes,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
all3D(plotattributes) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image, :plots_heatmap), get(plotattributes, :seriestype, :none))
# return a surface if this is a 3d plot, otherwise let it be sliced up
@recipe function f(mat::AMat{T}) where T<:Union{Integer,AbstractFloat,Missing}
-3
View File
@@ -717,9 +717,6 @@ makekw(; kw...) = KW(kw)
wraptuple(x::Tuple) = x
wraptuple(x) = (x,)
trueOrAllTrue(f::Function, x::AbstractArray) = all(f, x)
trueOrAllTrue(f::Function, x) = f(x)
allLineTypes(arg) = trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg)
allStyles(arg) = trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
allShapes(arg) = trueOrAllTrue(a -> is_marker_supported(get(_markerAliases, a, a)), arg) ||
+4 -2
View File
@@ -42,7 +42,7 @@ include("imgcomp.jl")
Random.seed!(1234)
default(show=false, reuse=true)
is_ci() = get(ENV, "CI", "false") == "true"
img_tol = is_ci() ? 1e-2 : 1e-3
img_tol = is_ci() ? 1e-2 : Sys.islinux() ? 1e-3 : 0.1
## Uncomment the following lines to update reference images for different backends
@@ -76,7 +76,9 @@ end
@test gr() == Plots.GRBackend()
@test backend() == Plots.GRBackend()
@static if Sys.islinux()
@static if haskey(ENV, "APPVEYOR")
@info "Skipping GR image comparison tests on AppVeyor"
else
image_comparison_facts(:gr, tol=img_tol, skip = Plots._backend_skips[:gr])
end
end
+261 -155
View File
@@ -1,166 +1,272 @@
using Plots, Test
pgfplotsx()
function create_plot( args...; kwargs... )
pgfx_plot = plot(args...; kwargs...)
return pgfx_plot, repr("application/x-tex", pgfx_plot)
function create_plot(args...; kwargs...)
pgfx_plot = plot(args...; kwargs...)
return pgfx_plot, repr("application/x-tex", pgfx_plot)
end
function create_plot!( args...; kwargs... )
pgfx_plot = plot!(args...; kwargs...)
return pgfx_plot, repr("application/x-tex", pgfx_plot)
function create_plot!(args...; kwargs...)
pgfx_plot = plot!(args...; kwargs...)
return pgfx_plot, repr("application/x-tex", pgfx_plot)
end
@testset "PGFPlotsX" begin
pgfx_plot = plot(1:5)
Plots._update_plot_object(pgfx_plot)
@test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument
@test pgfx_plot.series_list[1].plotattributes[:quiver] === nothing
axis = Plots.pgfx_axes(pgfx_plot.o)[1]
@test count( x-> x isa PGFPlotsX.Plot, axis.contents ) == 1
@test !haskey(axis.contents[1].options.dict, "fill")
pgfx_plot = plot(1:5)
Plots._update_plot_object(pgfx_plot)
@test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument
@test pgfx_plot.series_list[1].plotattributes[:quiver] === nothing
axis = Plots.pgfx_axes(pgfx_plot.o)[1]
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 1
@test !haskey(axis.contents[1].options.dict, "fill")
@testset "3D docs example" begin
n = 100
ts = range(0, stop=8π, length=n)
x = ts .* map(cos, ts)
y = (0.1ts) .* map(sin, ts)
z = 1:n
pl = plot(x, y, z, zcolor=reverse(z), m=(10, 0.8, :blues, Plots.stroke(0)), leg=false, cbar=true, w=5)
pgfx_plot = plot!(pl, zeros(n), zeros(n), 1:n, w=10)
Plots._update_plot_object(pgfx_plot)
if @test_nowarn(haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true)
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
end
end # testset
@testset "Color docs example" begin
y = rand(100)
plot(0:10:100, rand(11, 4), lab="lines", w=3, palette=:grays, fill=0, α=0.6)
pl = scatter!(y, zcolor=abs.(y .- 0.5), m=(:heat, 0.8, Plots.stroke(1, :green)), ms=10 * abs.(y .- 0.5) .+ 4, lab="grad")
Plots._update_plot_object(pl)
axis = Plots.pgfx_axes(pl.o)[1]
@test count( x->x isa PGFPlotsX.LegendEntry, axis.contents ) == 5
@test count( x->x isa PGFPlotsX.Plot, axis.contents ) == 108 # each marker is its own plot, fillranges create 2 plot-objects
marker = axis.contents[15]
@test marker isa PGFPlotsX.Plot
@test marker.options["mark"] == "*"
@test marker.options["mark options"]["color"] == RGBA{Float64}( colorant"green", 0.8)
@test marker.options["mark options"]["line width"] == 1
end # testset
@testset "Plot in pieces" begin
plot(rand(100) / 3, reg=true, fill=(0, :green))
scatter!(rand(100), markersize=6, c=:orange)
end # testset
@testset "Marker types" begin
markers = filter((m->begin
m in Plots.supported_markers()
end), Plots._shape_keys)
markers = reshape(markers, 1, length(markers))
n = length(markers)
x = (range(0, stop=10, length=n + 2))[2:end - 1]
y = repeat(reshape(reverse(x), 1, :), n, 1)
scatter(x, y, m=(8, :auto), lab=map(string, markers), bg=:linen, xlim=(0, 10), ylim=(0, 10))
end # testset
@testset "Layout" begin
plot(Plots.fakedata(100, 10), layout=4, palette=[:grays :blues :heat :lightrainbow], bg_inside=[:orange :pink :darkblue :black])
end # testset
@testset "Polar plots" begin
Θ = range(0, stop=1.5π, length=100)
r = abs.(0.1 * randn(100) + sin.(3Θ))
plot(Θ, r, proj=:polar, m=2)
end # testset
@testset "Drawing shapes" begin
verts = [(-1.0, 1.0), (-1.28, 0.6), (-0.2, -1.4), (0.2, -1.4), (1.28, 0.6), (1.0, 1.0), (-1.0, 1.0), (-0.2, -0.6), (0.0, -0.2), (-0.4, 0.6), (1.28, 0.6), (0.2, -1.4), (-0.2, -1.4), (0.6, 0.2), (-0.2, 0.2), (0.0, -0.2), (0.2, 0.2), (-0.2, -0.6)]
x = 0.1:0.2:0.9
y = 0.7 * rand(5) .+ 0.15
plot(x, y, line=(3, :dash, :lightblue), marker=(Shape(verts), 30, RGBA(0, 0, 0, 0.2)), bg=:pink, fg=:darkblue, xlim=(0, 1), ylim=(0, 1), leg=false)
end # testset
@testset "Histogram 2D" begin
histogram2d(randn(10000), randn(10000), nbins=20)
end # testset
@testset "Heatmap-like" begin
xs = [string("x", i) for i = 1:10]
ys = [string("y", i) for i = 1:4]
z = float((1:4) * reshape(1:10, 1, :))
pgfx_plot = heatmap(xs, ys, z, aspect_ratio=1)
Plots._update_plot_object(pgfx_plot)
if @test_nowarn(haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true)
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colormap name"] == "plots1"
end
@testset "3D docs example" begin
n = 100
ts = range(0, stop = 8π, length = n)
x = ts .* map(cos, ts)
y = (0.1ts) .* map(sin, ts)
z = 1:n
pl = plot(
x,
y,
z,
zcolor = reverse(z),
m = (10, 0.8, :blues, Plots.stroke(0)),
leg = false,
cbar = true,
w = 5,
)
pgfx_plot = plot!(pl, zeros(n), zeros(n), 1:n, w = 10)
Plots._update_plot_object(pgfx_plot)
if @test_nowarn(
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
)
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
end
end # testset
@testset "Color docs example" begin
y = rand(100)
plot(
0:10:100,
rand(11, 4),
lab = "lines",
w = 3,
palette = :grays,
fill = 0,
α = 0.6,
)
pl = scatter!(
y,
zcolor = abs.(y .- 0.5),
m = (:heat, 0.8, Plots.stroke(1, :green)),
ms = 10 * abs.(y .- 0.5) .+ 4,
lab = ["grad", "", "ient"],
)
Plots._update_plot_object(pl)
axis = Plots.pgfx_axes(pl.o)[1]
@test count(x -> x isa PGFPlotsX.LegendEntry, axis.contents) == 6
@test count(x -> x isa PGFPlotsX.Plot, axis.contents) == 108 # each marker is its own plot, fillranges create 2 plot-objects
marker = axis.contents[15]
@test marker isa PGFPlotsX.Plot
@test marker.options["mark"] == "*"
@test marker.options["mark options"]["color"] ==
RGBA{Float64}(colorant"green", 0.8)
@test marker.options["mark options"]["line width"] == 1
end # testset
@testset "Plot in pieces" begin
plot(rand(100) / 3, reg = true, fill = (0, :green))
scatter!(rand(100), markersize = 6, c = :orange)
end # testset
@testset "Marker types" begin
markers = filter((m -> begin
m in Plots.supported_markers()
end), Plots._shape_keys)
markers = reshape(markers, 1, length(markers))
n = length(markers)
x = (range(0, stop = 10, length = n + 2))[2:(end - 1)]
y = repeat(reshape(reverse(x), 1, :), n, 1)
scatter(
x,
y,
m = (8, :auto),
lab = map(string, markers),
bg = :linen,
xlim = (0, 10),
ylim = (0, 10),
)
end # testset
@testset "Layout" begin
plot(
Plots.fakedata(100, 10),
layout = 4,
palette = [:grays :blues :heat :lightrainbow],
bg_inside = [:orange :pink :darkblue :black],
)
end # testset
@testset "Polar plots" begin
Θ = range(0, stop = 1.5π, length = 100)
r = abs.(0.1 * randn(100) + sin.(3Θ))
plot(Θ, r, proj = :polar, m = 2)
end # testset
@testset "Drawing shapes" begin
verts = [
(-1.0, 1.0),
(-1.28, 0.6),
(-0.2, -1.4),
(0.2, -1.4),
(1.28, 0.6),
(1.0, 1.0),
(-1.0, 1.0),
(-0.2, -0.6),
(0.0, -0.2),
(-0.4, 0.6),
(1.28, 0.6),
(0.2, -1.4),
(-0.2, -1.4),
(0.6, 0.2),
(-0.2, 0.2),
(0.0, -0.2),
(0.2, 0.2),
(-0.2, -0.6),
]
x = 0.1:0.2:0.9
y = 0.7 * rand(5) .+ 0.15
plot(
x,
y,
line = (3, :dash, :lightblue),
marker = (Shape(verts), 30, RGBA(0, 0, 0, 0.2)),
bg = :pink,
fg = :darkblue,
xlim = (0, 1),
ylim = (0, 1),
leg = false,
)
end # testset
@testset "Histogram 2D" begin
histogram2d(randn(10000), randn(10000), nbins = 20)
end # testset
@testset "Heatmap-like" begin
xs = [string("x", i) for i = 1:10]
ys = [string("y", i) for i = 1:4]
z = float((1:4) * reshape(1:10, 1, :))
pgfx_plot = heatmap(xs, ys, z, aspect_ratio = 1)
Plots._update_plot_object(pgfx_plot)
if @test_nowarn(
haskey(Plots.pgfx_axes(pgfx_plot.o)[1].options.dict, "colorbar") == true
)
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colorbar"] === nothing
@test Plots.pgfx_axes(pgfx_plot.o)[1]["colormap name"] == "plots1"
end
pgfx_plot = wireframe(xs, ys, z, aspect_ratio=1)
# TODO: clims are wrong
end # testset
@testset "Contours" begin
x = 1:0.5:20
y = 1:0.5:10
f(x, y) = begin
(3x + y ^ 2) * abs(sin(x) + cos(y))
end
X = repeat(reshape(x, 1, :), length(y), 1)
Y = repeat(y, 1, length(x))
Z = map(f, X, Y)
p2 = contour(x, y, Z)
p1 = contour(x, y, f, fill=true)
plot(p1, p2)
# TODO: colorbar for filled contours
end # testset
@testset "Varying colors" begin
t = range(0, stop=1, length=100)
θ = (6π) .* t
x = t .* cos.(θ)
y = t .* sin.(θ)
p1 = plot(x, y, line_z=t, linewidth=3, legend=false)
p2 = scatter(x, y, marker_z=((x, y)->begin
x + y
end), color=:bluesreds, legend=false)
plot(p1, p2)
end # testset
@testset "Framestyles" begin
scatter(fill(randn(10), 6), fill(randn(10), 6), framestyle=[:box :semi :origin :zerolines :grid :none], title=[":box" ":semi" ":origin" ":zerolines" ":grid" ":none"], color=permutedims(1:6), layout=6, label="", markerstrokewidth=0, ticks=-2:2)
# TODO: support :semi
end # testset
@testset "Quiver" begin
x = -2pi:0.2:2*pi
y = sin.(x)
pgfx_plot = wireframe(xs, ys, z, aspect_ratio = 1)
# TODO: clims are wrong
end # testset
@testset "Contours" begin
x = 1:0.5:20
y = 1:0.5:10
f(x, y) = begin
(3x + y^2) * abs(sin(x) + cos(y))
end
X = repeat(reshape(x, 1, :), length(y), 1)
Y = repeat(y, 1, length(x))
Z = map(f, X, Y)
p2 = contour(x, y, Z)
p1 = contour(x, y, f, fill = true)
plot(p1, p2)
# TODO: colorbar for filled contours
end # testset
@testset "Varying colors" begin
t = range(0, stop = 1, length = 100)
θ = (6π) .* t
x = t .* cos.(θ)
y = t .* sin.(θ)
p1 = plot(x, y, line_z = t, linewidth = 3, legend = false)
p2 = scatter(
x,
y,
marker_z = ((x, y) -> begin
x + y
end),
color = :bluesreds,
legend = false,
)
plot(p1, p2)
end # testset
@testset "Framestyles" begin
scatter(
fill(randn(10), 6),
fill(randn(10), 6),
framestyle = [:box :semi :origin :zerolines :grid :none],
title = [":box" ":semi" ":origin" ":zerolines" ":grid" ":none"],
color = permutedims(1:6),
layout = 6,
label = "",
markerstrokewidth = 0,
ticks = -2:2,
)
# TODO: support :semi
end # testset
@testset "Quiver" begin
x = (-2pi):0.2:(2 * pi)
y = sin.(x)
u = ones(length(x))
v = cos.(x)
arrow_plot = plot( x, y, quiver = (u, v), arrow = true )
# TODO: could adjust limits to fit arrows if too long, but how?
# TODO: get latex available on CI
# mktempdir() do path
# @test_nowarn savefig(arrow_plot, path*"arrow.pdf")
# end
end # testset
@testset "Annotations" begin
y = rand(10)
plot(y, annotations=(3, y[3], Plots.text("this is \\#3", :left)), leg=false)
annotate!([(5, y[5], Plots.text("this is \\#5", 16, :red, :center)), (10, y[10], Plots.text("this is \\#10", :right, 20, "courier"))])
annotation_plot = scatter!(range(2, stop=8, length=6), rand(6), marker=(50, 0.2, :orange), series_annotations=["series", "annotations", "map", "to", "series", Plots.text("data", :green)])
# mktempdir() do path
# @test_nowarn savefig(annotation_plot, path*"annotation.pdf")
# end
end # testset
@testset "Ribbon" begin
aa = rand(10)
bb = rand(10)
cc = rand(10)
conf = [aa-cc bb-cc]
ribbon_plot = plot(collect(1:10),fill(1,10), ribbon=(conf[:,1],conf[:,2]))
Plots._update_plot_object(ribbon_plot)
axis = Plots.pgfx_axes(ribbon_plot.o)[1]
plots = filter(x->x isa PGFPlotsX.Plot, axis.contents)
@test length(plots) == 4
@test !haskey(plots[1].options.dict, "fill")
@test !haskey(plots[2].options.dict, "fill")
@test !haskey(plots[3].options.dict, "fill")
@test haskey(plots[4].options.dict, "fill")
@test ribbon_plot.o !== nothing
@test ribbon_plot.o.the_plot !== nothing
# mktempdir() do path
# @test_nowarn savefig(ribbon_plot, path*"ribbon.svg")
# end
end # testset
end # testset
u = ones(length(x))
v = cos.(x)
arrow_plot = plot(x, y, quiver = (u, v), arrow = true)
# TODO: could adjust limits to fit arrows if too long, but how?
# TODO: get latex available on CI
# mktempdir() do path
# @test_nowarn savefig(arrow_plot, path*"arrow.pdf")
# end
end # testset
@testset "Annotations" begin
y = rand(10)
plot(
y,
annotations = (3, y[3], Plots.text("this is \\#3", :left)),
leg = false,
)
annotate!([
(5, y[5], Plots.text("this is \\#5", 16, :red, :center)),
(10, y[10], Plots.text("this is \\#10", :right, 20, "courier")),
])
annotation_plot = scatter!(
range(2, stop = 8, length = 6),
rand(6),
marker = (50, 0.2, :orange),
series_annotations = [
"series",
"annotations",
"map",
"to",
"series",
Plots.text("data", :green),
],
)
# mktempdir() do path
# @test_nowarn savefig(annotation_plot, path*"annotation.pdf")
# end
end # testset
@testset "Ribbon" begin
aa = rand(10)
bb = rand(10)
cc = rand(10)
conf = [aa - cc bb - cc]
ribbon_plot =
plot(collect(1:10), fill(1, 10), ribbon = (conf[:, 1], conf[:, 2]))
Plots._update_plot_object(ribbon_plot)
axis = Plots.pgfx_axes(ribbon_plot.o)[1]
plots = filter(x -> x isa PGFPlotsX.Plot, axis.contents)
@test length(plots) == 4
@test !haskey(plots[1].options.dict, "fill")
@test !haskey(plots[2].options.dict, "fill")
@test !haskey(plots[3].options.dict, "fill")
@test haskey(plots[4].options.dict, "fill")
@test ribbon_plot.o !== nothing
@test ribbon_plot.o.the_plot !== nothing
# mktempdir() do path
# @test_nowarn savefig(ribbon_plot, path*"ribbon.svg")
# end
end # testset
end # testset