Creating complex graph layouts using Makie
This example demonstrates the tools of the Makie library for creating professional scientific visualizations by combining graphs of various types (scatter plots, contour plots, 3D models, and time series) into a single complex layout using nested GridLayout, linked axes, custom legends, and color scales, as well as advanced element positioning and space management techniques to create informative and colorful illustrations.
1. Package installation and data preparation
Pkg.add("Makie")
Pkg.add("CairoMakie")
Preparing data for the first chart:
seconds = 0:0.1:2
measurements = [8.2, 8.4, 6.3, 9.5, 9.1, 10.5, 8.6, 8.2, 10.5, 8.5, 7.2,
                8.8, 9.7, 10.8, 12.5, 11.6, 12.1, 12.1, 15.1, 14.7, 13.1]
using CairoMakie
2. Creating a basic schedule
Let's start with a simple visualization of experimental data and an exponential model.
Main components:
- Figure()creates a container for graphs
- Axis()defines a coordinate system with captions
- scatter!()adds measurement points
- lines!()draws the model line
- axislegend()creates a legend
Displaying a graph with experimental data and an exponential model:
f = Figure()
ax = Axis(f[1, 1],
    title = "Experimental data and exponential fit",
    xlabel = "Time (seconds)",
    ylabel = "Value",
)
CairoMakie.scatter!(
    ax,
    seconds,
    measurements,
    color = :tomato,
    label = "Measurements"
)
lines!(
    ax,
    seconds,
    exp.(seconds) .+ 7,
    color = :tomato,
    linestyle = :dash,
    label = "f(x) = exp(x) + 7",
)
# Добавление легенды в правый нижний угол
axislegend(position = :rb)
# Отображение фигуры
f
3. Forming a complex layout
Creating a complex graphic object (shape) with several areas for plotting:
- We use GridLayoutfor organizing nested grids
- We define 4 main areas (A, B, C, D)
- Adjust the background and shape size
- We connect the axes to synchronize the scales
using CairoMakie
using Makie.FileIO  # Для работы с файлами
# Активация бэкенда и создание фигуры
CairoMakie.activate!()
f = Figure(
    backgroundcolor = RGBf(0.98, 0.98, 0.98),
    size = (1000, 700)
)
# Создание вложенных сеток
ga = f[1, 1] = GridLayout()  # Область A
gb = f[2, 1] = GridLayout()  # Область B
gcd = f[1:2, 2] = GridLayout()  # Контейнер для C и D
gc = gcd[1, 1] = GridLayout()  # Область C (3D мозг)
gd = gcd[2, 1] = GridLayout()  # Область D (EEG сигналы)
# Создание связанных осей для Область A
axtop = Axis(ga[1, 1])        # Верхнее распределение
axmain = Axis(ga[2, 1],       # Основной график
    xlabel = "before", 
    ylabel = "after")
axright = Axis(ga[2, 2])      # Правое распределение
# Связывание осей для синхронизации
linkyaxes!(axmain, axright)  # Общая шкала Y
linkxaxes!(axmain, axtop)    # Общая шкала X
f
4. Area A: Scattering and distribution diagrams
Area A demonstrates:
- Grouping of data through color coding
- Related axes with common scales
- Distributions at the edges of the main graph
- Automatic legend placement
- Fine-tune the margins between the elements
# Генерация тестовых данных для трех групп
labels = ["treatment", "placebo", "control"]
data = randn(3, 100, 2) .+ [1, 3, 5]  # 3 группы × 100 точек × 2 измерения
# Визуализация каждой группы
for (label, col) in zip(labels, eachslice(data, dims = 1))
    CairoMakie.scatter!(axmain, col, label = label)
    CairoMakie.density!(axtop, col[:, 1])  # Распределение сверху
    CairoMakie.density!(axright, col[:, 2], direction = :y)  # Распределение справа
end
# Настройка границ осей
CairoMakie.ylims!(axtop, low = 0)
CairoMakie.xlims!(axright, low = 0)
# Установка меток на осях
axmain.xticks = 0:3:9
axtop.xticks = 0:3:9
# Добавление легенды и скрытие лишних элементов
leg = Legend(ga[1, 2], axmain)
hidedecorations!(axtop, grid = false)
hidedecorations!(axright, grid = false)
leg.tellheight = true  # Фиксируем высоту легенды
# Добавление заголовка области
Label(ga[1, 1:2, Top()], "Stimulus ratings", valign = :bottom,
    font = :bold,
    padding = (0, 0, 5, 0))
