Compare commits

..

24 Commits

Author SHA1 Message Date
Daniel Schwabeneder 67ba06cd77 Merge pull request #2548 from daschw/surface
Implement zerror and surface type recipe fixes
2020-04-07 17:50:32 +02:00
Daniel Schwabeneder aa61d681d9 bump version 2020-04-07 16:27:33 +02:00
Daniel Schwabeneder c8e6b967e5 add errorbar test example 2020-04-07 16:27:02 +02:00
Daniel Schwabeneder 819e91aa37 add zerror recipe 2020-04-07 16:06:12 +02:00
Daniel Schwabeneder 7f835cf7cb Merge pull request #2546 from isentropic/isen-dev
new aliases
2020-04-07 13:51:10 +02:00
Daniel Schwabeneder 43571e6d40 Merge pull request #2547 from Libbum/empty-fix
Fix 'isempy' check
2020-04-07 13:50:55 +02:00
Tim DuBois dd3666b6d3 Fix 'isempy' check 2020-04-07 13:33:16 +02:00
Simon Christ f98b191616 Layouting (#2486)
* distances very big

* better default

* respect margins

* remove failing example
2020-04-07 10:44:22 +02:00
Zhanibek 9fdcf0db74 new aliases 2020-04-07 17:23:05 +09:00
Daniel Schwabeneder 7abab41f2b add letter in surface type recipe 2020-04-07 09:52:32 +02:00
Daniel Schwabeneder 45569c0d4d Merge pull request #2544 from JeffBezanson/jb/optlevel1
use -O1 for code in Plots.jl
2020-04-06 22:59:32 +02:00
Simon Christ ca653c5bec Merge pull request #2541 from JuliaPlots/bbs/sani
special case LaTeXStrings in sanitation
2020-04-06 22:33:13 +02:00
Simon Christ 6c0539bac6 Merge pull request #2545 from JuliaPlots/bbs/annotations
fix annotations
2020-04-06 22:28:06 +02:00
Simon Christ 65c9a46c8d rely on require 2020-04-06 22:00:51 +02:00
Simon Christ 7bd97558dc fix annotations 2020-04-06 21:41:11 +02:00
Jeff Bezanson 6807f53a7e use -O1 for code in Plots.jl 2020-04-06 15:01:33 -04:00
Daniel Schwabeneder ae396ef576 Merge pull request #2543 from daschw/typesurface
allow type recipes for numbers in surfaces
2020-04-06 18:06:56 +02:00
Daniel Schwabeneder 3dd1c500cf fix ambiguity 2020-04-06 17:40:31 +02:00
Daniel Schwabeneder 59e4129194 skip maybestrings 2020-04-06 17:25:18 +02:00
Daniel Schwabeneder 54d441b62a bump versio 2020-04-06 17:10:27 +02:00
Daniel Schwabeneder f8d36363b4 allow type recipes for numbers in surfaces 2020-04-06 17:09:00 +02:00
Daniel Schwabeneder c32c7fda58 Merge pull request #2542 from harryscholes/splatted_annotations
Fix #2487
2020-04-06 16:32:44 +02:00
harryscholes 87b220e747 Fix #2487 2020-04-06 15:15:55 +01:00
Simon Christ 57a6f4303b special case LaTeXStrings 2020-04-06 15:44:06 +02:00
12 changed files with 195 additions and 81 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
name = "Plots"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
author = ["Tom Breloff (@tbreloff)"]
version = "1.0.2"
version = "1.0.4"
[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
+4
View File
@@ -1,5 +1,9 @@
module Plots
if isdefined(Base, :Experimental) && isdefined(Base.Experimental, Symbol("@optlevel"))
@eval Base.Experimental.@optlevel 1
end
const _current_plots_version = VersionNumber(split(first(filter(line -> occursin("version", line), readlines(normpath(@__DIR__, "..", "Project.toml")))), "\"")[2])
using Reexport
+5 -2
View File
@@ -267,6 +267,7 @@ const _series_defaults = KW(
:bar_edges => false,
:xerror => nothing,
:yerror => nothing,
:zerror => nothing,
:ribbon => nothing,
:quiver => nothing,
:arrow => nothing, # allows for adding arrows to line/path... call `arrow(args...)`
@@ -568,17 +569,18 @@ add_aliases(:bins, :bin, :nbin, :nbins, :nb)
add_aliases(:ribbon, :rib)
add_aliases(:annotations, :ann, :anns, :annotate, :annotation)
add_aliases(:xguide, :xlabel, :xlab, :xl)
add_aliases(:xlims, :xlim, :xlimit, :xlimits)
add_aliases(:xlims, :xlim, :xlimit, :xlimits, :xrange)
add_aliases(:xticks, :xtick)
add_aliases(:xrotation, :xrot, :xr)
add_aliases(:yguide, :ylabel, :ylab, :yl)
add_aliases(:ylims, :ylim, :ylimit, :ylimits)
add_aliases(:ylims, :ylim, :ylimit, :ylimits, :yrange)
add_aliases(:yticks, :ytick)
add_aliases(:yrotation, :yrot, :yr)
add_aliases(:zguide, :zlabel, :zlab, :zl)
add_aliases(:zlims, :zlim, :zlimit, :zlimits)
add_aliases(:zticks, :ztick)
add_aliases(:zrotation, :zrot, :zr)
add_aliases(:guidefontsize, :labelfontsize)
add_aliases(:fill_z, :fillz, :fz, :surfacecolor, :surfacecolour, :sc, :surfcolor, :surfcolour)
add_aliases(:legend, :leg, :key)
add_aliases(:legendtitle, :legend_title, :labeltitle, :label_title, :leg_title, :key_title)
@@ -593,6 +595,7 @@ add_aliases(:color_palette, :palette)
add_aliases(:overwrite_figure, :clf, :clearfig, :overwrite, :reuse)
add_aliases(:xerror, :xerr, :xerrorbar)
add_aliases(:yerror, :yerr, :yerrorbar, :err, :errorbar)
add_aliases(:zerror, :zerr, :zerrorbar)
add_aliases(:quiver, :velocity, :quiver2d, :gradient, :vectorfield)
add_aliases(:normalize, :norm, :normed, :normalized)
add_aliases(:show_empty_bins, :showemptybins, :showempty, :show_empty)
+1 -1
View File
@@ -211,7 +211,7 @@ const _base_supported_args = [
:seriestype,
:seriescolor, :seriesalpha,
:smooth,
:xerror, :yerror,
:xerror, :yerror, :zerror,
:subplot,
:x, :y, :z,
:show, :size,
+47 -31
View File
@@ -104,14 +104,20 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
end
for sp in plt.subplots
bb = bbox(sp)
sp_width = width(bb)
sp_height = height(bb)
dx, dy = bb.x0
# TODO: does this hold at every scale?
if sp[:legend] in (:outertopright, nothing)
dx *= 1.2
end
bb1 = sp.plotarea
bb2 = bbox(sp)
sp_width = width(bb2)
sp_height = height(bb2)
dx, dy = bb2.x0
lpad = leftpad(sp) + sp[:left_margin]
rpad = rightpad(sp) + sp[:right_margin]
tpad = toppad(sp) + sp[:top_margin]
bpad = bottompad(sp) + sp[:bottom_margin]
dx += lpad
dy += tpad
axis_height = sp_height - (tpad + bpad)
axis_width = sp_width - (rpad + lpad)
cstr = plot_color(sp[:background_color_legend])
a = alpha(cstr)
fg_alpha = alpha(plot_color(sp[:foreground_color_legend]))
@@ -164,9 +170,9 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
"xshift" => string(dx),
"yshift" => string(-dy),
)
sp_width > 0 * mm ? push!(axis_opt, "width" => string(sp_width)) :
sp_width > 0 * mm ? push!(axis_opt, "width" => string(axis_width)) :
nothing
sp_height > 0 * mm ? push!(axis_opt, "height" => string(sp_height)) :
sp_height > 0 * mm ? push!(axis_opt, "height" => string(axis_height)) :
nothing
# legend position
if sp[:legend] isa Tuple
@@ -352,17 +358,6 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
series_index,
)
end
# add series annotations
anns = series[:series_annotations]
for (xi, yi, str, fnt) in EachAnn(anns, series[:x], series[:y])
pgfx_add_annotation!(
axis,
xi,
yi,
PlotText(str, fnt),
pgfx_thickness_scaling(series),
)
end
# add to legend?
if sp[:legend] != :none
leg_entry = if opt[:label] isa AVec
@@ -388,15 +383,26 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
end
end
end # for segments
# add subplot annotations
for ann in sp[:annotations]
# add series annotations
anns = series[:series_annotations]
for (xi, yi, str, fnt) in EachAnn(anns, series[:x], series[:y])
pgfx_add_annotation!(
axis,
locate_annotation(sp, ann...)...,
pgfx_thickness_scaling(sp),
xi,
yi,
PlotText(str, fnt),
pgfx_thickness_scaling(series),
)
end
end # for series
# add subplot annotations
for ann in sp[:annotations]
pgfx_add_annotation!(
axis,
locate_annotation(sp, ann...)...,
pgfx_thickness_scaling(sp),
)
end
push!(the_plot, axis)
if length(plt.o.the_plot.elements) > 0
plt.o.the_plot.elements[1] = the_plot
@@ -405,6 +411,7 @@ function (pgfx_plot::PGFPlotsXPlot)(plt::Plot{PGFPlotsXBackend})
end
end # for subplots
pgfx_plot.is_created = true
pgfx_plot.was_shown = false
end # if
return pgfx_plot
end
@@ -900,6 +907,15 @@ function pgfx_sanitize_string(s::AbstractString)
s = replace(s, r"\\?\%" => "\\%")
s = replace(s, r"\\?\_" => "\\_")
s = replace(s, r"\\?\&" => "\\&")
s = replace(s, r"\\?\{" => "\\{")
s = replace(s, r"\\?\}" => "\\}")
end
@require LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" begin
using LaTeXStrings
function pgfx_sanitize_string(s::LaTeXString)
s = replace(s, r"\\?\#" => "\\#")
s = replace(s, r"\\?\%" => "\\%")
end
end
function pgfx_sanitize_plot!(plt)
for (key, value) in plt.attr
@@ -1129,12 +1145,12 @@ end
# Set the (left, top, right, bottom) minimum padding around the plot area
# to fit ticks, tick labels, guides, colorbars, etc.
function _update_min_padding!(sp::Subplot{PGFPlotsXBackend})
# TODO: make padding more intelligent
# TODO: how to include margins properly?
# sp.minpad = (50mm + sp[:left_margin],
# 0mm + sp[:top_margin],
# 50mm + sp[:right_margin],
# 0mm + sp[:bottom_margin])
leg = sp[:legend]
if leg in (:best, :outertopright, :outerright, :outerbottomright) || (leg isa Tuple && leg[1] >= 1)
sp.minpad = (0mm, 0mm, 5mm, 0mm)
else
sp.minpad = (0mm, 0mm, 0mm, 0mm)
end
end
function _create_backend_figure(plt::Plot{PGFPlotsXBackend})
+38
View File
@@ -946,6 +946,43 @@ const _examples = PlotExample[
end,
],
),
PlotExample(
"Error bars and array type recipes",
"",
[
quote
begin
struct Measurement <: Number
val::Float64
err::Float64
end
value(m::Measurement) = m.val
uncertainty(m::Measurement) = m.err
@recipe function f(::Type{T}, m::T) where T <: AbstractArray{<:Measurement}
if !(get(plotattributes, :seriestype, :path) in [:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image])
error_sym = Symbol(plotattributes[:letter], :error)
plotattributes[error_sym] = uncertainty.(m)
end
value.(m)
end
x = Measurement.(10sort(rand(10)), rand(10))
y = Measurement.(10sort(rand(10)), rand(10))
z = Measurement.(10sort(rand(10)), rand(10))
surf = Measurement.((1:10) .* (1:10)', rand(10,10))
plot(
scatter(x, [x y], msw = 0),
scatter(x, y, z, msw = 0),
heatmap(x, y, surf),
wireframe(x, y, surf),
legend = :topleft
)
end
end,
],
),
]
# Some constants for PlotDocs and PlotReferenceImages
@@ -958,6 +995,7 @@ _backend_skips = Dict(
:pgfplots => [2, 5, 6, 10, 16, 20, 22, 23, 25, 28, 30, 31, 34, 37, 38, 39],
:pgfplotsx => [ 6, # images
10, # histogram2d
16, # pgfplots thinks the upper panel is too small
22, # contourf
23, # pie
32, # spy
+1 -1
View File
@@ -159,7 +159,7 @@ end
function _add_errorbar_kw(kw_list::Vector{KW}, kw::AKW)
# handle error bars by creating new recipedata data... these will have
# the same recipedata index as the recipedata they are copied from
for esym in (:xerror, :yerror)
for esym in (:xerror, :yerror, :zerror)
if get(kw, esym, nothing) !== nothing
# we make a copy of the KW and apply an errorbar recipe
errkw = copy(kw)
+46 -33
View File
@@ -1012,49 +1012,62 @@ function error_zipit(ebar)
end
end
function error_coords(xorig, yorig, ebar)
# init empty x/y, and zip errors if passed Tuple{Vector,Vector}
x, y = Array{float_extended_type(xorig)}(undef, 0), Array{Float64}(undef, 0)
# for each point, create a line segment from the bottom to the top of the errorbar
for i = 1:max(length(xorig), length(yorig))
xi = _cycle(xorig, i)
yi = _cycle(yorig, i)
ebi = _cycle(ebar, i)
nanappend!(x, [xi, xi])
e1, e2 = if istuple(ebi)
first(ebi), last(ebi)
elseif isscalar(ebi)
ebi, ebi
else
error("unexpected ebi type $(typeof(ebi)) for errorbar: $ebi")
error_tuple(x) = x, x
error_tuple(x::Tuple) = x
function error_coords(errorbar, errordata, otherdata...)
ed = Vector{float_extended_type(errordata)}(undef, 0)
od = [Float64[] for _ in otherdata]
for (i, edi) in enumerate(errordata)
for (j, odj) in enumerate(otherdata)
odi = _cycle(odj, i)
nanappend!(od[j], [odi, odi])
end
nanappend!(y, [yi - e1, yi + e2])
e1, e2 = error_tuple(_cycle(errorbar, i))
nanappend!(ed, [edi - e1, edi + e2])
end
x, y
return (ed, od...)
end
# we will create a series of path segments, where each point represents one
# side of an errorbar
@recipe function f(::Type{Val{:yerror}}, x, y, z)
error_style!(plotattributes)
markershape := :hline
plotattributes[:x], plotattributes[:y] = error_coords(
plotattributes[:x],
plotattributes[:y],
error_zipit(plotattributes[:yerror]),
)
()
end
@deps yerror path
@recipe function f(::Type{Val{:xerror}}, x, y, z)
error_style!(plotattributes)
markershape := :vline
plotattributes[:y], plotattributes[:x] = error_coords(
plotattributes[:y],
plotattributes[:x],
error_zipit(plotattributes[:xerror]),
)
xerr = error_zipit(plotattributes[:xerror])
if z === nothing
plotattributes[:x], plotattributes[:y] = error_coords(xerr, x, y)
else
plotattributes[:x], plotattributes[:y], plotattributes[:z] =
error_coords(xerr, x, y, z)
end
()
end
@deps xerror path
@recipe function f(::Type{Val{:yerror}}, x, y, z)
error_style!(plotattributes)
markershape := :hline
yerr = error_zipit(plotattributes[:yerror])
if z === nothing
plotattributes[:y], plotattributes[:x] = error_coords(yerr, y, x)
else
plotattributes[:y], plotattributes[:x], plotattributes[:z] =
error_coords(yerr, y, x, z)
end
()
end
@deps yerror path
@recipe function f(::Type{Val{:zerror}}, x, y, z)
error_style!(plotattributes)
markershape := :hline
if z !== nothing
zerr = error_zipit(plotattributes[:zerror])
plotattributes[:z], plotattributes[:x], plotattributes[:y] =
error_coords(zerr, z, x, y)
end
()
end
@deps xerror path
+13 -6
View File
@@ -216,20 +216,27 @@ function _apply_type_recipe(plotattributes, v::AbstractArray, letter)
end
# special handling for Surface... need to properly unwrap and re-wrap
_apply_type_recipe(plotattributes, v::Surface{<:AMat{<:DataPoint}}) = v
function _apply_type_recipe(plotattributes, v::Surface)
ret = _apply_type_recipe(plotattributes, v.surf)
_apply_type_recipe(
plotattributes,
v::Surface{<:AMat{<:Union{AbstractFloat, Integer, AbstractString, Missing}}},
letter,
) = v
function _apply_type_recipe(plotattributes, v::Surface, letter)
ret = _apply_type_recipe(plotattributes, v.surf, letter)
if typeof(ret) <: Formatted
Formatted(Surface(ret.data), ret.formatter)
else
Surface(ret.data)
Surface(ret)
end
end
# don't do anything vectors of datapoints and for nothing
_apply_type_recipe(plotattributes, v::Nothing, letter) = v
_apply_type_recipe(plotattributes, v::AbstractArray{<:MaybeString}, letter) = v
_apply_type_recipe(plotattributes, v::AbstractArray{<:Union{Real, Nothing}}, letter) = v
_apply_type_recipe(
plotattributes,
v::AbstractArray{<:Union{AbstractFloat, Integer, AbstractString, Missing}},
letter,
) = v
# axis args before type recipes should still be mapped to all axes
function _preprocess_axis_args!(plotattributes)
+1 -1
View File
@@ -438,7 +438,7 @@ julia> plot(1:10)
julia> annotate!([(7,3,"(7,3)"),(3,7,text("hey", 14, :left, :top, :green))])
```
"""
annotate!(anns...; kw...) = plot!(; annotation = anns, kw...)
annotate!(anns...; kw...) = plot!(; annotation = collect(anns), kw...)
annotate!(anns::AVec{T}; kw...) where {T<:Tuple} = plot!(; annotation = anns, kw...)
"Flip the current plots' x axis"
+1 -1
View File
@@ -826,7 +826,7 @@ end
extend_to_length!(v::AbstractRange, n) = range(first(v), step = step(v), length = n)
function extend_to_length!(v::AbstractVector, n)
vmax = isempy(v) ? 0 : ignorenan_maximum(v)
vmax = isempty(v) ? 0 : ignorenan_maximum(v)
extend_by_data!(v, vmax .+ (1:(n - length(v))))
end
extend_by_data!(v::AbstractVector, x) = isimmutable(v) ? vcat(v, x) : push!(v, x)
+37 -4
View File
@@ -245,15 +245,39 @@ end
end # testset
@testset "Annotations" begin
y = rand(10)
plot(
pgfx_plot = plot(
y,
annotations = (3, y[3], Plots.text("this is \\#3", :left)),
leg = false,
)
Plots._update_plot_object(pgfx_plot)
axis_content = Plots.pgfx_axes(pgfx_plot.o)[1].contents
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
@test length(nodes) == 1
mktempdir() do path
file_path =joinpath(path,"annotations.tex")
@test_nowarn savefig(pgfx_plot, file_path)
open(file_path) do io
lines = readlines(io)
@test count(s -> occursin("node", s), lines) == 1
end
end
annotate!([
(5, y[5], Plots.text("this is \\#5", 16, :red, :center)),
(10, y[10], Plots.text("this is \\#10", :right, 20, "courier")),
])
Plots._update_plot_object(pgfx_plot)
axis_content = Plots.pgfx_axes(pgfx_plot.o)[1].contents
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
@test length(nodes) == 3
mktempdir() do path
file_path =joinpath(path,"annotations.tex")
@test_nowarn savefig(pgfx_plot, file_path)
open(file_path) do io
lines = readlines(io)
@test count(s -> occursin("node", s), lines) == 3
end
end
annotation_plot = scatter!(
range(2, stop = 8, length = 6),
rand(6),
@@ -267,9 +291,18 @@ end
Plots.text("data", :green),
],
)
# mktempdir() do path
# @test_nowarn savefig(annotation_plot, path*"annotation.pdf")
# end
Plots._update_plot_object(annotation_plot)
axis_content = Plots.pgfx_axes(annotation_plot.o)[1].contents
nodes = filter(x -> !isa(x, PGFPlotsX.Plot), axis_content)
@test length(nodes) == 9
mktempdir() do path
file_path =joinpath(path,"annotations.tex")
@test_nowarn savefig(annotation_plot, file_path)
open(file_path) do io
lines = readlines(io)
@test count(s -> occursin("node", s), lines) == 9
end
end
end # testset
@testset "Ribbon" begin
aa = rand(10)