[ANN] Makie Update: Figures And Integrated Layouts

Hey everybody,

For the last months Simon and I have been working on streamlining the integration of MakieLayout in the Makie ecosystem. Now, with AbstractPlotting.jl v0.15, we finally have made some bigger changes to our default plotting pipeline to make the features of MakieLayout more easily accessible and reduce boilerplate by a lot.

The biggest change is that where you had to write this before to use MakieLayout’s more powerful axis:

scene, layout = layoutscene()
ax = layout[1, 1] = LAxis(scene)
scatter!(ax, randn(100, 2))
scene

you can now just write this, as MakieLayout’s axis is the default now

scatter(randn(100, 2))

We’ve introduced a Figure object which bundles more of the functionality which was scattered around layoutscene and other helper functions. You can actually directly plot into nested positions in a figure, so the need to manually create GridLayouts is reduced. For example, you could create a subplot in a nested gridlayout with the command scatter(fig[1, 2][1, 1:2], randn(100, 2)). This would mean first row, second column, then in the nested grid first row, first to second column. You don’t have to create any intermediate gridlayouts yourself.

Also, we’ve renamed almost all of the LObjects and cleaned up some older cruft. LAxis => Axis, LButton => Button, LSlider => Slider, etc.

There’s still a lot to do, so we appreciate it if you check out the new features and let us know if you find any bugs! You only need to install one of the backends CairoMakie, GLMakie or WGLMakie, everything you need is imported with those. (Makie.jl is just an old GLMakie bundle by now and will probably be replaced soon)

Before you go and try it, here’s a quick demo figure where you can check out some of the new syntax. I did this static example with CairoMakie, but check out GLMakie and WGLMakie for interactivity if you haven’t, yet:

using CairoMakie
using AbstractPlotting.ColorSchemes

function demo()
    xs = LinRange(0.5, 6, 50)
    ys = LinRange(0.5, 6, 50)
    data1 = [sin(x^1.5) * cos(y^0.5) for x in xs, y in ys] .+ 0.1 .* randn.()
    data2 = [sin(x^0.8) * cos(y^1.5) for x in xs, y in ys] .+ 0.1 .* randn.()

    f = Figure(resolution = (1400, 1100), font = "Avenir Light")

    ax, co = contourf(f[1, 1][1, 1], xs, ys, data1, levels = 6,
        axis = (title = "Pyramidal Cells", ylabel = "Coronal Section"))
    contour!(xs, ys, data1, color = :black)

    contourf(f[1, 1][1, 2], xs, ys, data2, levels = 6, axis = (title = "Layer IV Neurons",))
    contour!(xs, ys, data2, color = :black)
    hidedecorations!.([ax, current_axis()])
    ax.ylabelvisible = true

    f[1, 1][1, 1:2, Bottom()] = Label(f, "Sagittal Section", padding = (0, 0, 0, 10))
    f[1, 1][2, 1:2] = Colorbar(f, height = 20, vertical = false, label = "Spike Rate",
        flipaxisposition = false, ticklabelalign = (:center, :top))

    _, sc1 = scatter(f[1, 2][1, 1], randn(100, 2) * [1 5; 3 1],
        color = :red, colorrange = (1, 10),
        axis = (title = "Particle Simulation", xlabel = "Velocity [m/s]",
            ylabel = "Acceleration [m/s²]"))
    sc2 = scatter!(randn(100, 2) * [0.5 -5; 1 0.1],
        color = :blue, colorrange = (1, 10), marker = 'x')

    f[1, 2][1, 2] = Legend(f, [sc1, sc2], ["Gamma", "Beta"], "Particles", framevisible = false)

    f[2, :] = Box(f, color = :gray90)
    f[2, :] = Label(f, "Group Measurements", padding = (0, 0, 5, 5))

    for group in 1:3
        f[3, :][1, group, Top()] = Box(f, color = :gray90)
        f[3, :][1, group, Top()] = Label(f, "Group $group", padding = (0, 0, 5, 5))
        for i in 1:3, j in 1:3
            f[3, :][1, group][i, j] = Axis(f, xticks = LinearTicks(4))
            i < 3 && hidexdecorations!(current_axis(), grid = false)
            j > 1 && hideydecorations!(current_axis(), grid = false)
            for n in 1:3
                lines!(0..10, cumsum(randn(1000)), color = ColorSchemes.Set1_4[n])
            end
        end
    end

    f[0, :] = Label(f, "Makie Complex Plot Demo", textsize = 30)
    
    f
end
86 Likes

That’s good news :+1: Lately I have been using MakieLayout regularly. I think it makes it very easy to assemble these beautiful plots. I am looking forward to trying it out

4 Likes

Awesome news! What’s the latest on Makie recipes? AbstractPlotting seems like a pretty big dependency.

5 Likes

This is awesome! Thanks to all for the hard work, Makie and MakieLayout have been a godsend for my work. I look forward to trying out the updated package.

What’s the latest tutorial on WGLMakie + JSServe?

2 Likes

Where can I find out information of how to update a package that used layoutscene to Figure?

The tutorial seems to still use the old scene and axis: http://makie.juliaplots.org/stable/basic-tutorial.html even at stable version 0.15. The MakieLayout tutorial still uses layoutscene instead of the new Figure: http://makie.juliaplots.org/stable/makielayout/tutorial.html#Scene-and-Layout

Is Figure identical, and a replacement to layout returned from layoutscene? and if yes, then where is the actual scene that one needs to pass to save?

That’s weird, the 0.15 docs should be updated. There was some weirdness with tagging that might have messed this up, I’ll check.

I checked and it was a tagbot issue, docs are now current (although there’s still a lot to update)

Figure is layout and top scene together, allowing for a better user interface and less room for error. But layoutscene should still work

