diff --git a/src/backends.jl b/src/backends.jl index 4197823b..7b1bc646 100644 --- a/src/backends.jl +++ b/src/backends.jl @@ -911,16 +911,21 @@ const _gaston_scale = [:identity, :ln, :log2, :log10] const _unicodeplots_attr = merge_with_base_supported([ :annotations, + :bins, + :guide, + # :grid, :label, + :layout, :legend, - :seriescolor, - :seriesalpha, + :lims, + :linealpha, + :linecolor, :linestyle, :markershape, - :bins, + :seriesalpha, + :seriescolor, + :scale, :title, - :guide, - :lims, ]) const _unicodeplots_seriestype = [ :path, diff --git a/src/backends/unicodeplots.jl b/src/backends/unicodeplots.jl index 73e81220..8bb64364 100644 --- a/src/backends/unicodeplots.jl +++ b/src/backends/unicodeplots.jl @@ -1,12 +1,10 @@ - # https://github.com/JuliaPlots/UnicodePlots.jl # don't warn on unsupported... there's just too many warnings!! -warn_on_unsupported_args(::UnicodePlotsBackend, plotattributes::KW) = nothing +warn_on_unsupported_args(::UnicodePlotsBackend, plotattributes) = nothing -# -------------------------------------------------------------------------------------- - -_canvas_map() = ( +# ------------------------------------------------------------------------------------------ +const _canvas_map = ( ascii = UnicodePlots.AsciiCanvas, block = UnicodePlots.BlockCanvas, braille = UnicodePlots.BrailleCanvas, @@ -16,22 +14,26 @@ _canvas_map() = ( lookup = UnicodePlots.LookupCanvas, ) -# do all the magic here... build it all at once, since we need to know about all the series at the very beginning +# do all the magic here... build it all at once, +# since we need to know about all the series at the very beginning function unicodeplots_rebuild(plt::Plot{UnicodePlotsBackend}) plt.o = UnicodePlots.Plot[] - canvas_map = _canvas_map() + for sp in plt.subplots xaxis = sp[:xaxis] yaxis = sp[:yaxis] xlim = collect(axis_limits(sp, :x)) ylim = collect(axis_limits(sp, :y)) - # we set x/y to have a single point, since we need to create the plot with some data. - # since this point is at the bottom left corner of the plot, it shouldn't actually be shown + # we set x/y to have a single point, + # since we need to create the plot with some data. + # since this point is at the bottom left corner of the plot, + # it shouldn't actually be shown x = Float64[xlim[1]] y = Float64[ylim[1]] - # create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds + # create a plot window with xlim/ylim set, + # but the X/Y vectors are outside the bounds canvas_type = if (ct = _canvas_type[]) == :auto isijulia() ? :ascii : :braille else @@ -40,16 +42,16 @@ function unicodeplots_rebuild(plt::Plot{UnicodePlotsBackend}) kw = ( title = sp[:title], - xlim = xlim, - ylim = ylim, - border = isijulia() ? :ascii : :solid, xlabel = xaxis[:guide], ylabel = yaxis[:guide], xscale = xaxis[:scale], yscale = yaxis[:scale], + border = isijulia() ? :ascii : :solid, + xlim = xlim, + ylim = ylim, ) - o = UnicodePlots.Plot(x, y, canvas_map[canvas_type]; kw...) + o = UnicodePlots.Plot(x, y, _canvas_map[canvas_type]; kw...) for series in series_list(sp) o = addUnicodeSeries!(sp, o, kw, series, sp[:legend] != :none) end @@ -58,7 +60,8 @@ function unicodeplots_rebuild(plt::Plot{UnicodePlotsBackend}) x, y, val = locate_annotation(sp, ann...) o = UnicodePlots.annotate!( o, x, y, val.str; - color = up_color(val.font.color), halign = val.font.halign, valign = val.font.valign + color = up_color(val.font.color), + halign = val.font.halign, valign = val.font.valign ) end @@ -66,17 +69,10 @@ function unicodeplots_rebuild(plt::Plot{UnicodePlotsBackend}) end end -function up_color(col) - if typeof(col) <: UnicodePlots.UserColorType - color = col - elseif typeof(col) <: RGBA - col = convert(ARGB32, col) - color = map(Int, (red(col).i, green(col).i, blue(col).i)) - else - color = :auto - end - color -end +up_color(col::UnicodePlots.UserColorType) = col +up_color(col::RGBA) = + (c = convert(ARGB32, col); map(Int, (red(c).i, green(c).i, blue(c).i))) +up_color(col) = :auto # add a single series function addUnicodeSeries!( @@ -104,9 +100,7 @@ function addUnicodeSeries!( cmap = [(red(c), green(c), blue(c)) for c in get(get_colorgradient(series), rng)] return UnicodePlots.heatmap( series[:z].surf; - zlabel = sp[:colorbar_title], - colormap = cmap, - kw... + zlabel = sp[:colorbar_title], colormap = cmap, kw... ) elseif st == :spy return UnicodePlots.spy(series[:z].surf; kw...) @@ -139,9 +133,10 @@ function addUnicodeSeries!( return up end -# ------------------------------- +# ------------------------------------------------------------------------------------------ -# since this is such a hack, it's only callable using `png`... should error during normal `show` +# since this is such a hack, it's only callable using `png`... +# should error during normal `show` function png(plt::Plot{UnicodePlotsBackend}, fn::AbstractString) fn = addExtension(fn, "png") @@ -149,17 +144,10 @@ function png(plt::Plot{UnicodePlotsBackend}, fn::AbstractString) # make some whitespace and show the plot println("\n\n\n\n\n\n") gui(plt) - - # BEGIN HACK - - # wait while the plot gets drawn sleep(0.5) - - # use osx screen capture when my terminal is maximized and cursor starts at the bottom (I know, right?) - # TODO: compute size of plot to adjust these numbers (or maybe implement something good??) + # use osx screen capture when my terminal is maximized + # and cursor starts at the bottom (I know, right?) run(`screencapture -R50,600,700,420 $fn`) - - # END HACK (phew) return elseif Sys.islinux() run(`clear`) @@ -169,12 +157,14 @@ function png(plt::Plot{UnicodePlotsBackend}, fn::AbstractString) end error( - "Can only savepng on MacOS or Linux with UnicodePlots (though even then I wouldn't do it)", + "Can only savepng on MacOS or Linux with UnicodePlots " * + "(though even then I wouldn't do it)" ) end -# ------------------------------- -Base.show(plt::Plot{UnicodePlotsBackend}) = _show(stdout, MIME("text/plain"), plt) +# ------------------------------------------------------------------------------------------ +Base.show(plt::Plot{UnicodePlotsBackend}) = show(stdout, plt) +Base.show(io::IO, plt::Plot{UnicodePlotsBackend}) = _show(io, MIME("text/plain"), plt) function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend}) unicodeplots_rebuild(plt) @@ -182,9 +172,11 @@ function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend}) lines_colored = Array{Union{Nothing,Vector{String}}}(undef, nr, nc) lines_uncolored = copy(lines_colored) l_max = zeros(Int, nr) + w_max = zeros(Int, nc) buf = IOBuffer() cbuf = IOContext(buf, :color => true) - sps = wmax = 0 + re_col = r"\x1B\[[0-9;]*[a-zA-Z]" + sps = 0 for r in 1:nr lmax = 0 for c in 1:nc @@ -198,27 +190,27 @@ function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend}) sp = plt.o[sps += 1] show(cbuf, sp) colored = String(take!(buf)) - uncolored = replace(colored, r"\x1B\[[0-9;]*[a-zA-Z]" => "") + uncolored = replace(colored, re_col => "") lines_colored[r, c] = lc = split(colored, "\n") lines_uncolored[r, c] = lu = split(uncolored, "\n") lmax = max(length(lc), lmax) - wmax = max(maximum(length.(lu)), wmax) + w_max[c] = max(maximum(length.(lu)), w_max[c]) end end end l_max[r] = lmax end - empty = ' '^wmax + empty = String[' '^w for w ∈ w_max] for r in 1:nr for n in 1:l_max[r] for c in 1:nc pre = c == 1 ? '\0' : ' ' lc = lines_colored[r, c] if lc === nothing || length(lc) < n - print(io, pre, empty) + print(io, pre, empty[c]) else lu = lines_uncolored[r, c] - print(io, pre, lc[n], ' '^(wmax - length(lu[n]))) + print(io, pre, lc[n], ' '^(w_max[c] - length(lu[n]))) end end println(io) diff --git a/test/runtests.jl b/test/runtests.jl index 584e0d11..7f8057e8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,18 +1,20 @@ using Plots: guidefont, series_annotations, PLOTS_SEED -import ImageMagick + using VisualRegressionTests -using Plots -using Random -using StableRNGs -using Test -using TestImages -using FileIO -using Gtk -using LibGit2 -import GeometryBasics -using Dates using RecipesBase +using StableRNGs +using TestImages +using LibGit2 +using Random +using FileIO +using Plots +using Dates using JSON +using Test +using Gtk + +import GeometryBasics +import ImageMagick @testset "Infrastructure" begin @test_nowarn JSON.Parser.parse( @@ -33,7 +35,7 @@ end end Plots.plotly_local_file_path[] = nothing Plots.use_local_dependencies[] = temp -end # testset +end include("test_defaults.jl") include("test_pipeline.jl") @@ -55,7 +57,6 @@ function reference_file(backend, i, version) refdir = reference_dir("Plots", string(backend)) fn = "ref$i.png" versions = sort(VersionNumber.(readdir(refdir)), rev = true) - reffn = joinpath(refdir, string(version), fn) for v in versions tmpfn = joinpath(refdir, string(v), fn) @@ -64,7 +65,6 @@ function reference_file(backend, i, version) break end end - return reffn end @@ -79,9 +79,10 @@ if !isdir(reference_dir()) end include("imgcomp.jl") -# don't actually show the plots Random.seed!(PLOTS_SEED) -default(show = false, reuse = true) + +default(show = false, reuse = true) # don't actually show the plots + is_ci() = get(ENV, "CI", "false") == "true" const PLOTS_IMG_TOL = parse(Float64, get(ENV, "PLOTS_IMG_TOL", is_ci() ? "1e-4" : "1e-5")) @@ -106,68 +107,8 @@ const PLOTS_IMG_TOL = parse(Float64, get(ENV, "PLOTS_IMG_TOL", is_ci() ? "1e-4" # image_comparison_facts(:pgfplotsx, tol=PLOTS_IMG_TOL, skip = Plots._backend_skips[:pgfplotsx]) # end -# 10 Histogram2D - ## -@testset "Backends" begin - @testset "GR" begin - ENV["PLOTS_TEST"] = "true" - ENV["GKSwstype"] = "100" - @test gr() == Plots.GRBackend() - @test backend() == Plots.GRBackend() - - @static if haskey(ENV, "APPVEYOR") - @info "Skipping GR image comparison tests on AppVeyor" - else - image_comparison_facts( - :gr, - tol = PLOTS_IMG_TOL, - skip = Plots._backend_skips[:gr], - ) - end - end - - @testset "UnicodePlots" begin - @test unicodeplots() == Plots.UnicodePlotsBackend() - @test backend() == Plots.UnicodePlotsBackend() - - io = IOBuffer() - - # lets just make sure it runs without error - p = plot(rand(10)) - @test p isa Plots.Plot - @test show(io, p) isa Nothing - p = bar(randn(10)) - @test p isa Plots.Plot - @test show(io, p) isa Nothing - p = plot([1, 2], [3, 4]) - annotate!(p, [(1.5, 3.2, Plots.text("Test", :red, :center))]) - hline!(p, [3.1]) - @test p isa Plots.Plot - @test show(io, p) isa Nothing - p = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4]) - hline!(p, [3.1]) - annotate!(p, [(Dates.Date(2019, 1, 15), 3.2, Plots.text("Test", :red, :center))]) - @test p isa Plots.Plot - @test show(io, p) isa Nothing - p = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4]) - annotate!(p, [(Dates.Date(2019, 1, 15), 3.2, :auto)]) - hline!(p, [3.1]) - @test p isa Plots.Plot - @test show(io, p) isa Nothing - end - - @testset "PlotlyJS" begin - @test plotlyjs() == Plots.PlotlyJSBackend() - @test backend() == Plots.PlotlyJSBackend() - - p = plot(rand(10)) - @test p isa Plots.Plot - @test_broken display(p) isa Nothing - end -end - @testset "Axes" begin p = plot() axis = p.subplots[1][:xaxis] @@ -183,10 +124,12 @@ end end @testset "NoFail" begin - #ensure backend with tested display + # ensure backend with tested display @test unicodeplots() == Plots.UnicodePlotsBackend() @test backend() == Plots.UnicodePlotsBackend() + dsp = TextDisplay(IOContext(IOBuffer(), :color => true)) + @testset "Plot" begin plots = [ histogram([1, 0, 0, 0, 0, 0]), @@ -199,21 +142,20 @@ end plot(["a" "b"; missing "d"], [1 2; 3 4]), ] for plt in plots - display(plt) + display(dsp, plt) end @test_nowarn plot(x -> x^2, 0, 2) end @testset "Bar" begin p = bar([3, 2, 1], [1, 2, 3]) - @test isa(p, Plots.Plot) - @test isa(display(p), Nothing) == true + @test p isa Plots.Plot + @test display(dsp, p) isa Nothing end end @testset "EmptyAnim" begin - anim = @animate for i in [] - end + anim = @animate for i in [] end @test_throws ArgumentError gif(anim) end @@ -257,3 +199,62 @@ end Plots.process_clims(missing) == Plots.process_clims(:auto) end + + +@testset "Backends" begin + @testset "UnicodePlots" begin + @test unicodeplots() == Plots.UnicodePlotsBackend() + @test backend() == Plots.UnicodePlotsBackend() + + io = IOContext(IOBuffer(), :color => true) + + # lets just make sure it runs without error + p = plot(rand(10)) + @test p isa Plots.Plot + @test show(io, p) isa Nothing + p = bar(randn(10)) + @test p isa Plots.Plot + @test show(io, p) isa Nothing + p = plot([1, 2], [3, 4]) + annotate!(p, [(1.5, 3.2, Plots.text("Test", :red, :center))]) + hline!(p, [3.1]) + @test p isa Plots.Plot + @test show(io, p) isa Nothing + p = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4]) + hline!(p, [3.1]) + annotate!(p, [(Dates.Date(2019, 1, 15), 3.2, Plots.text("Test", :red, :center))]) + @test p isa Plots.Plot + @test show(io, p) isa Nothing + p = plot([Dates.Date(2019, 1, 1), Dates.Date(2019, 2, 1)], [3, 4]) + annotate!(p, [(Dates.Date(2019, 1, 15), 3.2, :auto)]) + hline!(p, [3.1]) + @test p isa Plots.Plot + @test show(io, p) isa Nothing + end + + @testset "PlotlyJS" begin + @test plotlyjs() == Plots.PlotlyJSBackend() + @test backend() == Plots.PlotlyJSBackend() + + p = plot(rand(10)) + @test p isa Plots.Plot + @test_broken display(p) isa Nothing + end + + @testset "GR" begin + ENV["PLOTS_TEST"] = "true" + ENV["GKSwstype"] = "100" + @test gr() == Plots.GRBackend() + @test backend() == Plots.GRBackend() + + @static if haskey(ENV, "APPVEYOR") + @info "Skipping GR image comparison tests on AppVeyor" + else + image_comparison_facts( + :gr, + tol = PLOTS_IMG_TOL, + skip = Plots._backend_skips[:gr], + ) + end + end +end