Merge pull request #2438 from BeastyBlacksmith/pgfplotsx

Fix twinx()
This commit is contained in:
Simon Christ 2020-03-06 12:41:44 +01:00 committed by GitHub
commit d08b33e696
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 214 additions and 133 deletions

View File

@ -673,37 +673,86 @@ const _inspectdr_scale = [:identity, :ln, :log2, :log10]
const _pgfplotsx_attr = merge_with_base_supported([ const _pgfplotsx_attr = merge_with_base_supported([
:annotations, :annotations,
:background_color_legend, :background_color_inside, :background_color_outside, :background_color_legend,
:foreground_color_legend, :foreground_color_grid, :foreground_color_axis, :background_color_inside,
:foreground_color_text, :foreground_color_border, :background_color_outside,
:foreground_color_legend,
:foreground_color_grid,
:foreground_color_axis,
:foreground_color_text,
:foreground_color_border,
:label, :label,
:seriescolor, :seriesalpha, :seriescolor,
:linecolor, :linestyle, :linewidth, :linealpha, :seriesalpha,
:markershape, :markercolor, :markersize, :markeralpha, :linecolor,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :linestyle,
:fillrange, :fillcolor, :fillalpha, :linewidth,
:linealpha,
:markershape,
:markercolor,
:markersize,
:markeralpha,
:markerstrokewidth,
:markerstrokecolor,
:markerstrokealpha,
:fillrange,
:fillcolor,
:fillalpha,
:bins, :bins,
:layout, :layout,
:title, :window_title, :title,
:guide, :lims, :ticks, :scale, :flip, :window_title,
:guide,
:lims,
:ticks,
:scale,
:flip,
:match_dimensions, :match_dimensions,
:titlefontfamily, :titlefontsize, :titlefonthalign, :titlefontvalign, :titlefontfamily,
:titlefontrotation, :titlefontcolor, :titlefontsize,
:legendfontfamily, :legendfontsize, :legendfonthalign, :legendfontvalign, :titlefonthalign,
:legendfontrotation, :legendfontcolor, :titlefontvalign,
:tickfontfamily, :tickfontsize, :tickfonthalign, :tickfontvalign, :titlefontrotation,
:tickfontrotation, :tickfontcolor, :titlefontcolor,
:guidefontfamily, :guidefontsize, :guidefonthalign, :guidefontvalign, :legendfontfamily,
:guidefontrotation, :guidefontcolor, :legendfontsize,
:grid, :gridalpha, :gridstyle, :gridlinewidth, :legendfonthalign,
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry, :legendfontvalign,
:fill_z, :line_z, :marker_z, :levels, :legendfontrotation,
:ribbon, :quiver, :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, :orientation,
:overwrite_figure, :overwrite_figure,
:polar, :polar,
:aspect_ratio, :aspect_ratio,
:normalize, :weights, :normalize,
:weights,
:inset_subplots, :inset_subplots,
:bar_width, :bar_width,
:arrow, :arrow,
@ -712,13 +761,42 @@ const _pgfplotsx_attr = merge_with_base_supported([
:camera, :camera,
:contour_labels, :contour_labels,
]) ])
const _pgfplotsx_seriestype = const _pgfplotsx_seriestype = [
[:path, :scatter, :straightline, :path,
:path3d, :scatter3d, :surface, :wireframe, :scatter,
:heatmap, :contour, :contour3d, :straightline,
:path3d,
:scatter3d,
:surface,
:wireframe,
:heatmap,
:contour,
:contour3d,
:shape, :shape,
:steppre, :stepmid, :steppost, :ysticks, :xsticks] :steppre,
:stepmid,
:steppost,
:ysticks,
:xsticks,
]
const _pgfplotsx_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] 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] const _pgfplotsx_scale = [:identity, :ln, :log2, :log10]
is_marker_supported(::PGFPlotsXBackend, shape::Shape) = true is_marker_supported(::PGFPlotsXBackend, shape::Shape) = true

