From 9a4f6c5865d0a7b2fb97639168d647b978a1f331 Mon Sep 17 00:00:00 2001 From: "Tamas K. Papp" Date: Thu, 15 Feb 2018 14:44:32 +0100 Subject: [PATCH 1/3] Rewrite Plot and axis-like types. This is a step towards fixing #44 (almost there!). 1. New user-facing plot/plot3 constructor is ```julia Plot([incrementa], [options], data, trailing...) ``` and similarly for `Plot3`. `incremental` defaults to `false`, as this seems to be the most common use case. Some validation is done on `data` (checking for type). This removes the `label` kwarg, and allows us to close #16. Examples now recommend an explicit `\addlegendentry`. Docstrings are added for everything, and examples are rewritten accordingly. 2. Axis-like code cleaned up a bit with macros. Variations on log axes added. Explicitly document that strings are emitted as is. 3. GroupPlot rewritten, allow multiple plots and empty \nextgroupplot, two examples added, one from the manual and one for multiple plots. 4. Replaced random examples with deterministic ones, perhaps stick to this and close #63? Minor indentation issues fixed. 5. Minor fixes for testing framework. --- docs/make.jl | 1 + docs/src/examples/axislike.md | 37 ++++++++ docs/src/examples/coordinates.md | 6 +- docs/src/examples/gallery.md | 44 ++++++++- docs/src/examples/juliatypes.md | 76 ++++++++-------- docs/src/examples/tables.md | 10 +-- src/axiselements.jl | 147 +++++++++++++++++++----------- src/axislike.jl | 148 ++++++++++++++++--------------- test/test_build.jl | 34 +++---- test/test_elements.jl | 24 ++++- 10 files changed, 333 insertions(+), 194 deletions(-) create mode 100644 docs/src/examples/axislike.md diff --git a/docs/make.jl b/docs/make.jl index fddd835c..ba9d7c4a 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -19,6 +19,7 @@ makedocs( "Examples" => [ "examples/coordinates.md", "examples/tables.md", + "examples/axislike.md", "examples/gallery.md", "examples/juliatypes.md" ] diff --git a/docs/src/examples/axislike.md b/docs/src/examples/axislike.md new file mode 100644 index 00000000..fa2d8295 --- /dev/null +++ b/docs/src/examples/axislike.md @@ -0,0 +1,37 @@ +# Axis-like objects + +```@setup pgf +using PGFPlotsX +savefigs = (figname, obj) -> begin + PGFPlotsX.save(figname * ".pdf", obj) + run(`pdf2svg $(figname * ".pdf") $(figname * ".svg")`) + PGFPlotsX.save(figname * ".tex", obj); + return nothing +end +``` +------------------------ + +```@example pgf +x = linspace(0, 2*pi, 100) +@pgf GroupPlot( + { + group_style = + { + group_size="2 by 1", + xticklabels_at="edge bottom", + yticklabels_at="edge left" + }, + no_markers + }, + {}, + Plot(true, Table(x, sin.(x))), + Plot(true, Table(x, sin.(x .+ 0.5))), + {}, + Plot(true, Table(x, cos.(x))), + Plot(true, Table(x, cos.(x .+ 0.5)))) +savefigs("groupplot-multiple", ans) # hide +``` + +[\[.pdf\]](groupplot-multiple.pdf), [\[generated .tex\]](groupplot-multiple.tex) + +![](groupplot-multiple.svg) diff --git a/docs/src/examples/coordinates.md b/docs/src/examples/coordinates.md index 942003af..1a6b73c2 100644 --- a/docs/src/examples/coordinates.md +++ b/docs/src/examples/coordinates.md @@ -80,8 +80,7 @@ f(x, y) = (1 - x)^2 + 100*(y - x^2)^2 { surf, }, - Coordinates(x, y, f.(x, y')); - incremental = false + Coordinates(x, y, f.(x, y')) ) savefigs("coordinates-3d-matrix", ans) # hide ``` @@ -104,8 +103,7 @@ y = linspace(-0.5, 3, 50) surf, shader = "flat", }, - Coordinates(x, y, @. √(f(x, y'))); - incremental = false + Coordinates(x, y, @. √(f(x, y'))) ) ) savefigs("coordinates-3d-matrix-heatmap", ans) # hide diff --git a/docs/src/examples/gallery.md b/docs/src/examples/gallery.md index 7243c71c..1c8336da 100644 --- a/docs/src/examples/gallery.md +++ b/docs/src/examples/gallery.md @@ -77,8 +77,9 @@ savefigs("simple-expression", ans) # hide grid = "major", }, [ - Plot(Expression("-x^5 - 242"); label = "model") - Plot(Coordinates( + Plot(true, Expression("-x^5 - 242")), + raw"\addlegendentry{model}", + Plot(true, Coordinates( [ (-4.77778,2027.60977), (-3.55556,347.84069), @@ -89,8 +90,9 @@ savefigs("simple-expression", ans) # hide (2.55556,-341.40638), (3.77778,-1169.24780), (5.00000,-3269.56775), - ]; - ); label = "estimate") + ] + )), + raw"\addlegendentry{estimate}" ] ) savefigs("cost-gain", ans) # hide @@ -305,3 +307,37 @@ savefigs("mesh-scatter", ans) # hide ![](mesh-scatter.svg) +------------------------ + +```@example pgf +# this is an imitation of the figure in the manual, as we generate the data +x = linspace(0, 10, 100) +@pgf plot = Plot({very_thick}, Table(x = x, y = @. (sin(x * 8) + 1) * 4 * x)) +@pgf GroupPlot( + { + group_style = + { + group_size="2 by 2", + horizontal_sep="0pt", + vertical_sep="0pt", + xticklabels_at="edge bottom" + }, + xmin = 0, + ymin = 0, + height = "3.7cm", + width = "4cm", + no_markers + }, + nothing, + {xmin=5, xmax=10, ymin=50, ymax=100}, + plot, + {xmax=5, ymax=50}, + plot, + {xmin=5, xmax=10, ymax=50, yticklabels={}}, + plot) +savefigs("groupplot-nested", ans) # hide +``` + +[\[.pdf\]](groupplot-nested.pdf), [\[generated .tex\]](groupplot-nested.tex) + +![](groupplot-nested.svg) diff --git a/docs/src/examples/juliatypes.md b/docs/src/examples/juliatypes.md index 7987f1ac..408a4021 100644 --- a/docs/src/examples/juliatypes.md +++ b/docs/src/examples/juliatypes.md @@ -24,13 +24,14 @@ using Colors axis = Axis() @pgf for (i, col) in enumerate(distinguishable_colors(10)) offset = i * 50 - p = Plot(Expression("exp(-(x-$μ)^2 / (2 * $σ^2)) / ($σ * sqrt(2*pi)) + $offset"), - { - color = col, - domain = "-3*$σ:3*$σ", - style = { ultra_thick }, - samples = 50 - }; incremental = false) + p = Plot( + { + color = col, + domain = "-3*$σ:3*$σ", + style = { ultra_thick }, + samples = 50 + }, + Expression("exp(-(x-$μ)^2 / (2 * $σ^2)) / ($σ * sqrt(2*pi)) + $offset")) push!(axis, p) end axis @@ -45,15 +46,13 @@ Using a colormap ```@example pgf using Colors -@pgf begin -p = Plot3( - Expression("cos(deg(x)) * sin(deg(y))"), +p = @pgf Plot3( { surf, point_meta = "y", samples = 13 - }; - incremental = false + }, + Expression("cos(deg(x)) * sin(deg(y))") ) colormaps = ["Blues", "Greens", "Oranges", "Purples"] td = TikzDocument() @@ -63,13 +62,11 @@ end tp = TikzPicture("scale" => 0.5) push!(td, tp) -gp = GroupPlot({ group_style = {group_size = "2 by 2"}}) +gp = @pgf GroupPlot({ group_style = {group_size = "2 by 2"}}) push!(tp, gp) for cmap in colormaps - push!(gp, p, { colormap_name = cmap }) -end - + @pgf push!(gp, { colormap_name = cmap }, p) end savefigs("colormap", td) # hide ``` @@ -108,12 +105,13 @@ df = dataset("datasets", "iris") # load the dataset { x = "SepalLength", y = "SepalWidth", - meta = "Species" }, + meta = "Species" + }, df, # <--- Creating a Table from a DataFrame ) ), - Legend(["Setosa", "Versicolor", "Virginica"]) - ] + Legend(["Setosa", "Versicolor", "Virginica"]) + ] ) savefigs("dataframes", ans) # hide ``` @@ -133,11 +131,10 @@ x = 0.0:0.1:2π y = 0.0:0.1:2π f = (x,y) -> sin(x)*sin(y) @pgf Plot({ - contour_prepared, - very_thick - }, - Table(contours(x, y, f.(x, y'), 6)); - incremental = false) + contour_prepared, + very_thick + }, + Table(contours(x, y, f.(x, y'), 6))) savefigs("contour", ans) # hide ``` @@ -151,17 +148,18 @@ savefigs("contour", ans) # hide ```@example pgf using StatsBase: Histogram, fit -Axis(Plot(Table(fit(Histogram, randn(100), closed = :left)); - incremental = false), - @pgf { - "ybar interval", - "xticklabel interval boundaries", - xticklabel = raw"$[\pgfmathprintnumber\tick,\pgfmathprintnumber\nexttick)$", - "xticklabel style" = - { - font = raw"\tiny" - } - }) +@pgf Axis( + { + "ybar interval", + "xticklabel interval boundaries", + xmajorgrids = false, + xticklabel = raw"$[\pgfmathprintnumber\tick,\pgfmathprintnumber\nexttick)$", + "xticklabel style" = + { + font = raw"\tiny" + }, + }, + Plot(Table(fit(Histogram, linspace(0, 1, 100).^3, closed = :left)))) savefigs("histogram-1d", ans) # hide ``` @@ -171,7 +169,9 @@ savefigs("histogram-1d", ans) # hide ```@example pgf using StatsBase: Histogram, fit -h = fit(Histogram, (randn(1000), randn(1000)), closed = :left) +w = linspace(-1, 1, 100) .^ 3 +xy = vec(tuple.(w, w')) +h = fit(Histogram, (first.(xy), last.(xy)), closed = :left) @pgf Axis( { view = (0, 90), @@ -184,9 +184,7 @@ h = fit(Histogram, (randn(1000), randn(1000)), closed = :left) shader = "flat", }, - Table(h); - incremental = false - ) + Table(h)) ) savefigs("histogram-2d", ans) # hide ``` diff --git a/docs/src/examples/tables.md b/docs/src/examples/tables.md index 77b19787..a9049012 100644 --- a/docs/src/examples/tables.md +++ b/docs/src/examples/tables.md @@ -27,7 +27,7 @@ y = sin.(x) You can pass these coordinates in unnamed columns: ```@example pgf -Plot(Table([x, y]); incremental = false) +Plot(Table([x, y])) savefigs("table-unnamed-columns", ans) # hide ``` @@ -38,7 +38,7 @@ savefigs("table-unnamed-columns", ans) # hide or named columns: ```@example pgf -Plot(Table([:x => x, :y => y]); incremental = false) +Plot(Table([:x => x, :y => y])) savefigs("table-named-columns", ans) # hide ``` @@ -54,8 +54,7 @@ or rename using options: x = "a", y = "b", }, - Table([:a => x, :b => y]); - incremental = false) + Table([:a => x, :b => y])) savefigs("table-dict-rename", ans) # hide ``` @@ -79,8 +78,7 @@ z[z .≤ 0] .= -Inf surf, shader = "flat", }, - Table(x, x, z); - incremental = false + Table(x, x, z) ) ) savefigs("table-jump-3d", ans) # hide diff --git a/src/axiselements.jl b/src/axiselements.jl index 248bbfef..0dc4783b 100644 --- a/src/axiselements.jl +++ b/src/axiselements.jl @@ -1,55 +1,3 @@ -######## -# Plot # -######## - -struct Plot <: AxisElement - elements::AbstractVector{Any} - options::Options - label - incremental::Bool - _3d::Bool -end - -Base.push!(plot::Plot, element) = (push!(plot.elements, element); plot) -Base.append!(plot::Plot, element) = (append!(plot.elements, element); plot) - -function Plot(elements::AbstractVector, args::Vararg{PGFOption}; incremental = true, label = nothing) - Plot(elements, dictify(args), label, incremental, false) -end - -function Plot3(element::Vector, args::Vararg{PGFOption}; incremental = true, label = nothing) - Plot(element, dictify(args), label, incremental, true) -end - -Plot(options::Options, element; kwargs...) = Plot(element, options; kwargs...) -Plot(element, args...; kwargs...) = Plot([element], args...; kwargs...) -Plot3(element, args...; kwargs...) = Plot3([element], args...; kwargs...) -Plot3(options::Options, element; kwargs...) = Plot3(element, options; kwargs...) - -function save(filename::String, plot::Plot; kwargs...) - save(filename, Axis(plot); kwargs...) -end - -function print_tex(io_main::IO, p::Plot) - print_indent(io_main) do io - print(io, "\\addplot") - if p._3d - print(io, "3") - end - if p.incremental - print(io, "+") - end - print_options(io, p.options) - for element in p.elements - print_tex(io, element, p) - end - print(io, ";") - if p.label != nothing - print(io, "\n\\addlegendentry{$(p.label)}") - end - end -end - ############## # Expression # ############## @@ -534,6 +482,101 @@ function print_tex(io_main::IO, t::Graphics) end end +######## +# Plot # +######## + +""" +Default setting for the `incremental` flag of `Plot` and `Plot3`. +""" +const INCREMENTAL = false + +"Types accepted by `Plot` for the field `data`." +const PlotData = Union{Coordinates, Table, TableFile, Expression, Graphics} + +""" +$(TYPEDEF) + +Corresponds to the `\\addplot[3][+]` family of `pgfplot` commands. + +The 1-4+ argument outer constructors `Plot` and `Plot3` should be used in user +code. +""" +struct Plot <: OptionType + is3d::Bool + incremental::Bool + options::Options + data::PlotData + trailing::AbstractVector{Any} # FIXME can/should we be more specific? +end + +Plot(is3d::Bool, incremental::Bool, options::Options, data::PlotData, + trailing::Tuple) = Plot(is3d, incremental, options, data, collect(trailing)) + +Base.push!(plot::Plot, element) = (push!(plot.trailing, element); plot) +Base.append!(plot::Plot, element) = (append!(plot.trailing, element); plot) + +""" + Plot([incremental::Bool], [options::Options], data, trailing...) + +A plot with the given `data` (eg [`Coordinates`](@ref), [`Table`](@ref), +[`Expression`](@ref), …) and `options`, which is empty by default. + +When `incremental` (defaults to $(INCREMENTAL)), use the `\\addplot+` form, which +takes styles from the `cycle list` of `pgfplots`, otherwise `\\addplot`. + +`trailing` can be used to provide *trailing path commands* (eg `\\closedcycle`, +see the `pgfplots` manual), which are emitted using `print_tex`, before the +terminating `;`. +""" +Plot(incremental::Bool, options::Options, data::PlotData, trailing...) = + Plot(false, incremental, options, data, trailing) + +Plot(options::Options, data::PlotData, trailing...) = + Plot(false, INCREMENTAL, options, data, trailing) + +Plot(incremental::Bool, data::PlotData, trailing...) = + Plot(false, incremental, Options(), data, trailing) + +Plot(data::PlotData, trailing...) = + Plot(false, INCREMENTAL, Options(), data, trailing) + +""" + Plot3([incremental::Bool], [options::Options], data, trailing...) + +Same as [`Plot(::Bool, ::Options, ::PlotData, ...)`](@ref), but for 3D plots. +""" +Plot3(incremental::Bool, options::Options, data::PlotData, trailing...) = + Plot(true, incremental, options, data, trailing) + +Plot3(options::Options, data::PlotData, trailing...) = + Plot(true, INCREMENTAL, options, data, trailing) + +Plot3(incremental::Bool, data::PlotData, trailing...) = + Plot(true, incremental, Options(), data, trailing) + +Plot3(data::PlotData, trailing...) = + Plot(true, INCREMENTAL, Options(), data, trailing) + +function save(filename::String, plot::Plot; kwargs...) + save(filename, Axis(plot); kwargs...) +end + +function print_tex(io_main::IO, plot::Plot) + print_indent(io_main) do io + @unpack is3d, incremental, options, data, trailing = plot + print(io, "\\addplot") + is3d && print(io, "3") + incremental && print(io, "+") + print_options(io, options) + print_tex(io, data) + for t in trailing + print_tex(io, t) + end + print(io, ";") + end +end + struct Legend labels::Vector{String} end diff --git a/src/axislike.jl b/src/axislike.jl index de236530..4463f229 100644 --- a/src/axislike.jl +++ b/src/axislike.jl @@ -1,32 +1,41 @@ -abstract type AxisLike <: TikzElement end +""" +$(TYPEDEF) -Base.push!(axislike::AxisLike, plot) = (push!(axislike.plots, plot); axislike) -Base.append!(axislike::AxisLike, plot) = (append!(axislike.plots, plot); axislike) +An axis-like object that has `options` and `contents`. Each subtype `T` has the +constructor +```julia T([options], contents...) ``` -function (T::Type{<:AxisLike})(plots::AbstractVector, args::Vararg{PGFOption}) - T(plots, dictify(args)) -end +and supports [`axislike_environment(T)`](@ref). + +`contents` usually consists of `Plot` objects, but can also contain strings, +which are printed *as is* (use these for legends etc). Some subtypes have +special semantics, see their documentation. +""" +abstract type AxisLike <: TikzElement end -(T::Type{<:AxisLike})(plot, args::Vararg{PGFOption}) = T([plot], dictify(args)) -(T::Type{<:AxisLike})(options::Options, element) = T(element, options) +""" + axislike_environment(::Type{<: AxisLike}) -function (T::Type{<:AxisLike})(args::Vararg{PGFOption}) - T([], args...) -end +Return the corresponding LaTeX environment name. +""" +function axislike_environment end + +Base.push!(axislike::AxisLike, elt) = (push!(axislike.contents, elt); axislike) +Base.append!(axislike::AxisLike, elt) = (append!(axislike.contents, elt); axislike) -function print_tex(io_main::IO, axislike::AxisLike) +(T::Type{<:AxisLike})(contents...) = T(Options(), contents...) + +function print_tex(io_main::IO, axislike::T) where {T <: AxisLike} + @unpack options, contents = axislike + name = axislike_environment(T) print_indent(io_main) do io - print(io, "\\begin{", _tex_name(axislike), "}") - print_options(io, axislike.options) - for (i, plot) in enumerate(axislike.plots) - between = _in_between(axislike, i) - if !isempty(between) - print_tex(io, between) - end - print_tex(io, plot, axislike) + print(io, "\\begin{", name, "}") + print_options(io, options) + for elt in contents + print_tex(io, elt, axislike) end - print(io, "\\end{", _tex_name(axislike), "}") + print(io, "\\end{", name, "}") end end @@ -34,64 +43,63 @@ function save(filename::String, axislike::AxisLike; kwargs...) save(filename, TikzPicture(axislike); kwargs...) end -_in_between(::Any, ::Any) = "" +macro define_axislike(name, latex_environment) + @argcheck latex_environment isa String + _name = esc(name) + quote + Base.@__doc__ struct $(_name) <: AxisLike + options::Options + contents::Vector{Any} + function $(_name)(options::Options, contents...) + new(options, collect(contents)) + end + end + ($(esc(:axislike_environment)))(::Type{$(_name)}) = $(latex_environment) + end +end -######## -# Axis # -######## +@define_axislike Axis "axis" -struct Axis <: AxisLike - plots::Vector{Any} - options::Options +@define_axislike SemiLogXAxis "semilogxaxis" - # get rid of default constructor or ambiguities - Axis(v::Vector, o::Options) = new(v, o) -end +@define_axislike SemiLogYAxis "semilogyaxis" -_tex_name(::Axis) = "axis" +@define_axislike LogLogAxis "loglogaxis" -############# -# GroupPlot # -############# +@define_axislike PolarAxis "polaraxis" -struct GroupPlot <: AxisLike - plots::Vector{Any} - axisoptions::Vector{Options} - options::Options - # nextgroupplot::Vector{Options} # options for \nextgroupplot - # get rid of default constructor or ambiguities - GroupPlot(v::Vector, o::Options) = new(convert(Vector{Any}, v), [Options() for i in 1:length(v)], o) - GroupPlot(o::Options) = new(Any[], Options[], o) -end +""" + GroupPlot([options], contents...) -function print_tex(io::IO, v::Vector, gp::GroupPlot) - for p in v - print_tex(io, p, gp) - end -end +A group plot, using the `groupplots` library of `pgfplots`. -Base.push!(gp::GroupPlot, plot) = (push!(gp.plots, plot); push!(gp.axisoptions, Options()); gp) -Base.push!(gp::GroupPlot, plot, args...) = (push!(gp.plots, plot); push!(gp.axisoptions, dictify(args)); gp) +The `contents` after the global options are processed as follows: -_tex_name(::GroupPlot) = "groupplot" -#TODO Should these take IO instead? -function _in_between(gp::GroupPlot, i::Int) - io = IOBuffer() - print(io, "\\nextgroupplot") - print_options(io, gp.axisoptions[i]) - return String(take!(io)) -end +1. [`Options`](@ref) will emit a ``\\nextgroupplot` with the given options, -############# -# PolarAxis # -############# +2. `nothing` is emitted as a `\\nextgroupplot[group/empty plot]`, -struct PolarAxis <: AxisLike - plots::Vector{Any} - options::Options - # nextgroupplot::Vector{Options} # options for \nextgroupplot - # get rid of default constructor or ambiguities - PolarAxis(v::Vector, o::Options) = new(convert(Vector{Any}, v), o) -end +3. other values, eg `Plot` are emitted using [`print_tex`](@ref). +""" +@define_axislike GroupPlot "groupplot" -_tex_name(::PolarAxis) = "polaraxis" +function print_tex(io_main::IO, groupplot::GroupPlot) + @unpack options, contents = groupplot + print_indent(io_main) do io + print(io, "\\begin{groupplot}") + print_options(io, options) + for elt in contents + if elt isa Options + print(io, "\\nextgroupplot") + print_options(io, elt) + elseif elt isa Plot + print_tex(io, elt) + elseif elt isa Void + print(io, raw"\nextgroupplot[group/empty plot]") + else + print_tex(io, elt) + end + end + print(io, "\\end{groupplot}") + end +end diff --git a/test/test_build.jl b/test/test_build.jl index 077c76a8..eb02f5b2 100644 --- a/test/test_build.jl +++ b/test/test_build.jl @@ -83,22 +83,24 @@ end mktempdir() do dir cd(dir) do @pgf p = - Axis(Plot3(Expression(expr), - { - contour_gnuplot = { - number = 30, - labels = false}, - thick, - samples = 40, - }; incremental = false), - { - colorbar, - xlabel = "x", - ylabel = "y", - domain = 1:2, - y_domain = "74:87.9", - view = (0, 90), - }) + Axis( + { + colorbar, + xlabel = "x", + ylabel = "y", + domain = 1:2, + y_domain = "74:87.9", + view = (0, 90), + }, + Plot3( + { + contour_gnuplot = { + number = 30, + labels = false}, + thick, + samples = 40, + }, + Expression(expr))) PGFPlotsX.save(tmp_pdf, p) @test is_pdf_file(tmp_pdf) rm(tmp_pdf) diff --git a/test/test_elements.jl b/test/test_elements.jl index 4d10bbc8..9a6499b9 100644 --- a/test/test_elements.jl +++ b/test/test_elements.jl @@ -26,9 +26,9 @@ squashed_repr_tex(args...) = squash_whitespace(repr_tex(args...)) "A simple comparison of fields for unit tests." ≅(x, y) = x == y -function ≅(x::T, y::T) where T +function ≅(x::T, y::T) where T <: Union{PGFPlotsX.Coordinate, Coordinates, Table, Plot} for f in fieldnames(T) - getfield(x, f) == getfield(y, f) || return false + getfield(x, f) ≅ getfield(y, f) || return false end true end @@ -133,6 +133,24 @@ end path = "somefile.dat" _abspath = abspath(path) @test squashed_repr_tex(Table(@pgf({x = "a", y = "b"}), path)) == - "table [x={a}, y={b}]\n{$(_abspath)}" + "table [x={a}, y={b}]\n{$(_abspath)}" @test squashed_repr_tex(Table("somefile.dat")) == "table []\n{$(_abspath)}" end + +@testset "plot" begin + # sanity checks for constructors and printing, 2D + data2 = Table(x = 1:2, y = 3:4) + p2 = Plot(false, PGFPlotsX.INCREMENTAL, PGFPlotsX.Options(), data2, + [raw"\closedcycle"]) + @test squashed_repr_tex(p2) == + "\\addplot[]\ntable []\n{x y\n1 3\n2 4\n}\n\\closedcycle\n;" + @test Plot(PGFPlotsX.INCREMENTAL, @pgf({}), data2, raw"\closedcycle") ≅ p2 + @test Plot(@pgf({}), data2, raw"\closedcycle") ≅ p2 + @test Plot(PGFPlotsX.INCREMENTAL, data2, raw"\closedcycle") ≅ p2 + @test Plot(data2, raw"\closedcycle") ≅ p2 + # printing incremental w/ options, 2D and 3D + @test squashed_repr_tex(Plot(true, data2)) == + "\\addplot+[]\ntable []\n{x y\n1 3\n2 4\n}\n;" + @test squashed_repr_tex(Plot3(true, Table(x = 1:2, y = 3:4, z = 5:6))) == + "\\addplot3+[]\ntable []\n{x y z\n1 3 5\n2 4 6\n}\n;" +end From bd4f86cad7d1d9a1de47331faf8ad66f42dfc2f7 Mon Sep 17 00:00:00 2001 From: "Tamas K. Papp" Date: Tue, 20 Feb 2018 14:52:35 +0100 Subject: [PATCH 2/3] Separate incremental plot constructors, add LegendEntry. The `incremental` flag was removed from `Plot*` constructors in the API, replaced by `PlotInc` and `Plot3Inc`. A LegendEntry type was added. Minor fixes: - remove the INCREMENTAL constant, as it is now unnecessary - clarify docstrings - disabled docstring checks, as they lead to infinite loops for some reason --- docs/make.jl | 1 + docs/src/examples/axislike.md | 8 +-- docs/src/examples/gallery.md | 8 +-- src/PGFPlotsX.jl | 4 +- src/axiselements.jl | 97 +++++++++++++++++++++++++---------- test/test_elements.jl | 13 ++--- 6 files changed, 88 insertions(+), 43 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index ba9d7c4a..3a276303 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -8,6 +8,7 @@ makedocs( sitename = "PGFPlotsX.jl", doctest = false, strict = false, + checkdocs = :none, pages = Any[ "Home" => "index.md", "Manual" => [ diff --git a/docs/src/examples/axislike.md b/docs/src/examples/axislike.md index fa2d8295..8f677369 100644 --- a/docs/src/examples/axislike.md +++ b/docs/src/examples/axislike.md @@ -24,11 +24,11 @@ x = linspace(0, 2*pi, 100) no_markers }, {}, - Plot(true, Table(x, sin.(x))), - Plot(true, Table(x, sin.(x .+ 0.5))), + PlotInc(Table(x, sin.(x))), + PlotInc(Table(x, sin.(x .+ 0.5))), {}, - Plot(true, Table(x, cos.(x))), - Plot(true, Table(x, cos.(x .+ 0.5)))) + PlotInc(Table(x, cos.(x))), + PlotInc(Table(x, cos.(x .+ 0.5)))) savefigs("groupplot-multiple", ans) # hide ``` diff --git a/docs/src/examples/gallery.md b/docs/src/examples/gallery.md index 1c8336da..a5f0c40d 100644 --- a/docs/src/examples/gallery.md +++ b/docs/src/examples/gallery.md @@ -77,9 +77,9 @@ savefigs("simple-expression", ans) # hide grid = "major", }, [ - Plot(true, Expression("-x^5 - 242")), - raw"\addlegendentry{model}", - Plot(true, Coordinates( + PlotInc(Expression("-x^5 - 242")), + LegendEntry("model"), + PlotInc(Coordinates( [ (-4.77778,2027.60977), (-3.55556,347.84069), @@ -92,7 +92,7 @@ savefigs("simple-expression", ans) # hide (5.00000,-3269.56775), ] )), - raw"\addlegendentry{estimate}" + LegendEntry("estimate") ] ) savefigs("cost-gain", ans) # hide diff --git a/src/PGFPlotsX.jl b/src/PGFPlotsX.jl index 0a745ff4..836c43af 100644 --- a/src/PGFPlotsX.jl +++ b/src/PGFPlotsX.jl @@ -14,8 +14,8 @@ using Requires export TikzDocument, TikzPicture export Axis, GroupPlot, PolarAxis -export Plot, Plot3, Expression, EmptyLine, Coordinates, - Table, Graphics, Legend +export Plot, PlotInc, Plot3, Plot3Inc, Expression, EmptyLine, Coordinates, + Table, Graphics, Legend, LegendEntry export @pgf, print_tex, latexengine, latexengine!, CUSTOM_PREAMBLE, push_preamble! const DEBUG = haskey(ENV, "PGFPLOTSX_DEBUG") diff --git a/src/axiselements.jl b/src/axiselements.jl index 0dc4783b..f920404b 100644 --- a/src/axiselements.jl +++ b/src/axiselements.jl @@ -486,11 +486,6 @@ end # Plot # ######## -""" -Default setting for the `incremental` flag of `Plot` and `Plot3`. -""" -const INCREMENTAL = false - "Types accepted by `Plot` for the field `data`." const PlotData = Union{Coordinates, Table, TableFile, Expression, Graphics} @@ -499,8 +494,8 @@ $(TYPEDEF) Corresponds to the `\\addplot[3][+]` family of `pgfplot` commands. -The 1-4+ argument outer constructors `Plot` and `Plot3` should be used in user -code. +Instead of the default constructor, use `Plot([options], data, trailing...)` and +similar (`PlotInc`, `Plot3`, `Plot3Inc`) in user code. """ struct Plot <: OptionType is3d::Bool @@ -517,46 +512,61 @@ Base.push!(plot::Plot, element) = (push!(plot.trailing, element); plot) Base.append!(plot::Plot, element) = (append!(plot.trailing, element); plot) """ - Plot([incremental::Bool], [options::Options], data, trailing...) + Plot([options::Options], data, trailing...) A plot with the given `data` (eg [`Coordinates`](@ref), [`Table`](@ref), [`Expression`](@ref), …) and `options`, which is empty by default. -When `incremental` (defaults to $(INCREMENTAL)), use the `\\addplot+` form, which -takes styles from the `cycle list` of `pgfplots`, otherwise `\\addplot`. +Corresponds to `\\addplot` in `pgfplots`. `trailing` can be used to provide *trailing path commands* (eg `\\closedcycle`, see the `pgfplots` manual), which are emitted using `print_tex`, before the terminating `;`. """ -Plot(incremental::Bool, options::Options, data::PlotData, trailing...) = - Plot(false, incremental, options, data, trailing) - Plot(options::Options, data::PlotData, trailing...) = - Plot(false, INCREMENTAL, options, data, trailing) - -Plot(incremental::Bool, data::PlotData, trailing...) = - Plot(false, incremental, Options(), data, trailing) + Plot(false, false, options, data, trailing) Plot(data::PlotData, trailing...) = - Plot(false, INCREMENTAL, Options(), data, trailing) + Plot(false, false, Options(), data, trailing) """ - Plot3([incremental::Bool], [options::Options], data, trailing...) + PlotInc([options::Options], data, trailing...) -Same as [`Plot(::Bool, ::Options, ::PlotData, ...)`](@ref), but for 3D plots. +Corresponds to the `\\addplot+` form in `pgfplots`. + +For the interpretation of the other arguments, see `Plot(::Options, ::PlotData, ...)`. """ -Plot3(incremental::Bool, options::Options, data::PlotData, trailing...) = - Plot(true, incremental, options, data, trailing) +PlotInc(options::Options, data::PlotData, trailing...) = + Plot(false, true, options, data, trailing) -Plot3(options::Options, data::PlotData, trailing...) = - Plot(true, INCREMENTAL, options, data, trailing) +PlotInc(data::PlotData, trailing...) = + Plot(false, true, Options(), data, trailing) + +""" + Plot3([options::Options], data, trailing...) -Plot3(incremental::Bool, data::PlotData, trailing...) = - Plot(true, incremental, Options(), data, trailing) +Corresponds to the `\\addplot3` form in `pgfplots`. + +For the interpretation of the other arguments, see `Plot(::Options, ::PlotData, ...)`. +""" +Plot3(options::Options, data::PlotData, trailing...) = + Plot(true, false, options, data, trailing) Plot3(data::PlotData, trailing...) = - Plot(true, INCREMENTAL, Options(), data, trailing) + Plot(true, false, Options(), data, trailing) + +""" + Plot3([options::Options], data, trailing...) + +Corresponds to the `\\addplot3+` form in `pgfplots`. + +For the interpretation of the other arguments, see `Plot(::Options, ::PlotData, ...)`. +""" +Plot3Inc(options::Options, data::PlotData, trailing...) = + Plot(true, true, options, data, trailing) + +Plot3Inc(data::PlotData, trailing...) = + Plot(true, true, Options(), data, trailing) function save(filename::String, plot::Plot; kwargs...) save(filename, Axis(plot); kwargs...) @@ -582,3 +592,36 @@ struct Legend end print_tex(io_main::IO, l::Legend) = print(io_main, "\\legend{", join(l.labels, ", "), "}") + +############### +# LegendEntry # +############### + +struct LegendEntry + options::Options + name::AbstractString + isexpanded::Bool +end + +""" + LegendEntry([options::Options], name, [isexpanded]) + +Corresponds to the `\\addlegendentry` and `\\addlegendentryexpanded` forms of +`pgfplots`. +""" +LegendEntry(options::Options, name::AbstractString, isexpanded = false) + +LegendEntry(name::AbstractString, isexpanded = false) = + LegendEntry(Options(), name, isexpanded) + +function print_tex(io_main::IO, legendentry::LegendEntry) + print_indent(io_main) do io + @unpack options, name, isexpanded = legendentry + print(io, "\\addlegendentry") + isexpanded && print(io, "expanded") + print_options(io, options) + print(io, "{") + print(io, name) + print(io, "}") + end +end diff --git a/test/test_elements.jl b/test/test_elements.jl index 9a6499b9..8543a029 100644 --- a/test/test_elements.jl +++ b/test/test_elements.jl @@ -140,17 +140,18 @@ end @testset "plot" begin # sanity checks for constructors and printing, 2D data2 = Table(x = 1:2, y = 3:4) - p2 = Plot(false, PGFPlotsX.INCREMENTAL, PGFPlotsX.Options(), data2, - [raw"\closedcycle"]) + p2 = Plot(false, false, PGFPlotsX.Options(), data2, [raw"\closedcycle"]) @test squashed_repr_tex(p2) == "\\addplot[]\ntable []\n{x y\n1 3\n2 4\n}\n\\closedcycle\n;" - @test Plot(PGFPlotsX.INCREMENTAL, @pgf({}), data2, raw"\closedcycle") ≅ p2 @test Plot(@pgf({}), data2, raw"\closedcycle") ≅ p2 - @test Plot(PGFPlotsX.INCREMENTAL, data2, raw"\closedcycle") ≅ p2 + @test PlotInc(@pgf({}), data2, raw"\closedcycle") ≅ + Plot(false, true, PGFPlotsX.Options(), data2, [raw"\closedcycle"]) + @test PlotInc(data2, raw"\closedcycle") ≅ + Plot(false, true, PGFPlotsX.Options(), data2, [raw"\closedcycle"]) @test Plot(data2, raw"\closedcycle") ≅ p2 # printing incremental w/ options, 2D and 3D - @test squashed_repr_tex(Plot(true, data2)) == + @test squashed_repr_tex(PlotInc(data2)) == "\\addplot+[]\ntable []\n{x y\n1 3\n2 4\n}\n;" - @test squashed_repr_tex(Plot3(true, Table(x = 1:2, y = 3:4, z = 5:6))) == + @test squashed_repr_tex(Plot3Inc(Table(x = 1:2, y = 3:4, z = 5:6))) == "\\addplot3+[]\ntable []\n{x y z\n1 3 5\n2 4 6\n}\n;" end From e52ae2a8f756df4a08656613f22770c26aa25394 Mon Sep 17 00:00:00 2001 From: "Tamas K. Papp" Date: Wed, 21 Feb 2018 12:56:43 +0100 Subject: [PATCH 3/3] Trivial docstring fix. --- src/axiselements.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axiselements.jl b/src/axiselements.jl index f920404b..23f545d2 100644 --- a/src/axiselements.jl +++ b/src/axiselements.jl @@ -556,7 +556,7 @@ Plot3(data::PlotData, trailing...) = Plot(true, false, Options(), data, trailing) """ - Plot3([options::Options], data, trailing...) + Plot3Inc([options::Options], data, trailing...) Corresponds to the `\\addplot3+` form in `pgfplots`.