Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changes/dev/13773.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improved channel name label rendering in :func:`mne.viz.plot_alignment` when using ``show_channel_names=True`` by reducing overlap, adding a shadow for contrast, and offsetting labels outward from sensor positions, by `Aman Srivastava`_.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Improved channel name label rendering in :func:`mne.viz.plot_alignment` when using ``show_channel_names=True`` by reducing overlap, adding a shadow for contrast, and offsetting labels outward from sensor positions, by `Aman Srivastava`_.
Make channel name labels easier to read in :func:`mne.viz.plot_alignment`, by `Aman Srivastava`_.

28 changes: 21 additions & 7 deletions mne/viz/_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -955,13 +955,27 @@ def plot_alignment(
# transform to current coord frame
pos = apply_trans(to_cf_t["head"], pos)

for ch, xyz in zip(chs, pos):
renderer.text3d(
*xyz,
ch["ch_name"],
scale=0.005,
color=(1.0, 1.0, 1.0),
)
# offset labels outward from centroid so they clear the sensor glyphs
centroid = pos.mean(axis=0)
directions = pos - centroid
norms = np.linalg.norm(directions, axis=1, keepdims=True)
norms = np.where(norms > 0, norms, 1)
offsets = pos + 0.01 * directions / norms

labels = [ch["ch_name"] for ch in chs]
renderer.plotter.add_point_labels(
offsets,
labels,
font_size=10,
text_color=(1.0, 1.0, 1.0),
font_family="renderer.font_family",
shadow=True,
show_points=False,
shape=None,
always_visible=True,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have concerns about circumventing mne.viz.backends._pyvista.PyVistaRenderer.text3d and reaching straight for renderer.plotter.add_point_labels, because the aforementioned text3d does some checks/sanitizing, e.g.:

if "always_visible" in signature(self.plotter.add_point_labels).parameters:
kwargs["always_visible"] = True

@drammock WDYT?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for PyVista questions I always ask @larsoner 😆 But looking at the PyVista docs, I don't see any indication that add_point_labels is especially low-level or dangerous. That said, I agree that since we seem to have taken pains to add a wrapper for it, it was probably for a good reason and we should probably use the wrapper.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK! - my specific concern is just that here we hardcode always_visible=True whereas in text3d we check to see if that parameter exists (given the users version of PyVista(??..) ). Maybe that check is outdated and our lower pin guarantees the existence of this parameter, but someone would need to check...

@aman-coder03 is there a specific reason why we need to use add_point_labels instead of text3d here? What was your motivation for using switching to it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@scott-huberty good point...
the reason I switched to add_point_labels directly was to pass all labels at once as a batch and to use shadow=True and show_points=False which aren't available through the text3d wrapper but yeah bypassing text3d to get those params isn't ideal. I think the cleanest fix would be to just extend text3d in _pyvista.py to accept shadow and show_points as parameters and pass them through, that way we keep the version check for always_visible and everything stays within the wrapper.
happy to do that if @drammock and @larsoner are okay with it!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM

render=False,
reset_camera=False,
)

if src is not None:
atlas_ids, colors = read_freesurfer_lut()
Expand Down
Loading