Makie | interactive update of colorrange in image! / heatmap!

Hello,

I am attempting to do an interactive update of colorrange in image! of the form

image!( . . . colorrange = (window_level_min, window_level_max), . . . )

using input from two Sliders.

window_level_min and window_level_max are both declared to be Observables (Nodes)
and are connected to the output from the two Sliders as

connect!(window_level_min, slider_window_level_min.value)
connect!(window_level_max, slider_window_level_max.value)

Moving the Sliders updates window_level_min and window_level_max
but does not update image!.

How may I implement this correctly?

Code fragment.

using ModernGL
using GLMakie

using Colors
using PerceptualColourMaps

. . .

window_level_min_start = window_centre - 0.5f0 * window_width
window_level_max_start = window_centre + 0.5f0 * window_width

window_level_min = Observable(window_level_min_start)
window_level_max = Observable(window_level_max_start)

. . .

slider_window_level_min = 
    Slider(
        window_2D_level_sliders_grid_layout[1, 1][2, 2], 
        range = LinRange(voxel_scalar_min:0.1:voxel_scalar_max),
        startvalues = window_level_min_start,
        color_active = :orangered2,
        halign = :center,
        linewidth = 10.0)

slider_window_level_min_label_text = 
    lift(slider_window_level_min.value) do value
        string(round.(value, digits = 5), modality_units)

    end

connect!(window_level_min, slider_window_level_min.value)

# Max window level slider is analogous

. . .

#
# 2D image slice displays
#

i_lin_range = LinRange(1, size(image_vlm, 1), size(image_vlm, 1))
j_lin_range = LinRange(1, size(image_vlm, 2), size(image_vlm, 2))
k_lin_range = LinRange(1, size(image_vlm, 3), size(image_vlm, 3))

# 2D axial display
axis_2D_axial = 
        Axis(
            axial_grid_layout[1, 1],
            aspect = AxisAspect(spatial_extent[2] / spatial_extent[1]),
            backgroundcolor = :black)

hidedecorations!.(axis_2D_axial)

axis_start_index = size(image_vlm, 3) ÷ 2
axis_slice_index = Observable(axis_start_index)

axialSliceImage = @lift(image_vlm[:, :, $axis_slice_index])

image!(
    i_lin_range, 
    j_lin_range, 
    axialSliceImage,
    color = :black,
    colormap = colour_map,
    colorrange = (window_level_min, window_level_max),
    interpolate = true,
    ssao = false,
    transparency = false)

You need to make an observable with one tuple, currently you’re making a tuple with two observables.

You could also check out IntervalSlider, sounds like that would make sense for a colorrange, and it already has the output as a tuple.

Got it.

Good suggestion. Implemented.

Code fragment.

interval_slider_window_level = 
    IntervalSlider(
        window_2D_level_sliders_grid_layout[1, 1][2, 2], 
        range = LinRange(voxel_scalar_min:0.1:voxel_scalar_max),
        startvalues = (window_level_min_start, window_level_max_start),
        color_active = :orangered2,
        halign = :center,
        linewidth = 10.0)
		
. . .

image!(
    i_lin_range, 
    j_lin_range, 
    axialSliceImage,
    color = :black,
    colormap = colour_map,
    colorrange = interval_slider_window_level.interval,
    interpolate = true,
    ssao = false,
    transparency = false)

The now obvious bit that I was missing was that interval_slider_window_level.interval from IntervalSlider is an Observable.

Thank for your reply and suggestions.