Makie: Is there an easy way to combine several figures into a new figure without re-plotting?

I have several figures with many plots in each that I want to combine into a composite figure.
I can do this by re-plotting everything from scratch into a new figure but this is a bit inconvenient for large figures and will take time rewriting code.

Is there an easy way to combine several Makie figures into a new Makie figure?

A simple example could be the following code where I would want to draw combined_figure by extracting objects from fig1 and fig2 somehow without re-plotting using lines().

using CairoMakie

x = LinRange(0, 10, 100)
y = sin.(x)

fig1 = Figure()
lines(fig1[1, 1], x, y, color = :red)

fig2 = Figure()
lines(fig2[1, 1], x, y, color = :blue)

combined_figure = Figure()
lines(combined_figure[1, 1], x, y, color = :red)
lines(combined_figure[1, 2], x, y, color = :blue)

fig1
fig2
combined_figure

We don’t have the infrastructure currently to move or copy complex plot objects or axes from figure to figure. But you can write the logic for one of your figures so that you can pass a figure position of another figure to it and plot into that, this way you don’t have to redo any code for the figure itself.

So where you do scatter(f[1, 1]) and f is a Figure it can also be a figure position such as f[2:3, 4]. This gives you a very simple way to do nested figures.

Got it, thanks @jules!
I think I have a pretty good idea of how to use f[2:3,4] etc to make figure layouts.
Was just wondering if there’s some shortcut I’m not aware of to extract this info from figures but I guess it’s a pretty niche application and is easy to do with plotting in most cases as you suggested.

Just to add an example as I think I worded this quite confusingly:

function heatmaps(f = Figure())
    for i in 1:2, j in 1:2
        heatmap(f[i, j], rand(10, 10))
    end
    f
end

heatmaps()

function lineplots(f = Figure())
    lines(f[1:3, 1], randn(10))
    lines(f[2:3, 2], randn(10))
    lines(f[1, 2:3], randn(10))
    lines(f[2, 3], randn(10))
    lines(f[3, 3], randn(10))
    f
end

lineplots()

let
    f = Figure()
    heatmaps(f[1, 1])
    lineplots(f[1, 2][1, 1])
    heatmaps(f[1, 2][2, 1])
    f
end

7 Likes

Thank you @jules!

It turned out that I didn’t fully understand your suggestion so thank you for these examples.
This basically does exactly what I wanted!

For anyone else who might find this useful:
Essentially I can make a function from any complex figure by simply substitution fig=Figure() with function some_plot(fig=Figure()) (and adding end somewhere later of course) and then I can plot this plot into any part of any figure using using some_plot(fig[i,j]) which is exactly what I wanted in my original question. And usually only two lines of code per figure need to be changed.

3 Likes

Great, glad it was useful :slight_smile: I still find this feature pretty nifty