1 Like

I’m actually preparing one right now, with much better Pluto support! :slight_smile:

Where can I find out information of how to update a package that used layoutscene to Figure ?

I think @jules wanted to update the tutorial soon! But I actually want to make it quite clear, that this is more of a alpha release, with things still being a bit in flux - if you have a big code base using Makie, you may want to wait a bit with an upgrade.
Meanwhile, for an upgrade guide, I’d look at the diff of upgrading the tests (test/ReferenceTests/src/tests/*):

It’s usually something like this:

scene = plot(...)   -> figure, axis, plotobject = plot(...)
plot!(scene, ...)   -> plot!(axis, ...)
scene = Scene()     -> figure = Figure()
8 Likes

Wow amazing job! I love the new syntax!

1 Like

Hi, thank you very much! Lots of progress here, and I see that interaction also will be included.
It seems I just get working what I need - the flexible layout switching works (with one glitch I might report later).
I wrapped a submodule around it.

I am mixing 2d and 3d, so I am curious what the the plans for LScene are. Will its successor also have title + additional user interaction ?

Congratulations! This looks great!

@jules Could you amend the demo such that the colorbar matches the filled contours? I tried Colorbar(f, co, ...), and Colorbar(f, ax, ...) but these don’t work, so not sure how to do that.

(Edit: Ideally, I would use it with ax and have it match both the contourf and contour.)

2 Likes

I second this. I couldn’t figure out how to do a colorbar when I was just using a single layout[1,1] because I didn’t need an iteration/loop as a result.

I would like to add a more feature rich 3D axis as well, but it is not a direct priority. LScene is just a stop-gap solution because it uses the normal scene as it was before.

1 Like

I have to take a look, basically filled contours work a bit differently than your usual heatmap so the old quick solution for mapping plot object to colorbar doesn’t work with them. I think this actually needs a rethink for continuous / categorical colormaps because you’d want the colorbar to show only the existing levels, which might not be equally spaced like in a linear colormap.

2 Likes

I’ve come to the conclusion, that dependency less recipes are at their core a serialization problem.
In other words, if you want to create Makie plots without any Makie library loaded, it’s essentially boils down to the same problem as storing a Makie plot specification to disk, since it has similar constraints.
So the current battle plan is:

  1. Make any scene serializable and loadable from disk
  2. Create a small Package for writing & storing this format
  3. Create a high level API, to create this format with code, that looks more like normal Makie plot commands
  4. Integrate it into conversion pipeline
  5. Profit!

Quite some time ago, I talked with @davidanthoff and the Plots.jl devs at vizcon, to make this format universal to all plotting libraries, so once specified in this format it could get loaded by most Julia plotting library. But I’m not sure, if I will have the capacity to do this :wink: If I get the chance to implement this, I’d just do a 1:1 mapping to Makie types - which then of course others could still target!

12 Likes

I use the following pattern to avoid dependencies on plotting packages: I just pass the plot module as keyword parameters to my plotting commands. So I can write something like

myplot(grid, Plotter=GLMakie)

This also helps to support different plotting modules:

myplot(grid, Plotter=PyPlot)

Dispatching is done heuristically by looking for Plotter.gcf or Plotter.AbstractPlotting etc. This also allows to pass CairoMakie or WGLMakie instead of GLMakie.

2 Likes

Was today a bad time to take my first look at Makie? Neither the first example in the tutorial nor the above demo() function work for me.

julia> using GLMakie
julia> using AbstractPlotting.ColorSchemes
julia> function demo()
...
end

julia> demo()
ERROR: UndefVarError: Figure not defined
Stacktrace:
 [1] demo() at ./REPL[10]:7
 [2] top-level scope at REPL[11]:1

julia> versioninfo()
Julia Version 1.5.1
Commit 697e782ab8 (2020-08-25 20:08 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i7-3960X CPU @ 3.30GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-9.0.1 (ORCJIT, sandybridge)

(@v1.5) pkg> status GLMakie
Status `~/.julia/environments/v1.5/Project.toml`
  [e9467ef8] GLMakie v0.1.2
2 Likes

If something like this happens, please always check the Versions, most importantly of AbstractPlotting!
You need GLMakie@0.1.23 AbstractPlotting@0.15.1

3 Likes

Is the Makie package itself obsolete now? It seems to be blocking the AbstractPlotting upgrade:

(@v1.5) pkg> add AbstractPlotting@0.15.1
  Resolving package versions...
ERROR: Unsatisfiable requirements detected for package Makie [ee78f7c6]:
 Makie [ee78f7c6] log:
 ├─possible versions are: [0.9.0-0.9.6, 0.10.0, 0.11.0-0.11.2] or uninstalled
 ├─restricted to versions * by an explicit requirement, leaving only versions [0.9.0-0.9.6, 0.10.0, 0.11.0-0.11.2]
 └─restricted by compatibility requirements with AbstractPlotting [537997a7] to versions: uninstalled — no versions left
   └─AbstractPlotting [537997a7] log:
     ├─possible versions are: [0.9.0-0.9.27, 0.10.0-0.10.11, 0.11.0-0.11.2, 0.12.0-0.12.18, 0.13.0-0.13.11, 0.14.0-0.14.4, 0.15.0-0.15.1] or uninstalled
     └─restricted to versions 0.15.1 by an explicit requirement, leaving only versions 0.15.1

Currently, yes. It’s just a bundle of GLMakie and AbstractPlotting, so it tends to fall behind because we have to keep pushing its compat bounds. But because all backends reexport AbstractPlotting now, there’s no need for Makie anymore as there was at the beginning. We might rename AbstractPlotting to Makie at some point now that Makie.jl is useless.

1 Like

Has anyone been successful precompiling a system image with these new updates?