There are four different types of sizes you can give rows and columns.
Fixed(scene_units) is used to set a column or row to an absolute size, independent of its content. This only really makes sense if there is variable width content in the column or row, that can shrink or expand to meet this size. You will probably not need
Fixed sizes very often.
using CairoMakie f = Figure() Axis(f[1, 1], title = "My column has size Fixed(400)") Axis(f[1, 2], title = "My column has size Auto()") colsize!(f.layout, 1, Fixed(400)) # colsize!(f.layout, 1, 400) would also work f
Relative(fraction) is used to set a column or row to a size that is a certain fraction of the available width or height. This is useful, e.g., if you want a column to span 50% of the available width, no matter what other content is there. In this case, you would use
Relative(1/2). The available width is the width of the GridLayout minus the space taken by row or column gaps including protrusions.
using CairoMakie f = Figure() Axis(f[1, 1], title = "My column has size Relative(2/3)") Axis(f[1, 2], title = "My column has size Auto()") Colorbar(f[1, 3]) colsize!(f.layout, 1, Relative(2/3)) f
Auto size is a bit more complex to understand. It has two parameters, the Boolean
trydetermine and the number
ratio. The default is
Auto() == Auto(true, 1). This is also the default row height and column width.
A column or row that is sized
Auto(true) tries to fit its own size to its content.
GridLayout is solved, it looks at the content in each row or column and checks if it reports a fixed size. Many objects can report their own width or height because their content has a specific size, such as
Label. Other objects are often used with a user-set width or height, for example
Colorbar(scene, width = 30). In this case, the
GridLayout can also see that the colorbar has a width of 30 units and fit the column width to that value. Objects like
Axis on the other hand are usually not set to a specific size.
Only objects that span a single row or column report their width or height, respectively. If multiple rows or columns are spanned, it's not well defined how the space that the object needs should be distributed.
If there is more than one object with a fixed width or height in an
Auto(true) sized column, the maximum size is used.
If a column or row is sized
Auto(false), fixed-size objects are ignored. It can also happen of course, that there is no fixed-size object in a row or column with
Auto(true) size. In both these cases, columns or rows determine their size by what remains after all
Aspect and size-inferred
Auto(true) columns or rows have been calculated. Each undetermined
Auto column gets a share of the remaining space that is proportional to its
For example, let's say there are two columns left with undetermined
Auto size when 300 units space remain. Column 1 has ratio
1 while column 2 has ratio
2. The first column will get
1 / (1 + 2) * 300 == 100 units, while the second column gets
2 / (1 + 2) * 300 == 200 units.
using CairoMakie f = Figure() Axis(f[1, 1], title = "My column infers my width\nof 200 units") Axis(f[1, 2], title = "My column gets 1/3rd\nof the remaining space") Axis(f[1, 3], title = "My column gets 2/3rds\nof the remaining space") colsize!(f.layout, 2, Auto(1)) # equivalent to Auto(true, 1) colsize!(f.layout, 3, Auto(2)) # equivalent to Auto(true, 2) f
This size is also a little bit trickier to understand. The syntax is
Aspect(reference, ratio). A column with a width of
Aspect(1, 2.0) is set to 2.0 times the height of row 1, no matter what that height is. Therefore, the grid cell at [1, 1] will always have an aspect ratio of 2 to 1. The opposite pattern applies to rows with
Aspect sized columns or rows are very useful when you want to constrain the aspect ratio of a grid cell. For example, a plot that is always supposed to be square. Enforcing the aspect on the layout level is better than setting
axis.aspect = AxisAspect(1) in most cases, because it ensures an intact layout where all grid cell borders are aligned visually. An
aspect = AxisAspect(1) on the other hand simply shrinks so it remains square, but this will break alignment with other grid content.
using CairoMakie f = Figure() Axis(f[1, 1], title = "I'm square and aligned") Box(f[1, 2], color = (:blue, 0.1), strokecolor = :transparent) Axis(f[1, 2], aspect = AxisAspect(1), title = "I'm square but break the layout.\nMy actual cell is the blue rect.") Axis(f[2, 1]) Axis(f[2, 2]) rowsize!(f.layout, 2, Relative(2/3)) colsize!(f.layout, 1, Aspect(1, 1)) f
Keep in mind that if you set too many constraints on row and column sizes, a GridLayout can easily be too big or too small. It's good to have variable-width elements to fill the remaining space if you use an element with fixed size or fixed aspect ratio.
Grids can be nested inside other grids, and so on, to arbitrary depths. The top grid's parent should be the scene in which the layout is placed. When you place a grid inside another grid, that grid is automatically made its parent. Grids also are by default set to alignmode Inside which means that the content edges are aligned to the grid's bounding box, excluding the outer protrusions. This way, plots in nested grids are nicely aligned along their spines.
using CairoMakie f = Figure() subgl_left = GridLayout() subgl_left[1:2, 1:2] = [Axis(f) for i in 1:2, j in 1:2] subgl_right = GridLayout() subgl_right[1:3, 1] = [Axis(f) for i in 1:3] f.layout[1, 1] = subgl_left f.layout[1, 2] = subgl_right f
Here you can see the difference between the align modes Outside with and without margins and the Inside alignmode. Only the standard Inside mode aligns the axis spines of the contained axes nicely. The Outside mode is mostly useful for the main GridLayout so that there some space between the window edges and the plots. You can see that the normal axis looks the same as the one placed inside the grid with Inside alignment, and they are both effectively aligned exactly the same.
using CairoMakie f = Figure(resolution = (800, 800)) Axis(f[1, 1], title = "No grid layout") Axis(f[2, 1], title = "No grid layout") Axis(f[3, 1], title = "No grid layout") Axis(f[1, 2], title = "Inside", alignmode = Inside()) Axis(f[2, 2], title = "Outside", alignmode = Outside()) Axis(f[3, 2], title = "Outside(50)", alignmode = Outside(50)) [Box(f[i, 2], color = :transparent, strokecolor = :red) for i in 1:3] f
Elements in a grid layout can span multiple rows and columns. You can specify them with the range syntax and colons for the full width or height. You can also use end to specify the last row or column.
using CairoMakie f = Figure() Axis(f[1, 1:2], title = "[1, 1:2]") Axis(f[2:4, 1:2], title = "[2:4, 1:2]") Axis(f[:, 3], title = "[:, 3]") Axis(f[1:3, 4], title = "[1:3, 4]") Axis(f[end, end], title = "[end, end]") f
If you index outside of the current range of a grid layout, you do not get an error. Instead, the layout automatically resizes to contain the new indices. This is very useful if you want to iteratively build a layout, or add super or side titles.
using CairoMakie f = Figure(resolution = (800, 800)) Axis(f[1, 1]) for i in 1:3 Axis(f[:, end+1]) Axis(f[end+1, :]) end Label(f[0, :], text = "Super Title", textsize = 50) Label(f[end+1, :], text = "Sub Title", textsize = 50) Label(f[1:end-1, 0], text = "Left Text", textsize = 50, rotation = pi/2) Label(f[1:end-1, end+1], text = "Right Text", textsize = 50, rotation = -pi/2) f
If you have rows from 1 to 3 and index into row 0, the rows now range from 0 to 3. If you then index into row -1, the rows range from -1 to 3 and so on. This behavior has changed since GridLayoutBase.jl
using CairoMakie f = Figure(resolution = (800, 800)) for i in 1:3, j in 1:3 Axis(f[i, j]) end Label(f[0, :], text = "First Supertitle", textsize = 20) Label(f[-1, :], text = "Second Supertitle", textsize = 30) Label(f[-2, :], text = "Third Supertitle", textsize = 40) f
If you change a layout interactively and end up with unused rows or columns,
trim! will remove those for you.
Here we start with two axes:
using CairoMakie f = Figure() ax1 = Axis(f[1, 1], title = "Axis 1") ax2 = Axis(f[1, 2], title = "Axis 2") f
Now we decide that we'd like the second axis better if it was below the first one. We move it two the new cell, and the old unused column is left blank.
f[2, 1] = ax2 f
We can get rid of the unused space with
You can use
colgap! to change the spacing between rows or columns respectively.
using CairoMakie fig = Figure() axs = [Axis(fig[i, j]) for i in 1:3, j in 1:3] axs[1, 1].title = "Group A" axs[1, 2].title = "Group B.1" axs[1, 3].title = "Group B.2" hidedecorations!.(axs, grid=false) colgap!(fig.layout, 1, Relative(0.15)) fig
All spaces can be changed at once by omitting the index of the gap to resize.
rowgap!(fig.layout, 50) fig