Makie | ComputeGraph code example?

Hello,

Having read the Makie ComputeGraph documentation I find it a bit abstract for me to understand without a worked example.

Searching through Julia Discourse I only found only found a few inconclusive discussions that did not provide much insight.

If someone has figured out how to use ComputeGraph I would appreciate it if they could show me how convert the following MWE to ComputeGraph for the plot, sliders, and toggles

using GLMakie
using Random

function test_lscene()
    f = Figure()

    n_rows = 10
    n_cols = 10
    n_slcs = 10

    axs_1 = LScene(
        f[1, 1:3], 
        scenekw = (;
                aspect = :data,
                backgroundcolor = :white,
                camera = cam3d!,
                center = true,
                clear = true,
                raw = false
        )
    )

    x = LinRange(1, n_rows, n_rows)
    y = LinRange(1, n_cols, n_cols)
    z = LinRange(1, n_slcs, n_slcs)

    alpha = 0.7

    plt_1 = volumeslices!(
        axs_1,
        x, y, z,
        randn(5 * n_rows, 5 * n_cols, 5 * n_slcs),
        alpha = alpha,
        bbox_visible = true,
        colormap = :thermal,
        lowclip = :transparent,
        space = :data,
        transparency = true
    )

    # Sliders
    dspl_sldr_grid = SliderGrid(
        f[2, 2],
        (label = "Sagittal - yz plane", range = 1:length(x)),
        (label = "Coronal - xz plane", range = 1:length(y)),
        (label = "Axial - xy plane", range = 1:length(z)),
    )

    dspl_sldr_layout = dspl_sldr_grid.layout
    dspl_sldr_n_cols = ncols(dspl_sldr_layout)

    # Connect sliders to volumeslices! update methods
    dspl_sl_yz, dspl_sl_xz, dspl_sl_xy = dspl_sldr_grid.sliders

    # Sagittal slider
    on(dspl_sl_yz.value) do v; 
        plt_1[:update_yz][](v)
    end

    # Coronal slider
    on(dspl_sl_xz.value) do v; 
        plt_1[:update_xz][](v) 
    end

    # Axial slider
    on(dspl_sl_xy.value) do v;
        plt_1[:update_xy][](v)
    end

    set_close_to!(dspl_sl_yz, .5length(x))
    set_close_to!(dspl_sl_xz, .5length(y))
    set_close_to!(dspl_sl_xy, .5length(z))

    # Toggles
    # Add toggles to show/hide heatmaps
    hmap_1 = [plt_1[Symbol(:heatmap_, s)][] for s ∈ (:yz, :xz, :xy)]

    toggles_dspl = [Toggle(dspl_sldr_layout[i, dspl_sldr_n_cols + 1], active = true) for i ∈ 1:length(hmap_1)]

    map(zip(hmap_1, toggles_dspl)) do (h, t)
        on(t.active) do active
            h.visible = active
        end
    end

    resize_to_layout!(f)

    wait(display(f))
end

function main()
  test_lscene() 
end

begin
    main()
end

ComputeGraph in Makie is primarily an internal mechanism for defining custom plot recipes. It describes how a plot type’s input attributes flow through computation stages to produce rendered output. It’s not the idiomatic tool for wiring up UI interactions like sliders and toggles. If you were writing a new @recipe macro-defined plot type and needed to declare how input observables are transformed into plotting primitives, which is where Makie’s ComputeGraph infrastructure lives, then you’d be looking at the right use case.

In the MWE, consider the making data flow more declarative by replacing some on() callbacks with @lift / map where you’re deriving an observable from another. Instead of on() + imperative mutation, lift where you derive values (not applicable to update_yz since it’s a side-effectful call, not a derived value), For the toggles

 # More idiomatic to directly connect observable to attribute
map(zip(hmap_1, toggles_dspl)) do (h, t)
    connect!(h.visible, t.active)   # instead of on() + assignment
end

So, for wiring plots to the UI, you’re already using the right approach.

Thank you for your explanation.

Regarding the toggles, I replaced the current toggle code

    map(zip(hmap_1, toggles_dspl)) do (h, t)
        on(t.active) do active
            h.visible = active
        end
    end

with your suggested code

    map(zip(hmap_1, toggles_dspl)) do (h, t)
        connect!(h.visible, t.active)   
    end

which generated the following error message when run:

julia> include("test_LScene_3D.jl")
ERROR: LoadError: MethodError: no method matching connect!(::ComputePipeline.Computed, ::Observable{Any})
The function `connect!` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  connect!(::Observables.AbstractObservable, ::Observables.AbstractObservable)
   @ Observables C:\Users\Audrius Stundzia\.julia\packages\Observables\YdEbO\src\Observables.jl:545
  connect!(::Transformation, ::Transformation; connect_func, connect_model)
   @ Makie C:\Users\Audrius Stundzia\.julia\packages\Makie\WKgwk\src\layouting\transformation.jl:16

The issue seems to be that t.active is an Observable, whereas h.visible is not: typeof(h.visible) = ComputePipeline.Computed

In the case of the sliders, wrapping their values as Observables seems straightforward:

    dspl_sl_yz_obs = Observable(dspl_sl_yz.value)
    # Etc.
    
    # Sagittal slider
    on(to_value(dspl_sl_yz_obs)) do v; 
        plt_1[:update_yz][](v)
    end

    # Etc.

and works.