[ANN] MakieLayout.jl: A layout manager for Makie.jl

If you have used Makie.jl before, you will know that the interactivity with Observables and the GPU-powered plotting is great, but the layouting capabilities are limited and 2D axes suffer from problems like arbitrary font sizes. This package is an extension of Makie insofar that it adds its own 2D axis type that can be used together with a grid-based layout.

You can iteratively expand layouts, link axes, add super and side titles easily, and many more things. Decorations are given in pixel sizes so they all look the same. There are many small features and tweaks to allow you to create the best aligned layout that uses the available space efficiently while looking good.

The package is still in its early stages but is now registered and can be installed via the usual ] add MakieLayout.

Check out the documentation at MakieLayout.jl Tutorial · MakieLayout.jl, but to start with here are some examples:

51 Likes

This looks fantastic! I’ve been waiting a while for this, and I can’t while to play with it. Thanks a lot!

It appears that I cannot create a layout with a single column. I tried the example in the README, only changing ncols = 1 instead of ncols = 2 and got the following error:

MethodError: no method matching zero(::Type{Any})
Closest candidates are:
  zero(::Type{Union{Missing, T}}) where T at missing.jl:87
  zero(!Matched::Type{LibGit2.GitHash}) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.1/LibGit2/src/oid.jl:220
  zero(!Matched::Type{Pkg.Resolve.VersionWeights.VersionWeight}) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.1/Pkg/src/resolve/VersionWeights.jl:19
  ...

Stacktrace:
 [1] zero(::Type{Any}) at ./missing.jl:87
 [2] reduce_empty(::typeof(+), ::Type) at ./reduce.jl:227
 [3] reduce_empty(::typeof(Base.add_sum), ::Type) at ./reduce.jl:234
 [4] mapreduce_empty(::typeof(identity), ::Function, ::Type) at ./reduce.jl:251
 [5] _mapreduce(::typeof(identity), ::typeof(Base.add_sum), ::IndexLinear, ::Array{Any,1}) at ./reduce.jl:305
 [6] _mapreduce_dim at ./reducedim.jl:308 [inlined]
 [7] #mapreduce#548 at ./reducedim.jl:304 [inlined]
 [8] mapreduce at ./reducedim.jl:304 [inlined]
 [9] _sum at ./reducedim.jl:653 [inlined]
 [10] _sum at ./reducedim.jl:652 [inlined]
 [11] #sum#550 at ./reducedim.jl:648 [inlined]
 [12] sum(::Array{Any,1}) at ./reducedim.jl:648
 [13] solve(::GridLayout, ::GeometryTypes.HyperRectangle{2,Float32}) at /Volumes/Users/grogee/.julia/packages/MakieLayout/07lWR/src/layout_engine.jl:252
 [14] (::getfield(MakieLayout, Symbol("##4#6")))(::Bool) at /Volumes/Users/grogee/.julia/packages/MakieLayout/07lWR/src/types.jl:131
 [15] #setindex!#1(::getfield(Observables, Symbol("##2#4")), ::Function, ::Observables.Observable{Bool}, ::Bool) at /Volumes/Users/grogee/.julia/packages/Observables/qCJWB/src/Observables.jl:87
 [16] setindex! at /Volumes/Users/grogee/.julia/packages/Observables/qCJWB/src/Observables.jl:83 [inlined]
 [17] connectchildlayout!(::GridLayout, ::MakieLayout.SpannedLayout{ProtrusionLayout{LayoutedAxis}}) at /Volumes/Users/grogee/.julia/packages/MakieLayout/07lWR/src/layout_engine.jl:668
 [18] add_layout! at /Volumes/Users/grogee/.julia/packages/MakieLayout/07lWR/src/layout_engine.jl:644 [inlined]
 [19] setindex!(::GridLayout, ::LayoutedAxis, ::Int64, ::Int64) at /Volumes/Users/grogee/.julia/packages/MakieLayout/07lWR/src/layout_engine.jl:636
 [20] (::getfield(Main, Symbol("##49#50")))(::Tuple{Int64,Int64}) at ./none:0
 [21] iterate at ./generator.jl:47 [inlined]
 [22] collect(::Base.Generator{Base.Iterators.ProductIterator{Tuple{UnitRange{Int64},UnitRange{Int64}}},getfield(Main, Symbol("##49#50"))}) at ./array.jl:606
 [23] top-level scope at In[21]:12

Could you post the full code? I can’t reproduce this here :slight_smile: Although this error has often appeared while I was writing the package, because with one column there are no gaps to sum over… Maybe you also need to update the package again first

