Merge pull request #2310 from BeastyBlacksmith/pgfplotsx
fix ribbons and interaction between background and fill between
This commit is contained in:
commit
b73c38fe69
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user