Merge pull request #2310 from BeastyBlacksmith/pgfplotsx

fix ribbons and interaction between background and fill between
This commit is contained in:
Simon Christ 2019-12-17 15:23:26 +01:00 committed by GitHub
commit b73c38fe69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 96 additions and 46 deletions

View File

@ -1,4 +1,5 @@
using Contour: Contour using Contour: Contour
using UUIDs
Base.@kwdef mutable struct PGFPlotsXPlot Base.@kwdef mutable struct PGFPlotsXPlot
is_created::Bool = false is_created::Bool = false
was_shown::Bool = false was_shown::Bool = false
@ -8,25 +9,21 @@ Base.@kwdef mutable struct PGFPlotsXPlot
# tikz libraries # tikz libraries
PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usetikzlibrary{arrows.meta}") PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usetikzlibrary{arrows.meta}")
PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usetikzlibrary{backgrounds}") PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usetikzlibrary{backgrounds}")
PGFPlotsX.push_preamble!(pgfx_plot.the_plot,
"""
\\pgfkeys{/tikz/.cd,
background color/.initial=white,
background color/.get=\\backcol,
background color/.store in=\\backcol,
}
\\tikzset{background rectangle/.style={
fill=\\backcol,
},
use background/.style={
show background rectangle
}
}
"""
)
# pgfplots libraries # pgfplots libraries
PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usepgfplotslibrary{patchplots}") PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usepgfplotslibrary{patchplots}")
PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usepgfplotslibrary{fillbetween}") PGFPlotsX.push_preamble!(pgfx_plot.the_plot, "\\usepgfplotslibrary{fillbetween}")
# compatibility fixes
# add background layer to standard layers
PGFPlotsX.push_preamble!(pgfx_plot.the_plot,
raw"""
\pgfplotsset{
/pgfplots/layers/axis on top/.define layer set={
background, axis background,pre main,main,axis grid,axis ticks,axis lines,axis tick labels,
axis descriptions,axis foreground
}{/pgfplots/layers/standard},
}
"""
)
pgfx_plot pgfx_plot
end end
end end
@ -73,9 +70,11 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
cstr = plot_color(bgc) cstr = plot_color(bgc)
a = alpha(cstr) a = alpha(cstr)
push!(the_plot.options, push!(the_plot.options,
"draw opacity" => a, "/tikz/background rectangle/.style" => PGFPlotsX.Options(
"background color" => cstr, "fill" => cstr,
"use background" => nothing, "draw opacity" => a,
),
"show background rectangle" => nothing,
) )
end end
@ -89,22 +88,22 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
"horizontal sep" => string(maximum(sp -> sp.minpad[1], plt.subplots)), "horizontal sep" => string(maximum(sp -> sp.minpad[1], plt.subplots)),
"vertical sep" => string(maximum(sp -> sp.minpad[2], plt.subplots)), "vertical sep" => string(maximum(sp -> sp.minpad[2], plt.subplots)),
), ),
"height" => pl_height > 0 ? string(pl_height)*"px" : "{}", "height" => pl_height > 0 ? string(pl_height * px) : "{}",
"width" => pl_width > 0 ? string(pl_width)*"px" : "{}", "width" => pl_width > 0 ? string(pl_width * px) : "{}",
) )
) )
) )
end end
for sp in plt.subplots for sp in plt.subplots
bb = bbox(sp) bb = bbox(sp)
sp_width = width(bb)
sp_height = height(bb)
cstr = plot_color(sp[:background_color_legend]) cstr = plot_color(sp[:background_color_legend])
a = alpha(cstr) a = alpha(cstr)
fg_alpha = alpha(plot_color(sp[:foreground_color_legend])) fg_alpha = alpha(plot_color(sp[:foreground_color_legend]))
title_cstr = plot_color(sp[:titlefontcolor]) title_cstr = plot_color(sp[:titlefontcolor])
title_a = alpha(title_cstr) title_a = alpha(title_cstr)
axis_opt = PGFPlotsX.Options( axis_opt = PGFPlotsX.Options(
"height" => string(height(bb)),
"width" => string(width(bb)),
"title" => sp[:title], "title" => sp[:title],
"title style" => PGFPlotsX.Options( "title style" => PGFPlotsX.Options(
"font" => pgfx_font(sp[:titlefontsize], pgfx_thickness_scaling(sp)), "font" => pgfx_font(sp[:titlefontsize], pgfx_thickness_scaling(sp)),
@ -121,8 +120,11 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
), ),
"axis background/.style" => PGFPlotsX.Options( "axis background/.style" => PGFPlotsX.Options(
"fill" => sp[:background_color_inside] "fill" => sp[:background_color_inside]
) ),
"axis on top" => nothing,
) )
sp_width > 0*mm ? push!(axis_opt, "width" => string(sp_width)) : nothing
sp_height > 0*mm ? push!(axis_opt, "height" => string(sp_height)) : nothing
# legend position # legend position
if sp[:legend] isa Tuple if sp[:legend] isa Tuple
x, y = sp[:legend] x, y = sp[:legend]
@ -233,24 +235,18 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
segment_opt = merge( segment_opt, pgfx_marker(opt, i) ) segment_opt = merge( segment_opt, pgfx_marker(opt, i) )
end end
if st == :shape || if st == :shape ||
isfilledcontour(series) || isfilledcontour(series)
series[:ribbon] !== nothing
segment_opt = merge( segment_opt, pgfx_fillstyle(opt, i) ) segment_opt = merge( segment_opt, pgfx_fillstyle(opt, i) )
end end
# add fillrange # add fillrange
if series[:fillrange] !== nothing && !isfilledcontour(series) && series[:ribbon] === nothing if series[:fillrange] !== nothing && !isfilledcontour(series) && series[:ribbon] === nothing
pgfx_fillrange_series!( axis, series, series_func, i, _cycle(series[:fillrange], rng), rng) pgfx_fillrange_series!( axis, series, series_func, i, _cycle(series[:fillrange], rng), rng)
# add to legend?
if i == 1 && opt[:label] != "" && sp[:legend] != :none && should_add_to_legend(series) if i == 1 && opt[:label] != "" && sp[:legend] != :none && should_add_to_legend(series)
io = IOBuffer() pgfx_filllegend!(series_opt, opt)
PGFPlotsX.print_tex(io, pgfx_fillstyle(opt, i))
style = strip(String(take!(io)),['[',']', ' '])
push!( segment_opt, "legend image code/.code" => """{
\\draw[$style] (0cm,-0.1cm) rectangle (0.6cm,0.1cm);
}""" )
end end
end end
# series # series
#
coordinates = pgfx_series_coordinates!( sp, series, segment_opt, opt, rng ) coordinates = pgfx_series_coordinates!( sp, series, segment_opt, opt, rng )
segment_plot = series_func( segment_plot = series_func(
merge(series_opt, segment_opt), merge(series_opt, segment_opt),
@ -264,7 +260,11 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
end end
# add to legend? # add to legend?
if i == 1 && opt[:label] != "" && sp[:legend] != :none && should_add_to_legend(series) if i == 1 && opt[:label] != "" && sp[:legend] != :none && should_add_to_legend(series)
legend = PGFPlotsX.LegendEntry(PGFPlotsX.Options(), opt[:label], true) 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 ) push!( axis, legend )
end end
# add series annotations # add series annotations
@ -499,6 +499,15 @@ function pgfx_arrow( arr::Arrow )
return "every arrow/.append style={$(components)}" return "every arrow/.append style={$(components)}"
end end
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);
}""" )
end
function pgfx_colormap(grad::ColorGradient) function pgfx_colormap(grad::ColorGradient)
join(map(grad.colors) do c join(map(grad.colors) do c
@sprintf("rgb=(%.8f,%.8f,%.8f)", red(c), green(c), blue(c)) @sprintf("rgb=(%.8f,%.8f,%.8f)", red(c), green(c), blue(c))
@ -607,30 +616,41 @@ function pgfx_add_ribbons!( axis, series, segment_plot, series_func, series_inde
ribbon_y = series[:ribbon] ribbon_y = series[:ribbon]
opt = series.plotattributes opt = series.plotattributes
if ribbon_y isa AVec if ribbon_y isa AVec
ribbon_n = length(opt[:y]) ÷ length(ribbon) ribbon_n = length(opt[:y]) ÷ length(ribbon_y)
ribbon_y = repeat(ribbon, outer = ribbon_n) ribbon_yp = ribbon_ym = repeat(ribbon_y, outer = ribbon_n)
elseif ribbon_y isa Tuple
ribbon_ym, ribbon_yp = ribbon_y
ribbon_nm = length(opt[:y]) ÷ length(ribbon_ym)
ribbon_ym = repeat(ribbon_ym, outer = ribbon_nm)
ribbon_np = length(opt[:y]) ÷ length(ribbon_yp)
ribbon_yp = repeat(ribbon_yp, outer = ribbon_np)
else
ribbon_yp = ribbon_ym = ribbon_y
end end
# upper ribbon # upper ribbon
ribbon_name_plus = "plots_rib_p$series_index" rib_uuid = uuid4()
ribbon_name_plus = "plots_rib_p$rib_uuid"
ribbon_opt_plus = merge(segment_plot.options, PGFPlotsX.Options( ribbon_opt_plus = merge(segment_plot.options, PGFPlotsX.Options(
"name path" => ribbon_name_plus, "name path" => ribbon_name_plus,
"color" => opt[:fillcolor], "color" => opt[:fillcolor],
"draw opacity" => opt[:fillalpha] "draw opacity" => opt[:fillalpha],
"forget plot" => nothing
)) ))
coordinates_plus = PGFPlotsX.Coordinates(opt[:x], opt[:y] .+ ribbon_y) coordinates_plus = PGFPlotsX.Coordinates(opt[:x], opt[:y] .+ ribbon_yp)
ribbon_plot_plus = series_func( ribbon_plot_plus = series_func(
ribbon_opt_plus, ribbon_opt_plus,
coordinates_plus coordinates_plus
) )
push!(axis, ribbon_plot_plus) push!(axis, ribbon_plot_plus)
# lower ribbon # lower ribbon
ribbon_name_minus = "plots_rib_m$series_index" ribbon_name_minus = "plots_rib_m$rib_uuid"
ribbon_opt_minus = merge(segment_plot.options, PGFPlotsX.Options( ribbon_opt_minus = merge(segment_plot.options, PGFPlotsX.Options(
"name path" => ribbon_name_minus, "name path" => ribbon_name_minus,
"color" => opt[:fillcolor], "color" => opt[:fillcolor],
"draw opacity" => opt[:fillalpha] "draw opacity" => opt[:fillalpha],
"forget plot" => nothing
)) ))
coordinates_plus = PGFPlotsX.Coordinates(opt[:x], opt[:y] .- ribbon_y) coordinates_plus = PGFPlotsX.Coordinates(opt[:x], opt[:y] .- ribbon_ym)
ribbon_plot_plus = series_func( ribbon_plot_plus = series_func(
ribbon_opt_minus, ribbon_opt_minus,
coordinates_plus coordinates_plus
@ -638,7 +658,7 @@ function pgfx_add_ribbons!( axis, series, segment_plot, series_func, series_inde
push!(axis, ribbon_plot_plus) push!(axis, ribbon_plot_plus)
# fill # fill
push!(axis, series_func( push!(axis, series_func(
pgfx_fillstyle(opt, series_index), merge(pgfx_fillstyle(opt, series_index), PGFPlotsX.Options("forget plot" => nothing)),
"fill between [of=$(ribbon_name_plus) and $(ribbon_name_minus)]" "fill between [of=$(ribbon_name_plus) and $(ribbon_name_minus)]"
)) ))
return axis return axis

View File

@ -16,6 +16,9 @@ end
Plots._update_plot_object(pgfx_plot) Plots._update_plot_object(pgfx_plot)
@test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument @test pgfx_plot.o.the_plot isa PGFPlotsX.TikzDocument
@test pgfx_plot.series_list[1].plotattributes[:quiver] === nothing @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 @testset "3D docs example" begin
n = 100 n = 100
@ -124,13 +127,40 @@ end
u = ones(length(x)) u = ones(length(x))
v = cos.(x) v = cos.(x)
plot( x, y, quiver = (u, v), arrow = true ) arrow_plot = plot( x, y, quiver = (u, v), arrow = true )
# TODO: could adjust limits to fit arrows if too long, but how? # 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 end # testset
@testset "Annotations" begin @testset "Annotations" begin
y = rand(10) y = rand(10)
plot(y, annotations=(3, y[3], Plots.text("this is \\#3", :left)), leg=false) 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"))]) annotate!([(5, y[5], Plots.text("this is \\#5", 16, :red, :center)), (10, y[10], Plots.text("this is \\#10", :right, 20, "courier"))])
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)]) 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 end # testset
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