This page looks best with JavaScript enabled

Data Visualization with Makie

 ·  ☕ 22 min read  ·  👽 PolyMax

Makie.jl is the front end package that defines all plotting functions required to create plot objects. To convert these plot objects to an image, three main back ends which concretely implement all abstract ren-dering capabilities defined in Makie.

  • CairoMakie.j: a non-interactive 2D publication-quality vector graphics.
  • GLMakie.j: a interactive 2D and 3D plot-ting in standalone GLFW.jl windows.
  • WGLMakie.jl: a WebGL-based interactive 2D and 3D plotting that runs within browsers.

CairoMakie.jl

1
2
3
4
5
6
# save("filename.pdf", fig; pt_per_unit=2) 
# save("filename.png", fig; px_per_unit=0.5) 
using CairoMakie
CairoMakie.activate!()

fig = scatterlines(1:10, 1:10)

png

Attributes

1
2
fig, ax, pltobj = scatterlines(1:10) 
pltobj.attributes 
Attributes with 15 entries:
color => RGBA{Float32}(0.0,0.447059,0.698039,1.0)
colormap => viridis
colorrange => Automatic()
cycle => [:color]
inspectable => true
linestyle => nothing
linewidth => 1.5
marker => circle
markercolor => Automatic()
markercolormap => viridis
markercolorrange => Automatic()
markersize => 12
model => Float32[1.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0; 0.0 0.0 1.0 0.0; 0.0 0.0 0.0 1.0]
strokecolor => black
strokewidth => 0
1
2
3
4
5
lines(1:10, (1:10).^2; color = :black, linewidth = 2, linestyle = :dash, 
      figure = (; figure_padding = 5, resolution = (600, 400), font = "sans", backgroundcolor = :grey90,
                fontsize = 16),
      axis = (; xlabel = "x", ylabel = "x²", title = "title", xgridstyle = :dash, ygridstyle = :dash))
current_figure() 

png

1
2
3
4
5
6
7
lines(1:10, (1:10).^2; label = "x²", linewidth = 2, linestyle = nothing,
      figure = (; figure_padding = 5, resolution = (600, 400), font = "sans", backgroundcolor = :grey90, 
                fontsize = 16),
      axis=(; xlabel = "x", title = "title", xgridstyle = :dash, ygridstyle = :dash)) 
scatterlines!(1:10, (10:-1.0:1).^2; label = "Reverse(x)²") 
axislegend("legend"; position = :ct)
current_figure() 

png

1
2
3
4
5
6
7
set_theme!(; resolution = (600, 400), backgroundcolor = (:orange, 0.5), fontsize = 16, font="sans",
           Axis = (backgroundcolor = :grey90, xgridstyle = :dash, ygridstyle = :dash),
           Legend = (bgcolor = (:red, 0.2), framecolor = :dodgerblue)) 
lines(1:10, (1:10).^2; label = "x²", linewidth = 2, linestyle = nothing, axis = (; xlabel = "x", title = "title")) 
scatterlines!(1:10, (10:-1.0:1).^2; label = "Reverse(x)²")
axislegend("legend"; position = :ct) 
current_figure()

png

1
2
3
4
5
using Random: seed!

seed!(28)
xyvals = randn(100, 3)
xyvals[1:5, :]
5×3 Matrix{Float64}:
  0.550992   1.27614    -0.659886
 -1.06587   -0.0287242   0.175126
 -0.721591  -1.84423     0.121052
  0.801169   0.862781   -0.221599
 -0.340826   0.0589894  -1.76359
1
2
3
4
5
6
7
8
9
set_theme!()  # reset the default settings of Makie

fig, ax, pltobj = scatter(xyvals[:, 1], xyvals[:, 2]; color = xyvals[:, 3], label = "Bubbles",
                          colormap = :plasma, markersize = 15 * abs.(xyvals[:, 3]), 
                          figure = (; resolution = (600, 400)), axis = (; aspect = DataAspect()))
limits!(-3, 3, -3, 3)
Legend(fig[1, 2], ax, valign = :top)
Colorbar(fig[1, 2], pltobj, height = Relative(3 / 4))
fig

png

Themes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
seed!(123)
y =cumsum(randn(6, 6), dims = 2) 

seed!(13)
xv = yv = LinRange(-3, 0.5, 20)
matrix =randn(20, 20) 

function demo_themes(y, xv, yv, matrix)
    fig, _ = series(y; labels = ["$i" for i = 1:6], markersize=10, color=:Set1, figure = (; resolution=(600, 300)), 
                    axis = (; xlabel = "time(s)", ylabel = "Amplitude", title = "Measurements"))
    hmap = heatmap!(xv, yv, matrix; colormap = :plasma)
    limits!(-3.1, 8.5, -6, 5.1) 
    axislegend("legend"; merge = true)
    Colorbar(fig[1, 2], hmap) 
    fig 
end 

with_theme(theme_dark()) do 
    demo_themes(y, xv, yv, matrix) 
end

png

1
2
3
with_theme(theme_black()) do
    demo_themes(y, xv, yv, matrix)
end

png

1
2
3
with_theme(theme_ggplot2()) do 
    demo_themes(y, xv, yv, matrix) 
end 

png

1
2
3
with_theme(theme_minimal()) do 
    demo_themes(y, xv, yv, matrix) 
end 

png

1
2
3
with_theme(theme_light()) do 
    demo_themes(y, xv, yv, matrix)
end 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
publication_theme() = Theme(fontsize = 16, font = "CMU Serif", 
                            Axis = (xlabelsize = 20, xgridstyle = :dash, ygridstyle = :dash,
                                    xtickalign = 1, ytickalign = 1, yticksize = 10,
                                    xticksize = 10, xlabelpadding = -5, xlabel = "x", ylabel = "y"),
                            Legend = (framecolor = (:black, 0.5), bgcolor = (:white, 0.5)),
                            Colorbar = (ticksize = 16, tickalign = 1, spinewidth = 0.5), )

function plot_with_legend_and_colorbar()
    fig, ax, _ = scatterlines(1:10; label = "line")
    hm = heatmap!(ax, LinRange(6, 9, 15), LinRange(2, 5, 15), randn(15, 15); colormap = :Spectral_11)
    axislegend("legend"; position = :lt)
    Colorbar(fig[1, 2], hm, label = "values")
    ax.title = "my custom theme" 
    fig 
end 

with_theme(plot_with_legend_and_colorbar, publication_theme()) 

png

If something needs to be changed after set_theme!(your_theme), we can do it with update_theme!(resolution=(500, 400), fontsize=18), for example. Another approach will be to pass additional arguments to the with_theme function:

1
2
3
4
5
6
fig = (resolution = (600, 400), figure_padding = 1, backgroundcolor = :grey90)
ax = (; aspect = DataAspect(), xlabel = L"x", ylabel = L"y")
cbar = (; height = Relative(4 / 5))
with_theme(publication_theme(); fig..., Axis = ax, Colorbar = cbar) do
    plot_with_legend_and_colorbar() 
end 

png

Using LaTexStrings.jl

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
using LaTeXStrings

function LaTeX_Strings()
    x = 0:0.05:4π 
    lines(x, x -> sin(3x) / (cos(x) + 2) / x; label = L"\frac{\sin(3x)}{x(\cos(x)+2)}",
          figure = (; resolution = (600, 400)), axis = (; xlabel = L"x"))
    lines!(x, x -> cos(x) / x; label = L"\cos(x)/x")
    lines!(x, x -> exp(-x); label = L"e^{−x}")
    limits!(-0.5, 13, -0.6, 1.05)
    axislegend(L"f(x)") 
    current_figure()
end

with_theme(LaTeX_Strings, publication_theme()) 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function multiple_lines()
    x = collect(0:10)
    fig = Figure(resolution = (600, 400), font = "CMU Serif")
    ax = Axis(fig[1, 1], xlabel = L"x", ylabel = L"f(x,a)")
    for i = 0:10
        lines!(ax, x, i .* x; label = latexstring("$(i) x")) 
    end 
    axislegend(L"f(x)"; position = :lt, nbanks = 2, labelsize = 14)
    text!(L"f(x,a) = ax", position = (4, 80)) 
    fig 
end 

multiple_lines() 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
function multiple_scatters_and_lines()
    x = collect(0:10)
    cycle = Cycle([:color, :linestyle, :marker], covary=true)
    set_theme!(Lines = (cycle = cycle,), Scatter = (cycle = cycle,))
    fig = Figure(resolution = (600, 400), font = "CMU Serif")
    ax = Axis(fig[1, 1], xlabel = L"x", ylabel = L"f(x,a)")
    for i in x 
        lines!(ax, x, i .* x; label = latexstring("$(i) x"))
        scatter!(ax, x, i .* x; markersize = 13, strokewidth = 0.25, label = latexstring("$(i) x"))
    end 
    axislegend(L"f(x)"; merge = true, position = :lt, nbanks = 2, labelsize = 14) 
    text!(L"f(x,a) = ax", position = (4, 80)) 
    set_theme!() # reset to default theme
    fig 
end 

multiple_scatters_and_lines() 

png

Colors and Corlormaps

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
function set_colors_and_cycle()
    # Epicycloid lines
    x(r, k, θ) = r * (k .+ 1.0) .* cos.(θ) .- r * cos.((k .+ 1.0) .* θ)
    y(r, k, θ) = r * (k .+ 1.0) .* sin.(θ) .- r * sin.((k .+ 1.0) .* θ)
    θ = LinRange(0, 6.2π, 1000)
    axis = (; xlabel = L"x(\theta)", ylabel = L"y(\theta)", title = "Epicycloid", aspect = DataAspect())
    figure = (; resolution=(600, 400), font="CMU Serif")
    fig, ax, _ = lines(x(1, 1, θ), y(1, 1, θ); color = "firebrick1", label = L"1.0", axis = axis, figure = figure)
    lines!(ax, x(4, 2, θ), y(4, 2, θ); color = :royalblue1, label = L"2.0")
    for k = 2.5:0.5:5.5 
            lines!(ax, x(2k, k, θ), y(2k, k, θ); label = latexstring("$(k)")) #cycle
    end 
    Legend(fig[1, 2], ax, latexstring("k, r = 2k"), merge = true) 
    fig 
end 
    
set_colors_and_cycle() 

png

1
2
3
4
5
6
7
figure = (; resolution = (600, 400), font = "CMU Serif")
axis = (; xlabel = L"x", ylabel = L"y", aspect = DataAspect())
fig, ax, pltobj = heatmap(rand(20, 20); colorrange = (0, 1), colormap = Reverse(:viridis), axis = axis, 
                          figure = figure) 
Colorbar(fig[1, 2], pltobj, label = "Reverse colormap Sequential")
colsize!(fig.layout, 1, Aspect(1, 1.0))
fig 

png

1
2
3
4
5
6
7
8
9
using ColorSchemes 

figure = (; resolution = (600, 400), font = "CMU Serif")
axis = (; xlabel = L"x", ylabel = L"y", aspect = DataAspect())
fig, ax, pltobj = heatmap(randn(20, 20); colorrange = (-2, 2), colormap = "diverging_rainbow_bgymr_45_85_c67_n256",
                          highclip = :black, lowclip = :white, axis = axis, figure = figure)
Colorbar(fig[1, 2], pltobj, label = "Diverging colormap")
colsize!(fig.layout, 1, Aspect(1, 1.0))
fig 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
using Colors, ColorSchemes 

figure = (; resolution = (600, 400), font = "CMU Serif")
axis = (; xlabel = L"x", ylabel = L"y", aspect = DataAspect())
# cmap = ColorScheme(range(colorant"red", colorant"green", length = 3))
mycmap = ColorScheme([RGB{Float64}(i, 1.5i, 2i) for i in [0.0, 0.25, 0.35, 0.5]])
fig, ax, pltobj = heatmap(rand(-1:1, 20, 20);
                          colormap = cgrad(mycmap, 3, categorical = true, rev = true), # cgrad and Symbol, mygrays,
                          axis = axis, figure = figure)
cbar = Colorbar(fig[1, 2], pltobj, label = "Categories") 
cbar.ticks = ([-0.66, 0, 0.66], ["negative", "neutral", "positive"])
colsize!(fig.layout, 1, Aspect(1, 1.0))
fig 

png

1
2
3
4
5
6
7
8
figure = (; resolution = (600, 400), font = "CMU Serif")
axis = (; xlabel = L"x", ylabel = L"y", aspect = DataAspect())
fig, ax, pltobj = heatmap(rand(20, 20); colorrange = (0, 1), colormap = ["red", "black"], axis = axis,
                          figure = figure)
scatter!(ax, [11], [11], color = ("#C0C0C0", 0.5), markersize = 150)
Colorbar(fig[1, 2], pltobj, label = "2 colors")
colsize!(fig.layout, 1, Aspect(1, 1.0))
fig 

png

Custom cycle

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function new_cycle_theme()
    # https://nanx.me/ggsci/reference/pal_locuszoom.html 
    my_colors = ["#D43F3AFF", "#EEA236FF", "#5CB85CFF", "#46B8DAFF", "#357EBDFF", "#9632B8FF", "#B8B8B8FF"]
    cycle = Cycle([:color, :linestyle, :marker], covary = true) # alltogether
    my_markers = [:circle, :rect, :utriangle, :dtriangle, :diamond, :pentagon, :cross, :xcross]
    my_linestyle = [nothing, :dash, :dot, :dashdot, :dashdotdot] 
    Theme(fontsize = 16, font = "CMU Serif", colormap = :linear_bmy_10_95_c78_n256,
          palette = (color = my_colors, marker = my_markers, linestyle = my_linestyle),
          Lines = (cycle = cycle,), Scatter = (cycle = cycle,),
                   Axis = (xlabelsize = 20, xgridstyle = :dash, ygridstyle = :dash,
                           xtickalign = 1, ytickalign = 1, yticksize = 10, xticksize = 10,
                           xlabelpadding = -5, xlabel = "x", ylabel = "y"),
                   Legend = (framecolor = (:black, 0.5), bgcolor = (:white, 0.5)),
                   Colorbar = (ticksize = 16, tickalign = 1, spinewidth = 0.5))
end

