Skip to content

JuliaPlots/UnicodePlots.jl

Repository files navigation

UnicodePlots

License PkgEval CI Coverage Status JuliaHub deps UnicodePlots Downloads

AdvancedUnicodeplotting library designed for use inJulia'sREPL.

High-level Interface

Here is a list of the main high-level functions for common scenarios:

Introduction

Here is a quick hello world example of a typical use-case:

usingUnicodePlots
lineplot([-1,2,3,7], [-1,2,9,4], title="Example",name="my line",xlabel="x",ylabel="y")


There are other types ofCanvasavailable (see sectionLow-level Interface).

In some situations, such as printing to a file, usingAsciiCanvas,DotCanvasorBlockCanvasmight lead to better results:

plt=lineplot([-1,2,3,7], [-1,2,9,4], title="Example",name="my line",
xlabel="x",ylabel="y",canvas=DotCanvas, border=:ascii)


Some plot methods have a mutating variant that ends with an exclamation mark:

lineplot!(plt, [0,4,8], [10,1,10], color=:cyan,name="other line")


These mutating methods cannot update the limits of the axes as plots are drawn onto a fixed canvas. The limits must be set beforehand by the plotting function that creates the figure or by creating an emptyPlot:

p=Plot(; xlim=(-1,3), ylim=(-1,3))
lineplot!(p,1:2)


One can adjust the plotheightandwidthto the current terminal size by usingheight=:autoand/orwidth=:auto.

You can reverse/flip thePlotaxes by settingxflip=trueand/oryflip=trueon plot creation.

Lineplot
lineplot([1,2,7], [9,-6,8], title="My Lineplot")


It's also possible to specify a function and a range:

plt=lineplot(-π/2,2π, [cos, sin])


You can also plot lines by specifying an intercept and slope:

lineplot!(plt,-.5,.2,name="line")


Plotting multiple series is supported by providing aMatrix(<: AbstractMatrix) for theyargument, with the individual series corresponding to its columns. Auto-labeling is by default, but you can also label each series by providing aVectoror a1xnMatrixsuch as[ "series 1" "series2"...]:

lineplot(1:10,[0:93:12reverse(5:14)fill(4,10)], color=[:green:red:yellow:cyan])


Physical quantities ofUnitful.jlare supported throughpackage extensions - weak dependencies:

usingUnitful
a, t=1u"m/s^2",(0:100)*u"s"
lineplot(a/2*t.^2,a*t, xlabel="position",ylabel="speed",height=10)


Intervals fromIntervalSets.jlare supported:

usingIntervalSets
lineplot(-1..3,x->x^5-5x^4+5x^3+5x^2-6x-1;name="quintic")


Usehead_tailto mimic plotting arrows (:head,:tailor:both) where the length of the "arrow" head or tail is controlled usinghead_tail_fracwhere e.g. giving a value of0.1means10%of the segment length:

lineplot(1:10,1:10,head_tail=:head,head_tail_frac=.1,height=4)


UnicodePlotsexportshline!andvline!for drawing vertical and horizontal lines on a plot:

p=Plot([NaN], [NaN]; xlim=(0,8), ylim=(0,8))
vline!(p, [2,6], [2,6], color=:red)
hline!(p, [2,6], [2,6], color=:white)
hline!(p,7,color=:cyan)
vline!(p,1,color=:yellow)


Scatterplot
scatterplot(randn(50),randn(50), title="My Scatterplot")


Axis scaling (xscaleand/oryscale) is supported: choose from (:identity,:ln,:log2,:log10) or use an arbitrary scale function:

scatterplot(1:10,1:10,xscale=:log10,yscale=:log10)


For the axis scale exponent, one can revert to usingASCIIcharacters instead ofUnicodeones using the keywordunicode_exponent=false:

scatterplot(1:4,1:4,xscale=:log10,yscale=:ln,unicode_exponent=false,height=6)


Using amarkeris supported, choose aChar,a unit lengthStringor a symbol name such as:circle(more fromkeys(UnicodePlots.MARKERS)). One can also provide a vector ofmarkers and/orcolors as in the following example:

scatterplot([1,2,3], [3,4,1], marker=[:circle,'',""],
color=[:cyan,nothing,:yellow], height=2)


As withlineplot,scatterplotsupports plotting physicalUnitfulquantities, or plotting multiple series (Matrixargument).

Staircase plot
stairs([1,2,4,7,8], [1,3,4,2,7],
color=:yellow,style=:post,height=6,title="Staircase")


Barplot
barplot(["Paris","New York","Madrid"], [2.244,8.406,3.165], title="Population")


Note:You can use the keyword argumentsymbolsto specify the characters that should be used to plot the bars (e.g.symbols=['#']).

Histogram
histogram(randn(1_000).*.1,nbins=15,closed=:left)


Thehistogramfunction also supports axis scaling using the parameterxscale:

histogram(randn(1_000).*.1,nbins=15,closed=:right,xscale=:log10)


Vertical histograms are supported:

histogram(randn(100_000).*.1,nbins=60,vertical=true,height=10)


Boxplot
boxplot([1,3,3,4,6,10])


boxplot(["one","two"],
[[1,2,3,4,5], [2,3,4,5,6,7,8,9]],
title="Grouped Boxplot",xlabel="x")


Sparsity Pattern
usingSparseArrays
spy(sprandn(50,120,.05))


Plotting the zeros pattern is also possible usingshow_zeros=true:

usingSparseArrays
spy(sprandn(50,120,.9), show_zeros=true)


Density Plot
plt=densityplot(randn(10_000),randn(10_000))
densityplot!(plt,randn(10_000).+2,randn(10_000).+2)


Using a scale function (e.g. damping peaks) is supported using thedscalekeyword:

x=randn(10_000); x[1_000:6_000].=2
densityplot(x,randn(10_000); dscale=x->log(1+x))


Contour Plot
contourplot(-3:.01:3,-7:.01:3,(x, y)->exp(-(x/2)^2-((y+2)/4)^2))


The keywordlevelscontrols the number of contour levels. One can also choose acolormapas withheatmap,and disable the colorbar usingcolorbar=false.

Polar Plot

Plots data in polar coordinates withθthe angles in radians.

polarplot(range(0,2π, length=20),range(0,2,length=20))


Heatmap Plot
heatmap(repeat(collect(0:10)',outer=(11,1)), zlabel="z")


Theheatmapfunction also supports axis scaling using the parametersxfact,yfactand axis offsets after scaling usingxoffsetandyoffset.

Thecolormapparameter may be used to specify a named or custom colormap. See theheatmapfunction documentation for more details.

In addition, thecolorbarandcolorbar_borderoptions may be used to toggle the colorbar and configure its border.

Thezlabeloption andzlabel!method may be used to set thezaxis (colorbar) label.

Use thearraykeyword in order to display the matrix in the array convention (as in the repl).

heatmap(collect(0:30)*collect(0:30)',xfact=.1,yfact=.1,xoffset=-1.5,colormap=:inferno)


Image Plot

Draws an image, surround it with decorations.Sixelare supported (experimental) under a compatible terminal throughImageInTerminal(which must be imported beforeUnicodePlots).

importImageInTerminal#mandatory (triggers glue code loading)
usingTestImages
imageplot(testimage("monarch_color_256"), title="monarch")


Surface Plot

Plots a colored surface using height valueszabove ax-yplane, in three dimensions (masking values usingNaNs is supported).

sombrero(x, y)=15sinc((x^2+y^2)/π)
surfaceplot(-8:.5:8,-8:.5:8,sombrero, colormap=:jet)


Uselines=trueto increase the density (underlying call tolineplotinstead ofscatterplot,with color interpolation). By default,surfaceplotscales heights to adjust aspect wrt the remaining axes withzscale=:aspect. To plot a slice in 3D, use an anonymous function which maps to a constant value:zscale=z -> a_constant:

surfaceplot(
-2:2,-2:2,(x, y)->15sinc((x^2+y^2)/π),
zscale=z->0,lines=true,colormap=:jet
)


Isosurface Plot

UsesMarchingCubes.jlto extract an isosurface, whereisovaluecontrols the surface isovalue. Usingcentroidenables plotting the triangulation centroids instead of the triangle vertices (better for small plots). Back face culling (hide not visible facets) can be activated usingcull=true. One can use the legacy 'Marching Cubes' algorithm usinglegacy=true.

torus(x, y, z, r=0.2,R=0.5)=((x^2+y^2)-R)^2+z^2-r^2
isosurface(-1:.1:1,-1:.1:1,-1:.1:1,torus, cull=true,zoom=2,elevation=50)


Documentation

Installation
...

To install UnicodePlots, start upJuliaand type the following code snippet into theREPL(makes use of the nativeJuliapackage managerPkg):

julia>usingPkg
julia>Pkg.add("UnicodePlots")
Saving figures
...

Saving plots aspngortxtfiles using thesavefigcommand is supported (saving aspngis experimental and requiresimport FreeType, FileIObefore loadingUnicodePlots).

To recover the plot as a string with ansi color codes usestring(p; color=true).

Color mode
...

When theCOLORTERMenvironment variable is set to either24bitortruecolor,UnicodePlotswill use24bit colorsas opposed to8bit colorsor even4bit colorsfor named colors.

One can force a specific colormode using eitherUnicodePlots.truecolors!()orUnicodePlots.colors256!().

Named colors such as:redor:light_redwill use256color values (rendering will be terminal dependent). In order to force named colors to use true colors instead, useUnicodePlots.USE_LUT[]=true.

The default color cycle can be changed to bright (high intensity) colors usingUnicodePlots.brightcolors!()instead of the defaultUnicodePlots.faintcolors!().

3D plots
...

3d plots use a so-called "Model-View-Projection" transformation matrixMVPon input data to project 3D plots to a 2D screen.

Use keywordselevation,azimuth,uporzoomto control the view matrix, a.k.a. camera.

Theprojectiontype forMVPcan be set to either:persp(ective)or:ortho(graphic).

Displaying thex,y,andzaxes can be controlled using theaxes3dkeyword.

For enhanced resolution, use a wider and/or tallerPlot(this can be achieved usingdefault_size!(width=60)for all future plots).

Layout
...

UnicodePlotsis integrated inPlotsas a backend, with support forbasic layout.

For a more complex layout, use thegridplotfunction (requires loadingTermas extension).

usingUnicodePlots, Term

(
UnicodePlots.panel(lineplot(1:2))*
UnicodePlots.panel(scatterplot(rand(100)))
)/(
UnicodePlots.panel(lineplot(2:-1:1))*
UnicodePlots.panel(densityplot(randn(1_000),randn(1_000)))
)|>display

gridplot(map(i->lineplot(-i:i),1:5); show_placeholder=true)|>display
gridplot(map(i->lineplot(-i:i),1:3); layout=(2,nothing))|>display
gridplot(map(i->lineplot(-i:i),1:3); layout=(nothing,1))|>display
Known issues
...

Using a nontrue monospace fontcan lead to visual problems on aBrailleCanvas(border versus canvas).

Either change the font to e.g.JuliaMonoor useborder=:dottedkeyword argument in the plots.

For aJupyternotebook with theIJuliakernel seehere.

(Experimental) Terminals seem to respect a standard aspect ratio of4:3,hence a square matrix does not often look square in the terminal.

You can pass the experimental keywordfix_ar=truetospyorheatmapin order to recover a unit aspect ratio.

Methods (API)
...

Non-exhaustive methods description:

  • title!(plot::Plot, title::String)

    • titlethe string to write in the top center of the plot window. If the title is empty the whole line of the title will not be drawn
  • xlabel!(plot::Plot, xlabel::String)

    • xlabelthe string to display on the bottom of the plot window. If the title is empty the whole line of the label will not be drawn
  • ylabel!(plot::Plot, xlabel::String)

    • ylabelthe string to display on the far left of the plot window.

The methodlabel!is responsible for the setting all the textual decorations of a plot. It has two functions:

  • label!(plot::Plot, where::Symbol, value::String)

    • wherecan be any of::tl(top-left),:t(top-center),:tr(top-right),:bl(bottom-left),:b(bottom-center),:br(bottom-right),:l(left),:r(right)
  • label!(plot::Plot, where::Symbol, row::Int, value::String)

    • wherecan be any of::l(left),:r(right)

    • rowcan be between 1 and the number of character rows of the canvas

x=y=collect(1:10)
plt=lineplot(x, y, canvas=DotCanvas, height=10,width=30)
lineplot!(plt, x,reverse(y))
title!(plt,"Plot Title")
forlocin(:tl,:t,:tr,:bl,:b,:br)
label!(plt, loc,string(':',loc))
end
label!(plt,:l,":l")
label!(plt,:r,":r")
foriin1:10
label!(plt,:l,i,string(i))
label!(plt,:r,i,string(i))
end
plt


  • annotate!(plot::Plot, x::Number, y::Number, text::AbstractString; kw...)
    • textarbitrary annotation at position (x, y)
Keywords description (API)
...

All plots support the set (or a subset) of the following named parameters:

  • symbols::Array = ['■']:collection of characters used to render the bars.

  • title::String = "":text displayed on top of the plot.

  • name::String = "":current drawing annotation displayed on the right.

  • xlabel::String = "":text displayed on thexaxis of the plot.

  • ylabel::String = "":text displayed on theyaxis of the plot.

  • zlabel::String = "":text displayed on thezaxis (colorbar) of the plot.

  • xscale::Symbol =:identity:x-axis scale (:identity,:ln,:log2,:log10), or scale function e.g.x -> log10(x).

  • yscale::Symbol =:identity:y-axis scale.

  • labels::Bool = true:show plot labels.

    lineplot(1:.5:20,sin, labels=false)


  • border::Symbol =:solid:plot bounding box style (:corners,:solid,:bold,:dashed,:dotted,:ascii,:none).

    lineplot([-1.,2,3,7], [1.,2,9,4], canvas=DotCanvas, border=:dashed)


    lineplot([-1.,2,3,7], [1.,2,9,4], canvas=DotCanvas, border=:ascii)


    lineplot([-1.,2,3,7], [1.,2,9,4], canvas=DotCanvas, border=:bold)


    lineplot([-1.,2,3,7], [1.,2,9,4], border=:dotted)


    lineplot([-1.,2,3,7], [1.,2,9,4], border=:none)


  • margin::Int = 3:number of empty characters to the left of the whole plot.

  • padding::Int = 1:left and right space between the labels and the canvas.

  • color::Symbol =:auto:choose from (:green,:blue,:red,:yellow,:cyan,:magenta,:white,:normal,:auto), use an integer in[0-255],or provide3integers asRGBcomponents.

  • height::Int = 15:number of canvas rows, or:auto.

    lineplot(1:.5:20,sin, height=18)


  • width::Int = 40:number of characters per canvas row, or:auto.

    lineplot(1:.5:20,sin, width=60)


  • xlim::Tuple = (0, 0):plotting range for thexaxis ((0, 0)stands for automatic).

  • ylim::Tuple = (0, 0):plotting range for theyaxis.

  • zlim::Tuple = (0, 0):colormap scaled data range.

  • xticks::Bool = true:setfalseto disable ticks (labels) onx-axis.

  • yticks::Bool = true:setfalseto disable ticks (labels) ony-axis.

  • xflip::Bool = false:settrueto flip thexaxis.

  • yflip::Bool = false:settrueto flip theyaxis.

  • colorbar::Bool = false:toggle the colorbar.

  • colormap::Symbol =:viridis:choose a symbol fromColorSchemes.jle.g.:viridis,or supply a functionf: (z, zmin, zmax) -> Int(0-255),or a vector of RGB tuples.

  • colorbar_lim::Tuple = (0, 1):colorbar limit.

  • colorbar_border::Symbol =:solid:color bar bounding box style (:solid,:bold,:dashed,:dotted,:ascii,:none).

  • canvas::UnionAll = BrailleCanvas:type of canvas used for drawing.

  • grid::Bool = true:draws grid-lines at the origin.

  • compact::Bool = false:compact plot labels.

  • unicode_exponent::Bool = true:useUnicodesymbols for exponents: e.g.10²⸱¹instead of10^2.1.

  • thousands_separator::Char = ' ':thousands separator character (useChar(0)to disable grouping digits).

  • projection::Symbol =:orthographic:projection for 3D plots (:ortho(graphic),:persp(ective),orModel-View-Projection(MVP) matrix).

  • axes3d::Bool = true:draw 3d axes (x ->:red,y ->:green,z ->:blue).

  • elevation::Float = 35.264389682754654:elevation angle above or below thefloorplane (-90 ≤ θ ≤ 90).

  • azimuth::Float = 45.0:azimutal angle around theupvector (-180° ≤ φ ≤ 180°).

  • zoom::Float = 1.0:zooming factor in 3D.

  • up::Symbol =:z:up vector (:x,:yor:z), prefix withm -> -orp -> +to change the sign e.g.:mzfor-zaxis pointing upwards.

  • near::Float = 1.0:distance to the near clipping plane (:perspectiveprojection only).

  • far::Float = 100.0:distance to the far clipping plane (:perspectiveprojection only).

  • canvas_kw::NamedTuple = NamedTuple():extra canvas keywords.

  • blend::Bool = true:blend colors on the underlying canvas.

  • fix_ar::Bool = false:fix terminal aspect ratio (experimental).

  • visible::Bool = true:visible canvas.

Note:If you want to print the plot into a file but have monospace issues with your font, you should probably try settingborder=:asciiandcanvas=AsciiCanvas(orcanvas=DotCanvasfor scatterplots).

Low-level interface
...

The primary structures that do all the heavy lifting behind the curtain are subtypes ofCanvas.A canvas is a graphics object for rasterized plotting. Basically, it uses Unicode characters to represent pixel.

Here is a simple example:

importUnicodePlots:lines!, points!, pixel!
canvas=BrailleCanvas(15,40,#number of rows and columns (characters)
origin_y=0.,origin_x=0.,#position in virtual space
height=1.,width=1.)#size of the virtual space
lines!(canvas,0.,0.,1.,1.;color=:cyan)#virtual space
points!(canvas,rand(50),rand(50); color=:red)#virtual space
lines!(canvas,0.,1.,.5,0.;color=:yellow)#virtual space
pixel!(canvas,5,8;color=:red)#pixel space
Plot(canvas)


You can access the height and width of the canvas (in characters) withnrows(canvas)andncols(canvas)respectively. You can use those functions in combination withprint_rowto embed the canvas anywhere you wish. For example,print_row(STDOUT, canvas, 3)writes the third character row of the canvas to the standard output.

As you can see, one issue that arises when multiple pixel are represented by one character is that it is hard to assign color. That is because each of the "pixel" of a character could belong to a different color group (each character can only have a single color). This package deals with this using a color-blend for the whole group. You can disable canvas color blending / mi xing by passingblend=falseto any function.

importUnicodePlots:lines!
canvas=BrailleCanvas(15,40;origin_y=0.,origin_x=0.,height=1.,width=1.)
lines!(canvas,0.,0.,1.,1.;color=:cyan)
lines!(canvas,.25,1.,.5,0.;color=:yellow)
lines!(canvas,.2,.8,1.,0.;color=:red)
Plot(canvas)


The following types ofCanvasare implemented:

  • BrailleCanvas: This type of canvas is probably the one with the highest resolution forUnicodeplotting. It essentially uses the Unicode characters of theBraillesymbols as pixels. This effectively turns every character into eight pixels that can individually be manipulated using binary operations.

  • BlockCanvas: This canvas is alsoUnicodebased. It has half the resolution of the BrailleCanvas. In contrast toBrailleCanvas,the pixels don't have visible spacing between them. This canvas effectively turns every character into four pixels that can individually be manipulated using binary operations.

  • HeatmapCanvas: This canvas is alsoUnicodebased. It has half the resolution of theBlockCanvas.This canvas effectively turns every character into two color pixels, using the foreground and background terminal colors. As such, the number of rows of the canvas is half the number ofycoordinates being displayed.

  • AsciiCanvasandDotCanvas: These two canvas utilizes only standardASCIIcharacter for drawing. Naturally, it doesn't look quite as nice as the Unicode-based ones. However, in some situations it might yield better results. Printing plots to a file is one of those situations.

  • DensityCanvas: Unlike theBrailleCanvas,the density canvas does not simply mark a "pixel" as set. Instead it increments a counter per character that keeps track of the frequency of pixels drawn in that character. Together with a variable that keeps track of the maximum frequency, the canvas can thus draw the density of data-points.

  • BarplotGraphics: This graphics area is special in that it does not support any pixel manipulation. It is essentially the barplot without decorations but the numbers. It does only support one methodaddrow!which allows the user to add additional bars to the graphics object.

Developer notes
...

Because Julia uses column-major inde xing order for an array type, and because displaying data on a terminal is row based, we need an internal buffer compatible with efficient columns based iteration. We solve this by using the transpose of a (width,height) array for inde xing into an internal buffer likebuf[row, col]orbuf[y, x]. Common users of UnicodePlots don't need to be aware of this axis difference if sticking to public interface.

p=Plot([NaN], [NaN]; xlim=(1,10), ylim=(1,10), title="internal buffer conventions")

#plot axes
vline!(p,1,head_tail=:head,color=:green,name="y-axis (rows)")
hline!(p,1,head_tail=:head,color=:red,name="x-axis (cols)")

#square
vline!(p,2,[2,9], color=:cyan,name="buf[y, x] - buf[row, col]")
vline!(p, [2,9], [2,9], color=:cyan)
hline!(p, [2,9], [2,9], color=:cyan)

#internal axes
vline!(p,3,range(3,8;length=20), head_tail=:tail,color=:light_green,name="y-buffer (rows)")
hline!(p,8,range(3,8;length=20), head_tail=:head,color=:light_red,name="x-buffer (cols)")

#mem layout
vline!(p,4,[4,7]; color=:yellow,name="memory layout")
vline!(p,7,[4,7]; color=:yellow)
hline!(p, [4,7], [4,7]; color=:yellow)
hline!(p, [4.5,5,5.5,6], [4.5,6.5]; color=:yellow)


Invalidations check
...

Run the following snippet to analyze invalidations:

usingSnoopCompileCore

invalidations=@snooprusingUnicodePlots
tinf=@snoopi_deepUnicodePlots.precompile_workload()

usingSnoopCompile, AbstractTrees, PrettyTables#must occur after `invalidations`

print_tree(tinf; maxdepth=typemax(Int))

trees=invalidation_trees(invalidations)
trees=filtermod(UnicodePlots, trees; recursive=true)

@showlength(uinvalidated(invalidations))#all invalidations

#only from `UnicodePlots`
@showlength(staleinstances(tinf))
@showlength(trees)

SnoopCompile.report_invalidations(;
invalidations,
process_filename=x->last(split(x,".julia/packages/")),
n_rows=0,
)
Documentation update
...

The following snippet:

$cddocs
$ julia gen_docs.jl
$ (cd imgs;julia gen_imgs.jl)

will regenerateREADME.mdand the example images with root (prefix) urlhttps://github /JuliaPlots/UnicodePlots.jl/raw/unicodeplots-docs.

License

This code is free to use under the terms of the MIT license.

Acknowledgement

Inspired byTextPlots.jl,which in turn was inspired byDrawille.