diff --git a/doc/changes/dev/14126.newfeature.rst b/doc/changes/dev/14126.newfeature.rst new file mode 100644 index 00000000000..e4850922bd1 --- /dev/null +++ b/doc/changes/dev/14126.newfeature.rst @@ -0,0 +1 @@ +Add ``source_id`` as an optional parameter in :class:`mne.viz.ui_events.VertexSelect`, by `Lifeng Qiu Lin`_. \ No newline at end of file diff --git a/mne/viz/_brain/_brain.py b/mne/viz/_brain/_brain.py index 1711333bf76..d904d301493 100644 --- a/mne/viz/_brain/_brain.py +++ b/mne/viz/_brain/_brain.py @@ -1182,8 +1182,10 @@ def _configure_vertex_time_course(self): ind = np.unravel_index( np.argmax(np.abs(use_data), axis=None), use_data.shape ) - vertex_id = vertices[ind[0]] - publish(self, VertexSelect(hemi=hemi, vertex_id=vertex_id)) + publish( + self, + VertexSelect(hemi=hemi, vertex_id=vertices[ind[0]], source_id=ind[0]), + ) def _configure_picking(self): # get data for each hemi @@ -1457,6 +1459,7 @@ def _on_pick(self, vtk_picker, event): # shift = np.array(grid.GetOrigin()) + spacing / 2. # ijk = np.round((pos - shift) / spacing).astype(int) # vertex_id = np.ravel_multi_index(ijk, shape, order='F') + source_id = idx else: vtk_cell = mesh.GetCell(cell_id) cell = [ @@ -1466,7 +1469,12 @@ def _on_pick(self, vtk_picker, event): vert_pos = mesh.points[cell] vertex_id = cell[np.argmin(np.linalg.norm(vert_pos - pos, axis=1))] - publish(self, VertexSelect(hemi=hemi, vertex_id=vertex_id)) + # retrieve the nearest source_id from the smooth_mat + smooth_mat = self.act_data_smooth[hemi][1] + row = smooth_mat[vertex_id] + source_id = smooth_mat[vertex_id].argmax() if row.nnz else None + + publish(self, VertexSelect(hemi=hemi, vertex_id=vertex_id, source_id=source_id)) def _on_time_change(self, event): """Respond to a time change UI event.""" @@ -4050,9 +4058,11 @@ def _update_current_time_idx(self, time_idx): mesh = self.layered_meshes[hemi] mesh.smooth_mat = hemi_data.get("smooth_mat") key_rng = [ - -key_data["fmax"] - if key_data["center"] is not None - else key_data["fmin"], + ( + -key_data["fmax"] + if key_data["center"] is not None + else key_data["fmin"] + ), key_data["fmax"], ] if data_key in mesh._overlays: diff --git a/mne/viz/_brain/tests/test_brain.py b/mne/viz/_brain/tests/test_brain.py index 3e94837886a..6f85b02cacb 100644 --- a/mne/viz/_brain/tests/test_brain.py +++ b/mne/viz/_brain/tests/test_brain.py @@ -1674,6 +1674,35 @@ def test_brain_ui_events(renderer_interactive_pyvistaqt, brain_gc): # Should remain unchanged. assert_array_equal(brain._data["ctable"][:3, 3], [0, 2, 4]) + # Test effect of vertex selection publishing. + events = list() + ui_events.subscribe(brain, "vertex_select", lambda event: events.append(event)) + + mesh = brain.layered_meshes["lh"]._polydata + faces = brain.geo["lh"].faces + vertices = brain._data["lh"]["vertices"] + smooth_mat = brain.act_data_smooth["lh"][1] + + # each for existing and missing source vertex cases + for is_source in (True, False): + mask = np.isin(faces[:, 0], vertices) + # select first vertex satisfying the condition + cell_id = np.where(mask if is_source else ~mask)[0][0] + vertex_id = faces[cell_id, 0] + # make the selection + n_events = len(events) + brain._on_pick(TstVTKPicker(mesh, cell_id, "lh", brain), None) + assert len(events) == n_events + 1 + event = events[-1] + assert event.vertex_id == vertex_id + row = smooth_mat[vertex_id, :] + if is_source: + assert event.source_id == row.argmax() + assert vertices[event.source_id] == event.vertex_id + else: + assert row.sum() == 0 + assert event.source_id is None + brain.close() diff --git a/mne/viz/ui_events.py b/mne/viz/ui_events.py index 7667248a140..bb54447a9e4 100644 --- a/mne/viz/ui_events.py +++ b/mne/viz/ui_events.py @@ -167,6 +167,9 @@ class VertexSelect(UIEvent): Can be ``"lh"``, ``"rh"``, or ``"vol"``. vertex_id : int The vertex number (in the high resolution mesh) that was selected. + source_id : int | None + The index number of the closest source point to the vertex. + Only set if the publishing figure contains a source estimate. Attributes ---------- @@ -176,10 +179,14 @@ class VertexSelect(UIEvent): Can be ``"lh"``, ``"rh"``, or ``"vol"``. vertex_id : int The vertex number (in the high resolution mesh) that was selected. + source_id : int | None + The index number of the closest source point to the vertex. + Only set if the publishing figure contains a source estimate. """ hemi: str vertex_id: int + source_id: int = None @dataclass