Mesh Layers¶

This Python script demonstrates showing meshes with overlays. Notice that the cortical mesh has a reduced opacity, allowing you to visualize the underlying subcortical mesh.

The interactive controls allow you to explore different methods to observe meshes that might be obscured by other methods. One can use a shader like outline to hide parts of a mesh, adjust transparency, or use the XRay method.

This script also highlights an important feature of ipyniivue: image and mesh loading occurs asynchronously. As a result, the load_meshes() function returns before the dataset is fully ready, meaning we can not set properties for a mesh before it is created. To correctly configure the mesh, an on_mesh_loaded() callback is used. This event triggers once the mesh has finished loading and is ready for use. Although this asynchronous behavior is uncommon in typical Python workflows, it ensures the web interface remains responsive during file loading.

In [1]:
from ipyniivue import NiiVue
import ipywidgets as widgets

nv = NiiVue(
    show_3d_crosshair=True, back_color=(1, 1, 1, 1), is_colorbar=True
)
nv.opts.show_legend = False

mesh_layer = {
    "path": "../images/BrainMesh_ICBM152.lh.motor.mz3",
    "cal_min": 0.5,
    "cal_max": 5.5,
    "use_negative_cmap": True,
    "opacity": 0.7,
}

nv.load_meshes(
    [
        {
           "path": "../images/BrainMesh_ICBM152.lh.mz3",
           "layers": [mesh_layer],
        },
        {
            "path": "../images/CIT168.mz3",
        },

    ]
)

@nv.on_mesh_loaded
def on_mesh_loaded(volume):
    nv.meshes[1].colorbar_visible = False

## User interface

slider_opacity = widgets.IntSlider(min=1, max=10, value=7, description="Opacity", readout=False)

def on_opacity_change(change):
    """Set mesh layer opacity."""
    nv.set_mesh_layer_property(nv.meshes[0].id, 0, "opacity", change["new"] * 0.1)


slider_opacity.observe(on_opacity_change, names="value")

slider_xray = widgets.IntSlider(min=0, max=30, value=0, description="XRay", readout=False)

def on_xray_change(change):
    """Set mesh xray."""
    nv.opts.mesh_xray = change["new"] * 0.01


slider_xray.observe(on_xray_change, names="value")

shader_options = nv.mesh_shader_names()

shader_dropdown = widgets.Dropdown(
    options=shader_options,
    value="Phong",  # Default shader
    description="Shader:",
)


def on_shader_change(change):
    """Set mesh layer shader."""
    nv.set_mesh_shader(nv.meshes[0].id, change["new"])


shader_dropdown.observe(on_shader_change, names="value")

alpha_options = ["Opaque", "Translucent", "Transparent"]

alpha_dropdown = widgets.Dropdown(
    options=alpha_options,
    value="Opaque",
    description="Opacity:",
)


def on_alpha_change(change):
    """Set mesh layer shader."""
    idx = 0
    value = change["new"]
    if (value == "Opaque"):
        nv.meshes[idx].opacity = 1.0
    if (value == "Translucent"):
        nv.meshes[idx].opacity = 0.2
    if (value == "Transparent"):
        nv.meshes[idx].opacity = 0.0
    # TODO: draw_scene causes error if no voxels loaded
    # nv.draw_scene()
    nv.set_mesh_shader(nv.meshes[0].id, shader_dropdown.value)

alpha_dropdown.observe(on_alpha_change, names="value")

controls = widgets.HBox([slider_opacity, slider_xray, shader_dropdown, alpha_dropdown])

## Display user interface and meshes

display(controls)
nv
Out[1]:
In [ ]:
 
In [ ]: