@@ -74,7 +74,45 @@ def _norm(self, rng):
7474 return (self ._scalars - rng [0 ]) / factor
7575
7676
77- class _LayeredMesh :
77+ class LayeredMesh :
78+ """A mesh with support for layered RGBA overlays and optional smoothing.
79+
80+ This class manages a single brain-surface mesh and composites multiple
81+ named overlays (e.g., curvature, data, labels) on top of each other using
82+ alpha blending. It is the object stored in
83+ ``Brain.layered_meshes``.
84+
85+ .. warning::
86+ This class is not meant to be instantiated directly, and the
87+ initialization API could change at any time! Use
88+ :meth:`mne.viz.Brain.add_data` to create instances.
89+
90+ Parameters
91+ ----------
92+ renderer : instance of _Renderer
93+ The renderer used to create the underlying mesh polydata.
94+ vertices : array, shape (n_vertices, 3)
95+ Vertex coordinates in metres.
96+ triangles : array, shape (n_triangles, 3)
97+ Triangle indices into ``vertices``.
98+ normals : array, shape (n_vertices, 3)
99+ Vertex normals.
100+
101+ Attributes
102+ ----------
103+ smooth_mat : scipy.sparse.csr_array or None
104+ Optional spatial smoother / upsampler applied to scalar data before
105+ rendering. When set (e.g., by
106+ :meth:`~mne.viz.Brain.set_data_smoothing`), every call to
107+ :meth:`update_overlay` will multiply the incoming scalars by this
108+ matrix before storing them. Shape must be
109+ ``(n_surface_vertices, n_source_vertices)``.
110+
111+ Notes
112+ -----
113+ .. versionadded:: 1.13
114+ """
115+
78116 def __init__ (self , renderer , vertices , triangles , normals ):
79117 self ._renderer = renderer
80118 self ._vertices = vertices
@@ -91,8 +129,9 @@ def __init__(self, renderer, vertices, triangles, normals):
91129
92130 self ._default_scalars = np .ones (vertices .shape )
93131 self ._default_scalars_name = "Data"
132+ self .smooth_mat = None
94133
95- def map (self ):
134+ def _map (self ):
96135 kwargs = {
97136 "color" : None ,
98137 "pickable" : True ,
@@ -135,7 +174,30 @@ def _compose_overlays(self):
135174 B = self ._compute_over (cache , A )
136175 return B , cache
137176
138- def add_overlay (self , scalars , colormap , rng , opacity , name ):
177+ def add_overlay (self , scalars , colormap , rng , opacity , name , smooth = False ):
178+ """Add a named overlay to the mesh.
179+
180+ Parameters
181+ ----------
182+ scalars : array, shape (n_vertices,)
183+ Scalar values to display. If ``smooth=True`` and
184+ ``smooth_mat`` is set, shape must be ``(n_src_vertices,)``.
185+ colormap : array, shape (n_colors, 4)
186+ RGBA colormap table (values in ``[0, 255]``).
187+ rng : array-like, shape (2,)
188+ ``[min, max]`` range for colormap mapping.
189+ opacity : float | None
190+ Overlay opacity in ``[0, 1]``. ``None`` keeps the existing value.
191+ name : str
192+ Unique key identifying this overlay.
193+ smooth : bool
194+ If ``True`` and ``smooth_mat`` is set, multiply ``scalars``
195+ by ``smooth_mat`` before rendering. Use ``True`` only for
196+ source-space data; surface-space overlays (curvature, labels,
197+ annotations) should use the default ``False``.
198+ """
199+ if smooth and self .smooth_mat is not None :
200+ scalars = self .smooth_mat .dot (scalars )
139201 overlay = _Overlay (
140202 scalars = scalars ,
141203 colormap = colormap ,
@@ -156,6 +218,13 @@ def add_overlay(self, scalars, colormap, rng, opacity, name):
156218 self ._apply ()
157219
158220 def remove_overlay (self , names ):
221+ """Remove one or more overlays by name.
222+
223+ Parameters
224+ ----------
225+ names : str | list of str
226+ Name(s) of the overlay(s) to remove.
227+ """
159228 to_update = False
160229 if not isinstance (names , list ):
161230 names = [names ]
@@ -172,6 +241,14 @@ def _apply(self):
172241 self ._polydata [self ._default_scalars_name ] = self ._current_colors
173242
174243 def update (self , colors = None ):
244+ """Recompose all overlays and refresh the mesh texture.
245+
246+ Parameters
247+ ----------
248+ colors : array-like of shape (n_triangles, 4) | None
249+ Pre-composited RGBA colors to blend over the cached layer stack.
250+ If ``None``, all overlays are recomposed from scratch.
251+ """
175252 if colors is not None and self ._cached_colors is not None :
176253 self ._current_colors = self ._compute_over (self ._cached_colors , colors )
177254 else :
@@ -187,10 +264,30 @@ def _clean(self):
187264 self ._renderer = None
188265
189266 def update_overlay (self , name , scalars = None , colormap = None , opacity = None , rng = None ):
267+ """Update an existing overlay in-place.
268+
269+ Parameters
270+ ----------
271+ name : str
272+ Key of the overlay to update (must already exist).
273+ scalars : array, shape (n_vertices,) | None
274+ New scalar values. If ``None``, scalars are unchanged.
275+ If ``smooth_mat`` is set, smoothing is applied automatically
276+ (matching the behaviour of the original :meth:`~mne.viz.Brain.add_data`
277+ call that created the overlay).
278+ colormap : array, shape (n_colors, 4) | None
279+ New RGBA colormap table. If ``None``, colormap is unchanged.
280+ opacity : float | None
281+ New opacity in ``[0, 1]``. If ``None``, opacity is unchanged.
282+ rng : array-like, shape (2,) | None
283+ New ``[min, max]`` colormap range. If ``None``, range is unchanged.
284+ """
190285 overlay = self ._overlays .get (name , None )
191286 if overlay is None :
192287 return
193288 if scalars is not None :
289+ if self .smooth_mat is not None :
290+ scalars = self .smooth_mat .dot (scalars )
194291 overlay ._scalars = scalars
195292 if colormap is not None :
196293 overlay ._colormap = colormap
0 commit comments