for me one column looks like this, which might look weird but is intended behavior, because an auto width column resizes to fixed width single-span content if it finds any. in this case you would have to set the column width to Relative(1)

Sure, this is the the code I tried. This was right after updating MakieLayout (] up MakieLayout ) to v0.1.2 (Master) an hour or so ago.

using Makie, MakieLayout
scene = Scene(resolution = (300, 400), font="SF Hello");
screen = display(scene)
campixel!(scene);

nrows = 4
ncols = 1

maingl = GridLayout(
    nrows, ncols,
    parent = scene,
    alignmode = Outside(5, 5, 5, 5))

las = [maingl[i, j] = LayoutedAxis(scene) for i in 1:nrows, j in 1:ncols]

for i in 1:nrows, j in 1:ncols

    scatter!(las[i, j], rand(200, 2) .+ [i j])

    i > 1 && (las[i, j].attributes.titlevisible = false)
    j > 1 && (las[i, j].attributes.ylabelvisible = false)
    j > 1 && (las[i, j].attributes.yticklabelsvisible = false)
    j > 1 && (las[i, j].attributes.yticksvisible = false)
    i < nrows && (las[i, j].attributes.xticklabelsvisible = false)
    i < nrows && (las[i, j].attributes.xticksvisible = false)
    i < nrows && (las[i, j].attributes.xlabelvisible = false)
end

linkxaxes!(las...)
linkyaxes!(las...)

maingl[0, :] = LayoutedText(scene, text="Super Title", textsize=6)
maingl[2:end, end+1] = LayoutedText(scene, text="Side Title", textsize=6, rotation=-pi/2)
scene

That looks exactly like what I wanted. Perhaps I didn’t manage to update everything properly. I’ll try again in a bit.

I’ve pushed my latest changes and tagged a new version, so in a bit you can install that. Let me know if the error persists. I don’t have automated tests yet as Makie doesn’t work on travis, so my tests are of a manual nature so far and are likely to miss bugs.

Hm, it seems like I still get the same error, even after updating:

julia> versioninfo()
Julia Version 1.1.1
Commit 55e36cc (2019-05-16 04:10 UTC)
Platform Info:
  OS: macOS (x86_64-apple-darwin15.6.0)
  CPU: Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-6.0.1 (ORCJIT, skylake)

(MakieLayoutTests) pkg> st
    Status `~/Documents/programming/julia/MakieLayoutTests/Project.toml`
  [ee78f7c6] Makie v0.9.5
  [5a521ce4] MakieLayout v0.1.5 #master (https://github.com/jkrumbiegel/MakieLayout.jl.git)

hmm then I assume it’s because of your Julia version, I’m on 1.3. Could be that some bug with reducing an empty collection was fixed in between? I’ll investigate a bit

can you check with the current master, not the last tagged version? I’ve just pushed a commit where I’ve taken out the sums over empty vectors, even though for some reason they don’t cause problems for me

You were right in thinking that my julia version was the source of the problem. Once I upgraded to 1.3, the error went away.

I just wanted to share my appreciation for this package. To me, this really makes Makie an excellent candidate for producing publication quality plots : )

4 Likes

Is this the way to go to keep axes fixed in place when zooming/panning?

yes, the axes from standard Makie are more like a part of the plot itself. In MakieLayout, they “contain” the plots and therefore don’t move when panning and zooming

1 Like

Is there already an example of how to transform an axis-is-data plot into an axis-is-no-data plot? I guess that’s one of the most wanted features :smiley: Thus, an example is at least a good showcase.

How to place titles above each plot?
title(las[i,j], "A nice title") doesn’t seem to work.
Especially, is there a keyword argument, that I can supply to the heatmap?

Btw what is the state of serious threading (Julia 1.3) in MakieLayout and Makie?

Point 1: You don’t have to do anything but plot into the new axis instead of the old scene. So scatter!(scene, xy) becomes scatter!(newaxis, xy). The new axis is at its core just a subscene with orthographic projection (plus bells and whistles), so it mostly works the same as the old one in terms of plotting.

Point 2: All (or most anyway) attributes of the axis and the other widgets are in obj.attributes, but getproperty is overloaded for them to access attributes with simple dot notation if they exists. So the title would be either a kwarg when creating the axis like LAxis(scene, title = "A nice title") or you set it after the fact with ax.title = "A nice title. There might be some attributes inaccessible this way right now because there are so many possibilities, it’s easy for me to miss some.

Point 3: I don’t think there’s any threading in Makie right now, MakieLayout is a layer on top so it could never just introduce its own threading. Makie is older than the new threading, so there would need to be a lot of changes I think, but @sdanisch might be on it, I don’t know.

1 Like

Looks useful, thanks!