function scatters_and_lines()
    x = collect(0:10)
    xh = LinRange(4, 6, 25)
    yh = LinRange(70, 95, 25)
    h = randn(25, 25)
    fig = Figure(resolution = (600, 400), font = "CMU Serif")
    ax = Axis(fig[1, 1], xlabel = L"x", ylabel = L"f(x,a)")
    for i in x
        lines!(ax, x, i .* x; label = latexstring("$(i) x"))
        scatter!(ax, x, i .* x; markersize = 13, strokewidth = 0.25, label = latexstring("$(i) x"))
    end
    hm = heatmap!(xh, yh, h)
    axislegend(L"f(x)"; merge = true, position = :lt, nbanks = 2, labelsize = 14)
    Colorbar(fig[1, 2], hm, label = "new default colormap")
    limits!(ax, -0.5, 10.5, -5, 105)
    colgap!(fig.layout, 5)
    fig
end

with_theme(scatters_and_lines, new_cycle_theme())

png

Layouts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function first_layout()
    seed!(123)
    x, y, z = randn(6), randn(6), randn(6)
    fig = Figure(resolution = (600, 400), backgroundcolor = :grey90)
    ax = Axis(fig[1, 1], backgroundcolor = :white) 
    pltobj = scatter!(ax, x, y; color = z, label="scatters")
    lines!(ax, x, 1.1y; label = "line")
    Legend(fig[2, 1:2], ax, "labels", orientation = :horizontal)
    Colorbar(fig[1, 2], pltobj, label = "colorbar")
    fig
end 

first_layout() 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
function first_layout_fixed()
    seed!(123)
    x, y, z = randn(6), randn(6), randn(6)
    fig = Figure(figure_padding = (0, 3, 5, 2), resolution = (600, 400), backgroundcolor = :grey90,
                 font = "CMU Serif")
    ax = Axis(fig[1, 1], xlabel = L"x", ylabel = L"y", title = "Layout example", backgroundcolor = :white)
    pltobj = scatter!(ax, x, y; color = z, label = "scatters")
    lines!(ax, x, 1.1y, label = "line")
    Legend(fig[2, 1:2], ax, "Labels", orientation = :horizontal, tellheight = true, titleposition = :left) 
    Colorbar(fig[1, 2], pltobj, label = "colorbar")
    # additional aesthetics 
    Box(fig[1, 1, Right()], color = (:slateblue1, 0.35)) 
    Label(fig[1, 1, Right()], "protrusion", fontsize = 18, rotation = pi / 2, padding = (3, 3, 3, 3)) 
    Label(fig[1, 1, TopLeft()], "(a)", fontsize = 18, padding = (0, 3, 8, 0)) 
    colgap!(fig.layout, 5) 
    rowgap!(fig.layout, 5) 
    fig 
end 

first_layout_fixed() 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function complex_layout_double_axis()
    seed!(123) 
    x = LinRange(0, 1, 10)
    y = LinRange(0, 1, 10)
    z = rand(10, 10) 
    fig = Figure(resolution = (600, 400), font = "CMU Serif", backgroundcolor = :grey90)
    ax1 = Axis(fig, xlabel = L"x", ylabel=L"y") 
    ax2 = Axis(fig, xlabel = L"x")
    heatmap!(ax1, x, y, z; colorrange = (0, 1))
    series!(ax2, abs.(z[1:4, :]); labels = ["lab $i" for i = 1:4], color = :Set1_4) 
    hm = scatter!(10x, y; color = z[1, :], label= "dots", colorrange = (0, 1))
    hideydecorations!(ax2, ticks = false, grid = false) 
    linkyaxes!(ax1, ax2) 
    #layout
    fig[1, 1] = ax1 
    fig[1, 2] = ax2 
    Label(fig[1, 1, TopLeft()], "(a)", textsize = 18, padding = (0, 6, 8, 0)) 
    Label(fig[1, 2, TopLeft()], "(b)", textsize = 18, padding = (0, 6, 8, 0)) 
    Colorbar(fig[2, 1:2], hm, label = "colorbar", vertical = false, flipaxis = false) 
    Legend(fig[1, 3], ax2, "Legend") 
    colgap!(fig.layout, 5) 
    rowgap!(fig.layout, 5) 
    fig
end 

complex_layout_double_axis() 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function squares_layout()
    seed!(123)
    letters = reshape(collect('a':'d'), (2, 2)) 
    fig = Figure(resolution = (600, 400), fontsize = 14, font = "CMU Serif", backgroundcolor = :grey90) 
    axs = [Axis(fig[i, j], aspect = DataAspect()) for i = 1:2, j = 1:2]
    hms = [heatmap!(axs[i, j], randn(10, 10), colorrange = (-2, 2)) for i = 1:2, j = 1:2] 
    Colorbar(fig[1:2, 3], hms[1], label = "colorbar") 
    [Label(fig[i, j, TopLeft()], "($(letters[i, j]))", textsize = 16, padding = (-2, 0, -20, 0)) for i = 1:2, j = 1:2] 
    colgap!(fig.layout, 5)
    rowgap!(fig.layout, 5)
    fig
end

squares_layout() 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using Dates

