UnicodePlots: basic quiver and contour support (#4031)

* basic quiver support

* toggle grid

* add contour

* add colorbar

* toggle contour example

* rework keywords
This commit is contained in:
t-bltg 2022-01-19 18:58:11 +01:00 committed by GitHub
parent 1f49839529
commit f333cb284a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 62 additions and 32 deletions

View File

@ -55,7 +55,7 @@ Scratch = "1"
Showoff = "0.3.1, 1.0" Showoff = "0.3.1, 1.0"
StatsBase = "0.32 - 0.33" StatsBase = "0.32 - 0.33"
UnicodeFun = "0.4" UnicodeFun = "0.4"
UnicodePlots = "2.4" UnicodePlots = "2.6"
Unzip = "0.1" Unzip = "0.1"
julia = "1.6" julia = "1.6"

View File

@ -923,6 +923,8 @@ const _unicodeplots_attr = merge_with_base_supported([
:linecolor, :linecolor,
:linestyle, :linestyle,
:markershape, :markershape,
:quiver,
:arrow,
:seriesalpha, :seriesalpha,
:seriescolor, :seriescolor,
:scale, :scale,
@ -936,6 +938,7 @@ const _unicodeplots_seriestype = [
:shape, :shape,
:histogram2d, :histogram2d,
:heatmap, :heatmap,
:contour,
:spy, :spy,
] ]
const _unicodeplots_style = [:auto, :solid] const _unicodeplots_style = [:auto, :solid]
@ -970,10 +973,10 @@ const _unicodeplots_marker = [
const _unicodeplots_scale = [:identity, :ln, :log2, :log10] const _unicodeplots_scale = [:identity, :ln, :log2, :log10]
# Additional constants # Additional constants
const _unicodeplots_canvas = Ref(:auto) const _up_colormap = Ref(:none)
const _unicodeplots_border = Ref(:auto) const _up_canvas = Ref(:auto)
const _unicodeplots_height = Ref(15) const _up_border = Ref(:auto)
const _unicodeplots_width = Ref(40) const _up_fix_ar = Ref(true)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# hdf5 # hdf5

View File

@ -24,35 +24,55 @@ function unicodeplots_rebuild(plt::Plot{UnicodePlotsBackend})
yaxis = sp[:yaxis] yaxis = sp[:yaxis]
xlim = collect(axis_limits(sp, :x)) xlim = collect(axis_limits(sp, :x))
ylim = collect(axis_limits(sp, :y)) ylim = collect(axis_limits(sp, :y))
F = float(eltype(xlim))
# We set x/y to have a single point, # We set x/y to have a single point,
# since we need to create the plot with some data. # since we need to create the plot with some data.
# Since this point is at the bottom left corner of the plot, # Since this point is at the bottom left corner of the plot,
# it should be hidden by consecutive plotting commands. # it should be hidden by consecutive plotting commands.
x = Float64[xlim[1]] x = F[xlim[1]]
y = Float64[ylim[1]] y = F[ylim[1]]
# create a plot window with xlim/ylim set, # create a plot window with xlim/ylim set,
# but the X/Y vectors are outside the bounds # but the X/Y vectors are outside the bounds
canvas = if (up_c = _unicodeplots_canvas[]) == :auto canvas = if (up_c = _up_canvas[]) == :auto
isijulia() ? :ascii : :braille isijulia() ? :ascii : :braille
else else
up_c up_c
end end
border = if (up_b = _unicodeplots_border[]) == :auto border = if (up_b = _up_border[]) == :auto
isijulia() ? :ascii : :solid isijulia() ? :ascii : :solid
else else
up_b up_b
end end
width, height = UnicodePlots.DEFAULT_WIDTH[], UnicodePlots.DEFAULT_HEIGHT[]
grid = xaxis[:grid] && yaxis[:grid]
quiver = contour = false
for series in series_list(sp)
st = series[:seriestype]
quiver |= series[:arrow] isa Arrow # post-pipeline detection (:quiver -> :path)
contour |= st === :contour
if st === :histogram2d
xlim = ylim = (0, 0)
elseif st === :spy || st === :heatmap
width = height = 0
grid = false
end
end
grid &= !contour && !quiver
kw = ( kw = (
compact = true, compact = true,
title = texmath2unicode(sp[:title]), title = texmath2unicode(sp[:title]),
xlabel = texmath2unicode(xaxis[:guide]), xlabel = texmath2unicode(xaxis[:guide]),
ylabel = texmath2unicode(yaxis[:guide]), ylabel = texmath2unicode(yaxis[:guide]),
height = _unicodeplots_height[], grid = grid,
width = _unicodeplots_width[], blend = !(quiver || contour),
height = height,
width = width,
xscale = xaxis[:scale], xscale = xaxis[:scale],
yscale = yaxis[:scale], yscale = yaxis[:scale],
border = border, border = border,
@ -87,6 +107,11 @@ up_color(col::RGBA) =
(c = convert(ARGB32, col); map(Int, (red(c).i, green(c).i, blue(c).i))) (c = convert(ARGB32, col); map(Int, (red(c).i, green(c).i, blue(c).i)))
up_color(col) = :auto up_color(col) = :auto
function up_cmap(series)
rng = range(0, 1, length = length(UnicodePlots.COLOR_MAP_DATA[:viridis]))
[(red(c), green(c), blue(c)) for c in get(get_colorgradient(series), rng)]
end
# add a single series # add a single series
function addUnicodeSeries!( function addUnicodeSeries!(
sp::Subplot{UnicodePlotsBackend}, sp::Subplot{UnicodePlotsBackend},
@ -98,41 +123,44 @@ function addUnicodeSeries!(
st = series[:seriestype] st = series[:seriestype]
# get the series data and label # get the series data and label
x, y = if st == :straightline x, y = if st === :straightline
straightline_data(series) straightline_data(series)
elseif st == :shape elseif st === :shape
shape_data(series) shape_data(series)
else else
float(series[:x]), float(series[:y]) float(series[:x]), float(series[:y])
end end
# special handling (src/interface) # special handling (src/interface)
if st == :histogram2d if st === :histogram2d
kw[:xlim][:] .= kw[:ylim][:] .= 0
return UnicodePlots.densityplot(x, y; kw...) return UnicodePlots.densityplot(x, y; kw...)
elseif st == :heatmap elseif st === :spy
rng = range(0, 1, length = length(UnicodePlots.COLOR_MAP_DATA[:viridis])) return UnicodePlots.spy(series[:z].surf; fix_ar = _up_fix_ar[], kw...)
cmap = [(red(c), green(c), blue(c)) for c in get(get_colorgradient(series), rng)] elseif st in (:contour, :heatmap)
return UnicodePlots.heatmap( kw = (
series[:z].surf;
zlabel = sp[:colorbar_title],
colormap = cmap,
kw..., kw...,
zlabel = sp[:colorbar_title],
colormap = (cm = _up_colormap[] === :none) ? up_cmap(series) : cm,
colorbar = hascolorbar(sp),
) )
elseif st == :spy if st === :contour
return UnicodePlots.spy(series[:z].surf; kw...) isfilledcontour(series) && @warn "Plots(UnicodePlots): filled contour is not implemented"
return UnicodePlots.contourplot(x, y, series[:z].surf; kw..., levels = series[:levels])
elseif st === :heatmap
return UnicodePlots.heatmap(series[:z].surf; fix_ar = _up_fix_ar[], kw...)
# zlim = collect(axis_limits(sp, :z))
end
end end
series_kw = (;)
# now use the ! functions to add to the plot # now use the ! functions to add to the plot
if st in (:path, :straightline, :shape) if st in (:path, :straightline, :shape)
func = UnicodePlots.lineplot! func = UnicodePlots.lineplot!
elseif st == :scatter || series[:markershape] != :none series_kw = (; head_tail = series[:arrow] isa Arrow ? series[:arrow].side : nothing)
elseif st === :scatter || series[:markershape] != :none
func = UnicodePlots.scatterplot! func = UnicodePlots.scatterplot!
series_kw = (; marker = series[:markershape]) series_kw = (; marker = series[:markershape])
else else
error("Series type $st not supported by UnicodePlots") error("Plots(UnicodePlots): series type $st not supported")
end end
label = addlegend ? series[:label] : "" label = addlegend ? series[:label] : ""
@ -162,7 +190,7 @@ function addUnicodeSeries!(
) )
end end
return up up
end end
# ------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------
@ -198,7 +226,7 @@ end
Base.show(plt::Plot{UnicodePlotsBackend}) = show(stdout, plt) Base.show(plt::Plot{UnicodePlotsBackend}) = show(stdout, plt)
Base.show(io::IO, plt::Plot{UnicodePlotsBackend}) = _show(io, MIME("text/plain"), plt) Base.show(io::IO, plt::Plot{UnicodePlotsBackend}) = _show(io, MIME("text/plain"), plt)
# NOTE: _show(..) must be kept for Base.showable (src/output.jl) # NOTE: _show(...) must be kept for Base.showable (src/output.jl)
function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend}) function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
unicodeplots_rebuild(plt) unicodeplots_rebuild(plt)
nr, nc = size(plt.layout) nr, nc = size(plt.layout)
@ -222,7 +250,7 @@ function _show(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
for c in 1:nc for c in 1:nc
l = plt.layout[r, c] l = plt.layout[r, c]
if l isa GridLayout && size(l) != (1, 1) if l isa GridLayout && size(l) != (1, 1)
@error "UnicodePlots: complex nested layout is currently unsupported !" @error "Plots(UnicodePlots): complex nested layout is currently unsupported"
else else
if get(l.attr, :blank, false) if get(l.attr, :blank, false)
lines_colored[r, c] = lines_uncolored[r, c] = nothing lines_colored[r, c] = lines_uncolored[r, c] = nothing

View File

@ -1289,7 +1289,6 @@ _backend_skips = Dict(
6, # embedded images unsupported 6, # embedded images unsupported
16, # nested layout unsupported 16, # nested layout unsupported
21, # custom markers unsupported 21, # custom markers unsupported
22, # contours unsupported
24, # 3D unsupported 24, # 3D unsupported
26, # nested layout unsupported 26, # nested layout unsupported
27, # polar plots unsupported 27, # polar plots unsupported