View File

@ -51,7 +51,7 @@ function pgfx_preamble(pgfx_plot::Plot{PGFPlotsXBackend})
old_flag = pgfx_plot.attr[:tex_output_standalone] old_flag = pgfx_plot.attr[:tex_output_standalone]
pgfx_plot.attr[:tex_output_standalone] = true pgfx_plot.attr[:tex_output_standalone] = true
fulltext = String(repr("application/x-tex", pgfx_plot)) 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 pgfx_plot.attr[:tex_output_standalone] = old_flag
preamble preamble
end end
@ -129,7 +129,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
) => nothing, ) => nothing,
"fill" => cstr, "fill" => cstr,
"fill opacity" => a, "fill opacity" => a,
"text opacity" => alpha(plot_color(sp[:legendfontcolor])), "text opacity" =>
alpha(plot_color(sp[:legendfontcolor])),
"font" => pgfx_font( "font" => pgfx_font(
sp[:legendfontsize], sp[:legendfontsize],
pgfx_thickness_scaling(sp), pgfx_thickness_scaling(sp),
@ -157,11 +158,8 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
else else
push!( push!(
axis_opt, axis_opt,
"legend pos" => get( "legend pos" =>
_pgfplotsx_legend_pos, get(_pgfplotsx_legend_pos, sp[:legend], "outer north east"),
sp[:legend],
"outer north east",
),
) )
end end
for letter in (:x, :y, :z) for letter in (:x, :y, :z)
@ -186,7 +184,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
pgfx_plot.the_plot, pgfx_plot.the_plot,
"""\\pgfplotsset{ """\\pgfplotsset{
colormap={plots$(sp.attr[:subplot_index])}{$(pgfx_colormap(series.plotattributes[col]))}, colormap={plots$(sp.attr[:subplot_index])}{$(pgfx_colormap(series.plotattributes[col]))},
}""", }""",
) )
push!( push!(
axis_opt, axis_opt,
@ -229,14 +227,15 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
sf = series[:fillrange] sf = series[:fillrange]
series_opt = PGFPlotsX.Options( series_opt = PGFPlotsX.Options(
"color" => single_color(opt[:linecolor]), "color" => single_color(opt[:linecolor]),
"name path" => string(series_id) "name path" => string(series_id),
) )
if is3d(series) || st == :heatmap if is3d(series) || st == :heatmap
series_func = PGFPlotsX.Plot3 series_func = PGFPlotsX.Plot3
else else
series_func = PGFPlotsX.Plot series_func = PGFPlotsX.Plot
end end
if sf !== nothing && !isfilledcontour(series) && series[:ribbon] === nothing if sf !== nothing &&
!isfilledcontour(series) && series[:ribbon] === nothing
push!(series_opt, "area legend" => nothing) push!(series_opt, "area legend" => nothing)
end end
if st == :heatmap if st == :heatmap
@ -267,7 +266,10 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
scale_factor = 0.025 scale_factor = 0.025
mark_size = opt[:markersize] * scale_factor mark_size = opt[:markersize] * scale_factor
path = join( 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) c = get_markercolor(series, i)
@ -288,9 +290,17 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
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 sf !== nothing && !isfilledcontour(series) && series[:ribbon] === nothing if sf !== nothing &&
!isfilledcontour(series) && series[:ribbon] === nothing
if sf isa Number || sf isa AVec 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 end
if i == 1 && sp[:legend] != :none && pgfx_should_add_to_legend(series) if i == 1 && sp[:legend] != :none && pgfx_should_add_to_legend(series)
pgfx_filllegend!(series_opt, opt) pgfx_filllegend!(series_opt, opt)
@ -298,26 +308,25 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
end end
# series # series
# #
coordinates = pgfx_series_coordinates!( coordinates =
sp, pgfx_series_coordinates!(sp, series, segment_opt, opt, rng)
series, segment_plot =
segment_opt, series_func(merge(series_opt, segment_opt), coordinates)
opt,
rng,
)
segment_plot = series_func(
merge(series_opt, segment_opt),
coordinates,
)
push!(axis, segment_plot) push!(axis, segment_plot)
# fill between functions # fill between functions
if sf isa Tuple && series[:ribbon] === nothing if sf isa Tuple && series[:ribbon] === nothing
sf1, sf2 = sf sf1, sf2 = sf
@assert sf1 == series_index "First index of the tuple has to match the current series index." @assert sf1 == series_index "First index of the tuple has to match the current series index."
push!(axis, series_func( push!(
merge(pgfx_fillstyle(opt, series_index), PGFPlotsX.Options("forget plot" => nothing)), axis,
"fill between [of=$series_id and $(_pgfplotsx_series_ids[Symbol(string(sf2))])]" 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 end
# add ribbons? # add ribbons?
ribbon = series[:ribbon] ribbon = series[:ribbon]
@ -334,7 +343,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
if i == 1 && sp[:legend] != :none && pgfx_should_add_to_legend(series) if i == 1 && sp[:legend] != :none && pgfx_should_add_to_legend(series)
leg_opt = PGFPlotsX.Options() leg_opt = PGFPlotsX.Options()
if ribbon !== nothing if ribbon !== nothing
pgfx_filllegend!(axis.contents[end-3].options, opt) pgfx_filllegend!(axis.contents[end - 3].options, opt)
end end
legend = PGFPlotsX.LegendEntry(leg_opt, opt[:label], false) legend = PGFPlotsX.LegendEntry(leg_opt, opt[:label], false)
push!(axis, legend) push!(axis, legend)
@ -353,7 +362,11 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
end end
# add subplot annotations # add subplot annotations
for ann in sp[: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
end # for series end # for series
push!(the_plot, axis) push!(the_plot, axis)
@ -496,7 +509,7 @@ function pgfx_series_coordinates!(
) )
push!( push!(
segment_opt, 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])) return PGFPlotsX.Table(Contour.contours(args..., opt[:levels]))
end end
@ -509,7 +522,7 @@ function pgfx_series_coordinates!(
xs, ys, zs = collect(args) xs, ys, zs = collect(args)
push!( push!(
segment_opt, segment_opt,
"contour filled" => PGFPlotsX.Options("labels" => opt[:contour_labels],), "contour filled" => PGFPlotsX.Options("labels" => opt[:contour_labels]),
"point meta" => "explicit", "point meta" => "explicit",
"shader" => "flat", "shader" => "flat",
) )
@ -520,10 +533,10 @@ function pgfx_series_coordinates!(
end end
cs = join( cs = join(
[join(["($x, $y) [$(zs[j, i])]" for (j, x) in enumerate(xs)], " ") for ( [
i, join(["($x, $y) [$(zs[j, i])]" for (j, x) in enumerate(xs)], " ")
y, for (i, y) in enumerate(ys)
) in enumerate(ys)], ],
"\n\n", "\n\n",
) )
""" """
@ -576,11 +589,8 @@ const _pgfx_framestyle_defaults = Dict(:semi => :box)
# we use the anchors to define orientations for example to align left # we use the anchors to define orientations for example to align left
# one needs to use the right edge as anchor # one needs to use the right edge as anchor
const _pgfx_annotation_halign = KW( const _pgfx_annotation_halign =
:center => "", KW(:center => "", :left => "right", :right => "left")
:left => "right",
:right => "left",
)
## -------------------------------------------------------------------------------------- ## --------------------------------------------------------------------------------------
# Generates a colormap for pgfplots based on a ColorGradient # Generates a colormap for pgfplots based on a ColorGradient
pgfx_arrow(::Nothing) = "every arrow/.append style={-}" pgfx_arrow(::Nothing) = "every arrow/.append style={-}"
@ -608,12 +618,9 @@ function pgfx_filllegend!(series_opt, opt)
io = IOBuffer() io = IOBuffer()
PGFPlotsX.print_tex(io, pgfx_fillstyle(opt)) PGFPlotsX.print_tex(io, pgfx_fillstyle(opt))
style = strip(String(take!(io)), ['[', ']', ' ']) style = strip(String(take!(io)), ['[', ']', ' '])
push!( push!(series_opt, "legend image code/.code" => """{
series_opt, \\draw[$style] (0cm,-0.1cm) rectangle (0.6cm,0.1cm);
"legend image code/.code" => """{ }""")
\\draw[$style] (0cm,-0.1cm) rectangle (0.6cm,0.1cm);
}""",
)
end end
function pgfx_colormap(grad::ColorGradient) function pgfx_colormap(grad::ColorGradient)
@ -632,9 +639,7 @@ function pgfx_framestyle(style::Symbol)
return style return style
else else
default_style = get(_pgfx_framestyle_defaults, style, :axes) default_style = get(_pgfx_framestyle_defaults, style, :axes)
@warn( @warn( "Framestyle :$style is not (yet) supported by the PGFPlotsX backend. :$default_style was cosen instead.",)
"Framestyle :$style is not (yet) supported by the PGFPlotsX backend. :$default_style was cosen instead.",
)
default_style default_style
end end
end end
@ -707,18 +712,21 @@ function pgfx_marker(plotattributes, i = 1)
get_markerstrokealpha(plotattributes, i), get_markerstrokealpha(plotattributes, i),
) )
a_stroke = alpha(cstr_stroke) a_stroke = alpha(cstr_stroke)
mark_size = pgfx_thickness_scaling(plotattributes) * 0.5 * mark_size =
pgfx_thickness_scaling(plotattributes) *
0.5 *
_cycle(plotattributes[:markersize], i) _cycle(plotattributes[:markersize], i)
return PGFPlotsX.Options( return PGFPlotsX.Options(
"mark" => shape isa Shape ? "PlotsShape$i" : "mark" =>
get(_pgfplotsx_markers, shape, "*"), shape isa Shape ? "PlotsShape$i" : get(_pgfplotsx_markers, shape, "*"),
"mark size" => "$mark_size pt", "mark size" => "$mark_size pt",
"mark options" => PGFPlotsX.Options( "mark options" => PGFPlotsX.Options(
"color" => cstr_stroke, "color" => cstr_stroke,
"draw opacity" => a_stroke, "draw opacity" => a_stroke,
"fill" => cstr, "fill" => cstr,
"fill opacity" => a, "fill opacity" => a,
"line width" => pgfx_thickness_scaling(plotattributes) * "line width" =>
pgfx_thickness_scaling(plotattributes) *
_cycle(plotattributes[:markerstrokewidth], i), _cycle(plotattributes[:markerstrokewidth], i),
"rotate" => if shape == :dtriangle "rotate" => if shape == :dtriangle
180 180
@ -874,7 +882,8 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
opt, opt,
string(letter, "label style") => PGFPlotsX.Options( string(letter, "label style") => PGFPlotsX.Options(
labelpos => nothing, labelpos => nothing,
"font" => pgfx_font(axis[:guidefontsize], pgfx_thickness_scaling(sp)), "font" =>
pgfx_font(axis[:guidefontsize], pgfx_thickness_scaling(sp)),
"color" => cstr, "color" => cstr,
"draw opacity" => α, "draw opacity" => α,
"rotate" => axis[:guidefontrotation], "rotate" => axis[:guidefontrotation],
@ -888,10 +897,8 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
scale = axis[:scale] scale = axis[:scale]
if scale in (:log2, :ln, :log10) if scale in (:log2, :ln, :log10)
push!(opt, string(letter, :mode) => "log") push!(opt, string(letter, :mode) => "log")
scale == :ln || push!( scale == :ln ||
opt, push!(opt, "log basis $letter" => "$(scale == :log2 ? 2 : 10)")
"log basis $letter" => "$(scale == :log2 ? 2 : 10)",
)
end end
# ticks on or off # ticks on or off
@ -914,8 +921,9 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
if !(axis[:ticks] in (nothing, false, :none, :native)) && framestyle != :none if !(axis[:ticks] in (nothing, false, :none, :native)) && framestyle != :none
ticks = get_ticks(sp, axis) ticks = get_ticks(sp, axis)
#pgf plot ignores ticks with angle below 90 when xmin = 90 so shift values #pgf plot ignores ticks with angle below 90 when xmin = 90 so shift values
tick_values = ispolar(sp) && letter == :x ? tick_values =
[rad2deg.(ticks[1])[3:end]..., 360, 405] : ticks[1] ispolar(sp) && letter == :x ? [rad2deg.(ticks[1])[3:end]..., 360, 405] :
ticks[1]
push!( push!(
opt, opt,
string(letter, "tick") => string("{", join(tick_values, ","), "}"), string(letter, "tick") => string("{", join(tick_values, ","), "}"),
@ -931,44 +939,37 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
end end
push!( push!(
opt, opt,
string(letter, "ticklabels") => string( string(letter, "ticklabels") =>
"{\$", string("{\$", join(tick_labels, "\$,\$"), "\$}"),
join(tick_labels, "\$,\$"),
"\$}",
),
) )
elseif axis[:showaxis] elseif axis[:showaxis]
tick_labels = ispolar(sp) && letter == :x ? tick_labels =
[ticks[2][3:end]..., "0", "45"] : ticks[2] ispolar(sp) && letter == :x ? [ticks[2][3:end]..., "0", "45"] :
ticks[2]
if axis[:formatter] in (:scientific, :auto) if axis[:formatter] in (:scientific, :auto)
tick_labels = string.("\$", convert_sci_unicode.(tick_labels), "\$") tick_labels = string.("\$", convert_sci_unicode.(tick_labels), "\$")
tick_labels = replace.(tick_labels, Ref("×" => "\\times")) tick_labels = replace.(tick_labels, Ref("×" => "\\times"))
end end
push!( push!(
opt, opt,
string(letter, "ticklabels") => string( string(letter, "ticklabels") =>
"{", string("{", join(tick_labels, ","), "}"),
join(tick_labels, ","),
"}",
),
) )
else else
push!(opt, string(letter, "ticklabels") => "{}") push!(opt, string(letter, "ticklabels") => "{}")
end end
push!( push!(
opt, opt,
string(letter, "tick align") => (axis[:tick_direction] == :out ? string(letter, "tick align") =>
"outside" : "inside"), (axis[:tick_direction] == :out ? "outside" : "inside"),
) )
cstr = plot_color(axis[:tickfontcolor]) cstr = plot_color(axis[:tickfontcolor])
α = alpha(cstr) α = alpha(cstr)
push!( push!(
opt, opt,
string(letter, "ticklabel style") => PGFPlotsX.Options( string(letter, "ticklabel style") => PGFPlotsX.Options(
"font" => pgfx_font( "font" =>
axis[:tickfontsize], pgfx_font(axis[:tickfontsize], pgfx_thickness_scaling(sp)),
pgfx_thickness_scaling(sp),
),
"color" => cstr, "color" => cstr,
"draw opacity" => α, "draw opacity" => α,
"rotate" => axis[:tickfontrotation], "rotate" => axis[:tickfontrotation],
@ -987,7 +988,9 @@ function pgfx_axis!(opt::PGFPlotsX.Options, sp::Subplot, letter)
# framestyle # framestyle
if framestyle in (:axes, :origin) if framestyle in (:axes, :origin)
axispos = framestyle == :axes ? "left" : "middle" axispos = axis[:mirror] ? "right" :
framestyle == :axes ? "left" : "middle"
if axis[:draw_arrow] if axis[:draw_arrow]
push!(opt, string("axis ", letter, " line") => axispos) push!(opt, string("axis ", letter, " line") => axispos)
else else