Mesh mosaics¶
A mosaic is a custom collection of perspectives for the same scene. With ipyniivue, you use the set_slice_mosaic_string to define each tile for a lightbox view. Note that the load_meshes() command is asynchronous, so we need to use the on_mesh_loaded() to set the mosaic string after the mesh is available.
This Jupyter notebook mirrors the mesh mosaic web page.
Note Atlas slider will require upgrade to NiiVue 0.66
In [1]:
import ipywidgets as widgets
from ipyniivue import NiiVue, SliceType
nv = NiiVue(
show_3d_crosshair=True,
back_color=(1, 1, 1, 1),
)
nv.set_slice_type(SliceType.RENDER)
nv.opts.is_colorbar = True
nv.opts.show_legend = False
mesh_layers = [
{
"path": "../images/lh.curv",
"colormap": "gray",
"cal_min": 0.49,
"cal_max": 0.51,
"opacity": 0.5,
},
{
"path": "../images/boggle.lh.annot",
"opacity": 0.01,
},
{
"path": "../images/boggle.lh.annot",
"opacity": 0.01,
},
]
nv.load_meshes(
[
{
"path": "../images/lh.pial",
"layers": mesh_layers,
},
]
)
kCurvLayer = 0
kAtlasLayer = 1
kStatLayer = 2
@nv.on_mesh_loaded
def on_mesh_loaded(mesh):
nv.meshes[0].layers[kStatLayer].cal_min = 2.3
nv.meshes[0].layers[kStatLayer].cal_max = 5
nv.meshes[0].layers[kStatLayer].colormap = 'warm'
nv.meshes[0].layers[kStatLayer].colormap_negative = 'winter'
nv.meshes[0].layers[kStatLayer].atlas_values = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0, 0, 0, -5, 0, 0, 7, 0, 0, 4, 7, 0, 0, 0, 0, 0, 0, 0]
nv.set_mesh_shader(nv.meshes[0].id, "Rim")
nv.meshes[0].layers[kCurvLayer].colorbar_visible = False
nv.meshes[0].layers[kAtlasLayer].colorbar_visible = False
nv.opts.slice_mosaic_string = "A R 0 R -0 S R 0 R -0 C R 0 R -0"
nv.set_clip_plane(-0.1, 270, 0)
## User interface widgets
curv_slider = widgets.IntSlider(
value=50,
min=1,
max=99,
description="Curvature",
readout=False
)
def on_curv_change(change):
"""Set curve transparency."""
nv.set_mesh_layer_property(
mesh_id=nv.meshes[0].id,
layer_index=kCurvLayer,
attribute="opacity",
value=change["new"] * 0.01,
)
curv_slider.observe(on_curv_change, names="value")
atlas_slider = widgets.IntSlider(
value=1,
min=1,
max=99,
description="Atlas",
readout=False
)
def on_atlas_change(change):
"""Set atlas transparency."""
nv.set_mesh_layer_property(
mesh_id=nv.meshes[0].id,
layer_index=kAtlasLayer,
attribute="opacity",
value=change["new"] * 0.01,
)
atlas_slider.observe(on_atlas_change, names="value")
stat_slider = widgets.IntSlider(
value=1,
min=1,
max=99,
description="Stats",
readout=False
)
def on_stat_change(change):
"""Set stat transparency."""
nv.set_mesh_layer_property(
mesh_id=nv.meshes[0].id,
layer_index=kStatLayer,
attribute="opacity",
value=change["new"] * 0.01,
)
stat_slider.observe(on_stat_change, names="value")
border_options = ["Dark border", "Transparent border", "No border", "Opaque border"]
border_dropdown = widgets.Dropdown(
options=border_options,
value="No border",
description="Border",
)
def on_border_change(change):
"""Set mesh border style."""
value_name = change["new"]
# Default borderValue for "No border"
borderValue = 0.0
if value_name == "Dark border":
borderValue = -0.01 # MRIcroGL convention: negative = dark border
elif value_name == "Transparent border":
borderValue = 0.01 # small positive = transparent border
elif value_name == "Opaque border":
borderValue = 1.0 # fully opaque border
# else "No border" → 0.0
# Apply to the mesh layer (assuming kAtlasLayer is defined elsewhere)
nv.set_mesh_layer_property(nv.meshes[0].id, kAtlasLayer, "outline_border", borderValue)
border_dropdown.observe(on_border_change, names="value")
widgets.VBox(
[
widgets.HBox([curv_slider, stat_slider, border_dropdown]),
nv,
]
)
Out[1]:
In [ ]: