Makie - connecting Slider to update a 2D slice [matrix] heatmap display of a 3D array

Hello,

I would like to connect a Slider to an heatmap display of a 2D slice, i.e, matrix, of a 3D array:
a3DArray[:, :, sliderValue]. such that as the Slider is moved, a new 2D slice is selected/indexed and the 2D heatmap is updated to display it.

My understanding from the documentation is that I should use “lift” to make the Slider - heatmap connection. However, it is not clear to me how I should implement it. Any insight would be appreciated.

The current code fragment:

module QntImageDisplay

using ModernGL
using GLMakie
using Observables
using PerceptualColourMaps

export static_image_display
function static_image_display(
            imageModality::String,
            imageVolume::Array{Float32, 3}, # the 3D image array
            spatialExtent::Vector{Float32},
            windowCentre::Float32,
            windowWidth::Float32)

    # . . . 

    #
    # 2D displays
    #
    windowMin::Float32 = windowCentre - 0.5 * windowWidth
    windowMax::Float32 = windowCentre + 0.5 * windowWidth

    iLinRange = LinRange(1, size(imageVolume, 1), size(imageVolume, 1))
    jLinRange = LinRange(1, size(imageVolume, 2), size(imageVolume, 2))
    kLinRange = LinRange(1, size(imageVolume, 3), size(imageVolume, 3))

    # 2D axial display
    axis2DAxial = Axis(
                     figure[1, 2][1, 1],
                     aspect = AxisAspect(spatialExtent[2]/spatialExtent[1]),
                     backgroundcolor = :gray10)

    hidedecorations!.(axis2DAxial)

    # Slider to set the number/index of the 2D slice of the 3D array
    slider2DAxial = Slider( 
                        figure[1, 2][1, 2],
                        horizontal = false,
                        linewidth = 10.0,
                        range = 1:1:size(imageVolume, 3), 
                        startvalue = size(imageVolume, 3)//2)

    axialSliceNumber = to_value(slider2DAxial.value)

    # 2D heatmap display of a slice [matrix] of the 3D array
    axialHeatMap = heatmap!(
                       iLinRange, 
                       jLinRange, 
                       imageVolume[:,  :,  axialSliceNumber], # axialSliceNumber to be set by the Slider
                       color = :gray10,
                       colormap = colourMap,
                       colorrange = (windowMin, windowMax),
                       interpolate = true,
                       ssao = false,
                       transparency = false)

    # I think I need to use lift here. If so, then how do I implement it?

    # . . .

end

end

Here’s a small example

data = randn(100, 100, 100)
z_index = Node(1)

slice = @lift(data[:, :, $z_index])

f = Figure()

heatmap(f[1, 1], slice)

sl = Slider(f[1, 2], horizontal = false, range = 1:size(data, 3))
connect!(z_index, sl.value)

f

You can also make the slider first and avoid the z_index observable, but that can be quite confusing in terms of code ordering. Usually small helper observables with clear names are better, and you can easily connect! them.

Your elegant method works well. Thank you.