diff --git a/doc/changes/dev/13502.newfeature.rst b/doc/changes/dev/13502.newfeature.rst new file mode 100644 index 00000000000..8ca57c6b4b0 --- /dev/null +++ b/doc/changes/dev/13502.newfeature.rst @@ -0,0 +1 @@ +Add ``show_names`` parameter to :func:`mne.viz.plot_alignment` to display channel names in the 3D scene. diff --git a/mne/viz/_3d.py b/mne/viz/_3d.py index cc660fe4986..0f48779bcb9 100644 --- a/mne/viz/_3d.py +++ b/mne/viz/_3d.py @@ -1,4 +1,4 @@ -"""Functions to make 3D plots with M/EEG data.""" +z"""Functions to make 3D plots with M/EEG data.""" # Authors: The MNE-Python contributors. # License: BSD-3-Clause @@ -554,6 +554,7 @@ def plot_alignment( sensor_colors=None, *, sensor_scales=None, + show_names=False, verbose=None, ): """Plot head, sensor, and source space alignment in 3D. @@ -621,6 +622,13 @@ def plot_alignment( %(seeg)s %(fnirs)s .. versionadded:: 0.20 + + show_names : bool + If True (default False), plot the name of each channel near its + location in the 3D scene. + + .. versionadded:: 1.x (The maintainer will finalize the version number) + show_axes : bool If True (default False), coordinate frame axis indicators will be shown: @@ -941,6 +949,30 @@ def plot_alignment( sensor_colors=sensor_colors, sensor_scales=sensor_scales, ) + + if show_names is True and info is not None: + for ch_type in ['mag', 'grad', 'eeg', 'ecog', 'seeg', 'dbs']: + picks = pick_types(info, **{ch_type: True, 'ref_meg': ch_type == 'meg'}) + + if len(picks) > 0: + ch_pos = np.array([info['chs'][k]['loc'][:3] for k in picks]) + ch_names = [info['ch_names'][k] for k in picks] + ch_coord_frame = info['chs'][picks[0]]['coord_frame'] + trans = to_cf_t[_frame_to_str[ch_coord_frame]] + + ch_pos = apply_trans(trans, ch_pos) + + scale = 0.003 + + for pos, name in zip(ch_pos, ch_names): + renderer.text3d( + x=pos[0], + y=pos[1], + z=pos[2], + text=name, + scale=scale, + color='gray' + ) if src is not None: atlas_ids, colors = read_freesurfer_lut() diff --git a/mne/viz/tests/test_3d.py b/mne/viz/tests/test_3d.py index ab24e6a70db..54a4c6fca90 100644 --- a/mne/viz/tests/test_3d.py +++ b/mne/viz/tests/test_3d.py @@ -440,6 +440,31 @@ def test_plot_alignment_ieeg( renderer.backend._close_all() +@testing.requires_testing_data +def test_plot_alignment_show_names(raw): + """Test that channel names are correctly added in plot_alignment (#13502).""" + info = raw.info + + fig = plot_alignment( + info, + eeg="sensors", + show_names=True, + show=False, + trans="fsaverage", + subject="fsaverage", + subjects_dir=testing.data_path() / "subjects", + ) + + n_text_actors = sum( + 1 for actor in fig.plotter.actors.values() if "Label" in actor.name + ) + + # Assert presence of labels (e.g., more than 10) + assert n_text_actors > 10 + + fig.close() + + @pytest.mark.slowtest # Slow on Azure @testing.requires_testing_data # all use trans + head surf @pytest.mark.parametrize(