# Настройка расстояний между элементами
colgap!(ga, 10)
rowgap!(ga, 10)
f
5. Area B: Contour plots
Area B shows:
- Contour graphs for visualization of 2D data
- Graph hierarchy: contourf+ contour
- Custom color scale with bin marking
- Alignment of elements via Mixedalignmode
- Precise control over the distances between the graphs
# Подготовка данных для контурных графиков
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.()
# Построение контурных графиков
ax1, hm = CairoMakie.contourf(gb[1, 1], xs, ys, data1, levels = 6)
ax1.title = "Histological analysis"
CairoMakie.contour!(ax1, xs, ys, data1, levels = 5, color = :black)
hidexdecorations!(ax1)  # Скрываем метки на оси X
ax2, hm2 = CairoMakie.contourf(gb[2, 1], xs, ys, data2, levels = 6)
CairoMakie.contour!(ax2, xs, ys, data2, levels = 5, color = :black)
# Настройка цветовой шкалы
cb = CairoMakie.Colorbar(gb[1:2, 2], hm, label = "cell group")
low, high = CairoMakie.extrema(data1)
edges = range(low, high, length = 7)
centers = (edges[1:6] .+ edges[2:7]) .* 0.5
cb.ticks = (centers, string.(1:6))  # Кастомные метки
# Оптимизация выравнивания
cb.alignmode = Mixed(right = 0)
# Настройка расстояний
colgap!(gb, 10)
rowgap!(gb, 10)
f
6. Area C: 3D visualization of the brain
Area C demonstrates:
- Download and visualization of 3D models (STL format)
- Use Axis3for 3D graphs
- Color coding based on Y coordinates
- Inverted color map for better perception
- Integration of 3D visualization into the overall layout
# Загрузка и визуализация 3D модели мозга
brain = load(assetpath("brain.stl"))
ax3d = Axis3(gc[1, 1], title = "Brain activation")
m = mesh!(
    ax3d,
    brain,
    color = [tri[1][2] for tri in brain for i in 1:3],
    colormap = Reverse(:magma),  # Инвертированная цветовая карта
)
Colorbar(gc[1, 2], m, label = "BOLD level")  # Цветовая шкала
f
7. Area D: EEG signals
Area D shows:
- Arrays of axes for grouping graphs
- Generation of artificial EEG signals
- Dynamic scaling of axes by the number of points
- Rotated labels to save space
- Automatic alignment of column sizes
# Создание сетки осей для сигналов ЭЭГ
axs = [Axis(gd[row, col]) for row in 1:3, col in 1:2]
hidedecorations!.(axs, grid = false, label = false)  # Упрощаем оформление
# Генерация и визуализация ЭЭГ сигналов
for row in 1:3, col in 1:2
    xrange = col == 1 ? (0:0.1:6pi) : (0:0.1:10pi)  # Разные диапазоны
    eeg = [sum(sin(pi * rand() + k * x) / k for k in 1:10)
           for x in xrange] .+ 0.1 .* randn.()
    lines!(axs[row, col], eeg, color = (:black, 0.5))  # Полупрозрачные линии
end
# Подписи осей и заголовок
axs[3, 1].xlabel = "Day 1"
axs[3, 2].xlabel = "Day 2"
Label(gd[1, :, Top()], "EEG traces", valign = :bottom,
    font = :bold,
    padding = (0, 0, 5, 0))
# Добавление повернутых меток
for (i, label) in enumerate(["sleep", "awake", "test"])
    Box(gd[i, 3], color = :gray90)  # Фон метки
    Label(gd[i, 3], label, rotation = pi/2, tellheight = false)  # Вертикальный текст
end
# Автомасштабирование по количеству точек
n_day_1 = length(0:0.1:6pi)
n_day_2 = length(0:0.1:10pi)
colsize!(gd, 1, Auto(n_day_1))
colsize!(gd, 2, Auto(n_day_2))
# Настройка расстояний
rowgap!(gd, 10)
colgap!(gd, 10)
colgap!(gd, 2, 0)  # Убираем отступ у колонки с метками
f
8. Final settings and labels
Additional settings:
- Adding A/B/C/D labels
- Fine-tune the column proportions
- Row height balancing
- Optimization of the overall arrangement of the elements
# Добавление меток областей
for (label, layout) in zip(["A", "B", "C", "D"], [ga, gb, gc, gd])
    Label(layout[1, 1, TopLeft()], label,
        fontsize = 26,
        font = :bold,
        padding = (0, 5, 5, 0),
        halign = :right)
end
# Настройка пропорций
colsize!(f.layout, 1, Auto(0.5))  # Уменьшаем ширину первой колонки
rowsize!(gcd, 1, Auto(1.5))       # Увеличиваем высоту 3D визуализации
# Отображение финального результата
f