function mixed_mode_layout()
    seed!(123)
    longlabels = ["$(today()  Day(1))", "$(today())", "$(today() + Day(1))"]
    fig = Figure(resolution = (600, 400), fontsize = 12, backgroundcolor = :grey90, font = "CMU Serif")
    ax1 = Axis(fig[1, 1])
    ax2 = Axis(fig[1, 2], xticklabelrotation = pi / 2, alignmode = Mixed(bottom = 0), xticks = ([1, 5, 10],
               longlabels))
    ax3 = Axis(fig[2, 1:2])
    ax4 = Axis(fig[3, 1:2]) 
    axs = [ax1, ax2, ax3, ax4]
    [lines!(ax, 1:10, rand(10)) for ax in axs]
    hidexdecorations!(ax3; ticks = false, grid = false)
    Box(fig[2:3, 1:2, Right()], color = (:slateblue1, 0.35))
    Label(fig[2:3, 1:2, Right()], "protrusion", rotation = pi / 2, textsize = 14, padding = (3, 3, 3, 3))
    Label(fig[1, 1:2, Top()], "Mixed alignmode", textsize = 16, padding = (0, 0, 15, 0))
    colsize!(fig.layout, 1, Auto(2)) 
    rowsize!(fig.layout, 2, Auto(0.5))
    rowsize!(fig.layout, 3, Auto(0.5)) 
    rowgap!(fig.layout, 1, 15) 
    rowgap!(fig.layout, 2, 0)
    colgap!(fig.layout, 5)
    fig
end

mixed_mode_layout() 

png

Nested Axis (subplots)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
function nested_sub_plot!(fig)
    color = rand(RGBf)
    ax1 = Axis(fig[1, 1], backgroundcolor = (color, 0.25))
    ax2 = Axis(fig[1, 2], backgroundcolor = (color, 0.25))
    ax3 = Axis(fig[2, 1:2], backgroundcolor = (color, 0.25)) 
    ax4 = Axis(fig[1:2, 3], backgroundcolor = (color, 0.25)) 
    return (ax1, ax2, ax3, ax4)
end

function main_figure() 
    fig = Figure()
    Axis(fig[1, 1])
    nested_sub_plot!(fig[1, 2])
    nested_sub_plot!(fig[1, 3])
    nested_sub_plot!(fig[2, 1:3])
    fig
end

main_figure() 

png

Nested GridLayout

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
function nested_Grid_Layouts() 
    fig = Figure(backgroundcolor = RGBf(0.96, 0.96, 0.96))
    ga = fig[1, 1] = GridLayout() 
    gb = fig[1, 2] = GridLayout() 
    gc = fig[1, 3] = GridLayout() 
    gd = fig[2, 1:3] = GridLayout()
    gA = Axis(ga[1, 1]) 
    nested_sub_plot!(gb) 
    axsc = nested_sub_plot!(gc)
    nested_sub_plot!(gd) 
    [hidedecorations!(axsc[i], grid = false, ticks = false) for i = 1:length(axsc)] 
    colgap!(gc, 5)
    rowgap!(gc, 5)
    rowsize!(fig.layout, 2, Auto(0.5)) 
    colsize!(fig.layout, 1, Auto(0.5))
    fig
end

nested_Grid_Layouts() 

png

Inset plots

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function add_box_inset(fig; left = 100, right = 250, bottom = 200, top = 300, bgcolor = :grey90)
    inset_box = Axis(fig, bbox = BBox(left, right, bottom, top), xticklabelsize = 12, yticklabelsize = 12,
                     backgroundcolor = bgcolor)
    # bring content upfront 
    translate!(inset_box.scene, 0, 0, 10) 
    elements = keys(inset_box.elements) 
    filtered = filter(ele -> ele != :xaxis && ele != :yaxis, elements) 
    foreach(ele -> translate!(inset_box.elements[ele], 0, 0, 9), filtered) 
    return inset_box 
end 

function figure_box_inset()
    fig = Figure(resolution = (650, 400))
    ax = Axis(fig[1, 1], backgroundcolor = :white)
    inset_ax1 = add_box_inset(fig; left = 100, right = 250, bottom = 200, top = 300, bgcolor = :grey90)
    inset_ax2 = add_box_inset(fig; left = 500, right = 600, bottom = 100, top = 200, bgcolor = (:white, 0.65))
    lines!(ax, 1:10)
    lines!(inset_ax1, 1:10)
    scatter!(inset_ax2, 1:10, color = :black)
    fig
end

figure_box_inset() 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function add_axis_inset(; pos = fig[1, 1], halign = 0.1, valign = 0.5,width = Relative(0.5),
                        height = Relative(0.35), bgcolor = :lightgray)
    inset_box = Axis(pos, width = width, height = height, halign = halign, valign = valign,
                     xticklabelsize = 12, yticklabelsize = 12, backgroundcolor = bgcolor)
    # bring content upfront
    translate!(inset_box.scene, 0, 0, 10)
    return inset_box
end

function figure_axis_inset()
    fig = Figure(resolution = (600, 400))
    ax = Axis(fig[1, 1], backgroundcolor = :white)
    inset_ax1 = add_axis_inset(; pos = fig[1, 1], halign = 0.1, valign = 0.65, width=Relative(0.3),
                               height = Relative(0.3), bgcolor = :grey90)
    inset_ax2 = add_axis_inset(; pos = fig[1, 1], halign = 1, valign = 0.25, width = Relative(0.25),
                               height = Relative(0.3), bgcolor = (:white, 0.65))
    lines!(ax, 1:10)
    lines!(inset_ax1, 1:10)
    scatter!(inset_ax2, 1:10, color = :black)
    fig
end

figure_axis_inset() 

png

GLMakie.jl

Scatters and Lines

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
using GLMakie
GLMakie.activate!() 

function scatters_in_3D()
    seed!(123)
    xyz = randn(10, 3)
    x, y, z = xyz[:, 1], xyz[:, 2], xyz[:, 3]
    fig = Figure(resolution = (1200, 400))
    ax1 = Axis3(fig[1, 1]; aspect = (1, 1, 1), perspectiveness = 0.5)
    ax2 = Axis3(fig[1, 2]; aspect = (1, 1, 1), perspectiveness = 0.5) 
    ax3 = Axis3(fig[1, 3]; aspect = :data, perspectiveness = 0.5) 
    scatter!(ax1, x, y, z; markersize = 50) 
    meshscatter!(ax2, x, y, z; markersize = 0.25)
    hm = meshscatter!(ax3, x, y, z; markersize = 0.25, marker = FRect3D(Vec3f0(0), Vec3f0(1)),
                      color = 1:size(xyz)[2], colormap = :plasma, transparency = false) 
    Colorbar(fig[1, 4], hm, label = "values", height = Relative(0.5))
    fig
end

scatters_in_3D() 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
function lines_in_3D()
    seed!(123)
    xyz = randn(10, 3)
    x, y, z = xyz[:, 1], xyz[:, 2], xyz[:, 3]
    fig = Figure(resolution = (1300, 400))
    ax1 = Axis3(fig[1, 1]; aspect = (1, 1, 1), perspectiveness = 0.5)
    ax2 = Axis3(fig[1, 2]; aspect = (1, 1, 1), perspectiveness = 0.5)
    ax3 = Axis3(fig[1, 3]; aspect = :data, perspectiveness = 0.5)
    lines!(ax1, x, y, z; color = 1:size(xyz)[2], linewidth = 3) 
    scatterlines!(ax2, x, y, z; markersize = 50) 
    hm = meshscatter!(ax3, x, y, z; markersize = 0.2, color = 1:size(xyz)[2]) 
    lines!(ax3, x, y, z; color = 1:size(xyz)[2])
    Colorbar(fig[2, 1], hm; label="values", height = 15, vertical = false, flipaxis = false,
             ticksize = 15, tickalign = 1, width = Relative(3.55 / 4)) 
    fig
end 

lines_in_3D()    

png

Surfaces, wireframe, contour, contourf and contour3d

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function peaks(; n = 49)
    x = LinRange(-3, 3, n)
    y = LinRange(-3, 3, n)
    a = 3 * (1 .- x') .^ 2 .* exp.(-(x' .^ 2) .- (y .+ 1) .^ 2)
    b = 10 * (x' / 5 .- x' .^ 3 .- y .^ 5) .* exp.(-x' .^ 2 .- y .^ 2)
    c = 1 / 3 * exp.(-(x' .+ 1) .^ 2 .- y .^ 2)
    return (x, y, a .- b .- c)
end


function plot_peaks_function()
    x, y, z = peaks()
    x2, y2, z2 = peaks(; n = 15)
    fig = Figure(resolution = (1300, 400), fontsize = 26)
    axs = [Axis3(fig[1, i]; aspect = (1, 1, 1)) for i = 1:3]
    hm = surface!(axs[1], x, y, z)
    wireframe!(axs[2], x2, y2, z2)
    contour3d!(axs[3], x, y, z; levels = 20)
    Colorbar(fig[1, 4], hm, height = Relative(0.5))
    fig
end

plot_peaks_function() 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function heatmap_contour_and_contourf()
    x, y, z = peaks()
    fig = Figure(resolution = (1300, 400), fontsize = 26)
    axs = [Axis(fig[1, i]; aspect = DataAspect()) for i = 1:3]
    hm = heatmap!(axs[1], x, y, z)
    contour!(axs[2], x, y, z; levels = 20)
    contourf!(axs[3], x, y, z)
    Colorbar(fig[1, 4], hm, height = Relative(0.5))
    fig
end

heatmap_contour_and_contourf() 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function heatmap_contour_and_contourf_in_a_3d_plane()
    x, y, z = peaks()
    fig = Figure(resolution = (1600, 400), fontsize = 26)
    axs = [Axis3(fig[1, i]) for i = 1:3]
    hm = heatmap!(axs[1], x, y, z)
    contour!(axs[2], x, y, z; levels = 20)
    contourf!(axs[3], x, y, z)
    Colorbar(fig[1, 4], hm, height = Relative(0.5))
    fig
end

heatmap_contour_and_contourf_in_a_3d_plane() 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 using TestImages 

 function mixing_surface_contour3d_contour_and_contourf()
    img = testimage("coffee.png")
    x, y, z = peaks()
    cmap = :Spectral_11
    fig = Figure(resolution = (1200, 600), fontsize = 26)
    ax1 = Axis3(fig[1, 1]; aspect = (1, 1, 1), elevation=pi / 6, xzpanelcolor=(:black, 0.75), perspectiveness = 0.5,
                yzpanelcolor = :black, zgridcolor = :grey70, ygridcolor = :grey70, xgridcolor = :grey70)
    ax2 = Axis3(fig[1, 3]; aspect = (1, 1, 1), elevation = pi / 6, perspectiveness = 0.5) 
    hm = surface!(ax1, x, y, z; colormap = (cmap, 0.95), shading = true) 
    contour3d!(ax1, x, y, z .+ 0.02; colormap = cmap, levels = 20, linewidth = 2) 
    xmin, ymin, zmin = minimum(ax1.finallimits[]) 
    xmax, ymax, zmax = maximum(ax1.finallimits[])
    contour!(ax1, x, y, z; colormap = cmap, levels = 20, transformation = (:xy, zmax))
    contourf!(ax1, x, y, z; colormap = cmap, transformation = (:xy, zmin))
    Colorbar(fig[1, 2], hm, width = 15, ticksize = 15, tickalign = 1, height = Relative(0.35)) 
    # transformations into planes 
    heatmap!(ax2, x, y, z; colormap = :viridis, transformation = (:yz, 3.5)) 
    contourf!(ax2, x, y, z; colormap = :CMRmap, transformation = (:xy, -3.5)) 
    contourf!(ax2, x, y, z; colormap = :bone_1, transformation = (:xz, 3.5)) 
    image!(ax2, -3 .. 3, -3 .. 2, rotr90(img); transformation = (:xy, 3.8))
    xlims!(ax2, -3.8, 3.8) 
    ylims!(ax2, -3.8, 3.8) 
    zlims!(ax2, -3.8, 3.8) 
    fig
end 

mixing_surface_contour3d_contour_and_contourf()

png

Arrows and Streamplots

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
using LinearAlgebra 

function arrows_and_streamplot_in_3d()
    ps = [Point3f0(x, y, z) for x = -3:1:3 for y = -3:1:3 for z = -3:1:3]
    ns = map(p -> 0.1 * rand() * Vec3f0(p[2], p[3], p[1]), ps)
    lengths = norm.(ns)
    flowField(x, y, z) = Point(-y + x * (-1 + x^2 + y^2)^2, x + y * (-1 + x^2 + y^2)^2, z + x * (y - z^2))
    fig = Figure(resolution = (1200, 600), fontsize = 26) 
    axs = [Axis3(fig[1, i]; aspect =( 1, 1, 1), perspectiveness = 0.5) for i = 1:2]
    arrows!(axs[1], ps, ns, color = lengths, arrowsize = Vec3f0(0.2, 0.2, 0.3), linewidth = 0.1)
    streamplot!(axs[2], flowField, -4 .. 4, -4 .. 4, -4 .. 4, colormap = :plasma, gridsize = (7, 7),
                arrow_size = 0.25, linewidth = 1)
    fig
end

arrows_and_streamplot_in_3d() 

png

Meshes and Volumes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using GeometryBasics 

function mesh_volume_contour()
    # mesh objects
    rectMesh = FRect3D(Vec3f0(-0.5), Vec3f0(1))
    recmesh = GeometryBasics.mesh(rectMesh) 
    sphere = Sphere(Point3f0(0), 1) 
    # https://juliageometry.github.io/GeometryBasics.jl/stable/primitives/ 
    spheremesh = GeometryBasics.mesh(Tesselation(sphere, 64)) 
    # uses 64 for tesselation, a smoother sphere 
    colors = [rand() for v in recmesh.position] 
    # cloud points for volume 
    x = y = z = 1:10 
    vals = randn(10, 10, 10) 
    fig = Figure(resolution = (1200, 400)) 
    axs = [Axis3(fig[1, i]; aspect = (1, 1, 1), perspectiveness = 0.5) for i = 1:3]
    mesh!(axs[1], recmesh; color = colors, colormap = :rainbow, shading = false) 
    mesh!(axs[1], spheremesh; color = (:white, 0.25), transparency = true)
    volume!(axs[2], x, y, z, vals; colormap = Reverse(:plasma)) 
    contour!(axs[3], x, y, z, vals; colormap = Reverse(:plasma)) 
    fig 
end 

mesh_volume_contour() 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
using GeometryBasics, ColorSchemes

seed!(123) 
spheresGrid = [Point3f0(i, j, k) for i in 1:2:10 for j in 1:2:10 for k in 1:2:10] 
colorSphere = [RGBA(i * 0.1, j * 0.1, k * 0.1, 0.75) for i in 1:2:10 for j in 1:2:10 for k in 1:2:10] 
spheresPlane = [Point3f0(i, j, k) for i in 1:2.5:20 for j in 1:2.5:10 for k in 1:2.5:4] 
cmap = get(colorschemes[:plasma], LinRange(0, 1, 50))
colorsPlane = cmap[rand(1:50,50)] 
rectMesh = FRect3D(Vec3f0(-1, -1, 2.1), Vec3f0(22, 11, 0.5)) 
recmesh = GeometryBasics.mesh(rectMesh) 
colors = [RGBA(rand(4)...) for v in recmesh.position] 

svg

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function grid_spheres_and_rectangle_as_plate()
    fig = with_theme(theme_dark()) do 
        fig = Figure(resolution = (1200, 600))
        ax1 = Axis3(fig[1, 1]; aspect = :data, perspectiveness = 0.5, azimuth = 0.72) 
        ax2 = Axis3(fig[1, 2]; aspect = :data, perspectiveness = 0.5) 
        meshscatter!(ax1, spheresGrid; color = colorSphere, markersize = 1, shading = false) 
        meshscatter!(ax2, spheresPlane; color = colorsPlane, markersize = 0.75,
                     lightposition = Vec3f0(10, 5, 2), ambient = Vec3f0(0.95, 0.95, 0.95), backlight = 1.0f0) 
        mesh!(recmesh; color = colors, colormap = :rainbow, shading = false) 
        limits!(ax1, 0, 10, 0, 10, 0, 10)
        fig 
    end
    fig 
end 

grid_spheres_and_rectangle_as_plate() 

png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
x, y, z = peaks(; n = 15) 
δx = (x[2] - x[1]) / 2 
δy = (y[2] - y[1]) / 2 
cbarPal = :Spectral_11 
ztmp = (z .- minimum(z)) ./ (maximum(z .- minimum(z))) 
cmap = get(colorschemes[cbarPal], ztmp) 
cmap2 = reshape(cmap, size(z)) 
ztmp2 = abs.(z) ./ maximum(abs.(z)) .+ 0.15 

function histogram_or_bars_in_3d() 
    fig= Figure(resolution = (1200, 600), fontsize = 26)
    ax1 = Axis3(fig[1, 1]; aspect = (1, 1, 1), elevation = π/6, perspectiveness= 0.5)
    ax2 = Axis3(fig[1, 2]; aspect = (1, 1, 1), perspectiveness = 0.5)
    rectMesh = FRect3D(Vec3f0(-0.5, -0.5, 0), Vec3f0(1, 1, 1)) 
    meshscatter!(ax1, x, y, 0 * z, marker = rectMesh, color = z[:], markersize = Vec3f0.(2δx, 2δy, z[:]),
                 colormap = :Spectral_11, shading = false) 
    limits!(ax1, -3.5, 3.5, -3.5, 3.5, -7.45, 7.45) 
    meshscatter!(ax2, x, y, 0 * z, marker = rectMesh, color = z[:], markersize = Vec3f0.(2δx, 2δy, z[:]),
                 colormap = (:Spectral_11, 0.25), shading = false, transparency = true) 
    for (idx, i) in enumerate(x), (idy, j) in enumerate(y) 
        rectMesh = FRect3D(Vec3f0(i - δx, j - δy, 0), Vec3f0(2δx, 2δy, z[idx, idy])) 
        recmesh = GeometryBasics.mesh(rectMesh) 
        lines!(ax2, recmesh; color = (cmap2[idx, idy], ztmp2[idx, idy])) 
    end 
    fig 
end

histogram_or_bars_in_3d()

png

Filled Line and Band

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function filled_line_and_linesegments_in_3D()
    xs = LinRange(-3, 3, 10)
    lower = [Point3f0(i, -i, 0) for i in LinRange(0, 3, 100)]
    upper = [Point3f0(i, -i, sin(i) * exp(-(i + i))) for i in range(0, 3, length = 100)] 
    fig = Figure(resolution = (1200, 600)) 
    axs = [Axis3(fig[1, i]; elevation = pi/6, perspectiveness=0.5) for i = 1:2]
    band!(axs[1], lower, upper, color = repeat(norm.(upper), outer = 2), colormap = :CMRmap) 
    lines!(axs[1], upper, color = :black) 
    linesegments!(axs[2], cos.(xs), xs, sin.(xs), linewidth = 5, color = 1:length(xs))
    fig 
end

filled_line_and_linesegments_in_3D() 

png


  1. Storopoli, Huijzer and Alonso (2021). Julia Data Science. https://juliadatascience.io. ISBN: 9798489859165.
Share on

Guankui Liu
WRITTEN BY
PolyMax
My research interests include statistics, computer vision and ecological modelling.