Repository: mne-tools/mne-python

Issue:
Inaccurate iEEG electrode positions on 3d/2d plots
Originally posted here: https://mne.discourse.group/t/ieeg-electrode-positions-on-3d-2d-plots/2687

The electrode positions I am getting from the mayavi plot look inaccurate.

The issue is well visible in the tutorial as well: [Working with ECoG data](https://mne.tools/dev/auto_tutorials/misc/plot_ecog.html#sphx-glr-auto-tutorials-misc-plot-ecog-py):
![Example1](https://aws1.discourse-cdn.com/free1/uploads/mne/original/1X/ce01e0041af17be9ddfd51a6711e36faf532879c.png)

The electrodes plotted with `scatter` (colored blobs) are off compared to the ones made with `plot_alignment` (white spheres underneath). Reducing the radius of the scatter plot reveals how inaccurate it is:

![Example2](https://aws1.discourse-cdn.com/free1/uploads/mne/original/1X/17ab5e455b0fe5c66e058f19d7665bc7d936fcd9.png)

I understand that these positions depend on the view, but both plots are made using the same viewpoint:

```
fig = plot_alignment(raw_car.info, subject=subject, subjects_dir=subjects_dir, surfaces=['pial'], coord_frame='mri')
mne.viz.set_3d_view(fig, 200, 70)
xy, im = snapshot_brain_montage(fig, montage)
xy_pts = np.vstack([xy[ch] for ch in raw_car.info['ch_names']])

fig, ax = plt.subplots(figsize=(5, 5))
ax.imshow(im)
ax.set_axis_off()
sc = ax.scatter(*xy_pts.T, c=ts, vmin=10, vmax=100)
```


Discussion:
Any chance someone is looking at this yet? 

Resolving patch:
diff --git a/mne/viz/backends/_pyvista.py b/mne/viz/backends/_pyvista.py
--- a/mne/viz/backends/_pyvista.py
+++ b/mne/viz/backends/_pyvista.py
@@ -902,22 +902,15 @@ def _mat_to_array(vtk_mat):
 
 
 def _3d_to_2d(plotter, xyz):
-    size = plotter.window_size
-    xyz = np.column_stack([xyz, np.ones(xyz.shape[0])])
-
-    # Transform points into 'unnormalized' view coordinates
-    comb_trans_mat = _get_world_to_view_matrix(plotter)
-    view_coords = np.dot(comb_trans_mat, xyz.T).T
-
-    # Divide through by the fourth element for normalized view coords
-    norm_view_coords = view_coords / (view_coords[:, 3].reshape(-1, 1))
-
-    # Transform from normalized view coordinates to display coordinates.
-    view_to_disp_mat = _get_view_to_display_matrix(size)
-    xy = np.dot(view_to_disp_mat, norm_view_coords.T).T
-
-    # Pull the first two columns since they're meaningful for 2d plotting
-    xy = xy[:, :2]
+    # https://vtk.org/Wiki/VTK/Examples/Cxx/Utilities/Coordinate
+    import vtk
+    coordinate = vtk.vtkCoordinate()
+    coordinate.SetCoordinateSystemToWorld()
+    xy = list()
+    for coord in xyz:
+        coordinate.SetValue(*coord)
+        xy.append(coordinate.GetComputedLocalDisplayValue(plotter.renderer))
+    xy = np.array(xy, float).reshape(-1, 2)  # in case it's empty
     return xy
 
 
diff --git a/tutorials/misc/plot_ecog.py b/tutorials/misc/plot_ecog.py
--- a/tutorials/misc/plot_ecog.py
+++ b/tutorials/misc/plot_ecog.py
@@ -15,7 +15,7 @@
 - channel locations in subject's MRI space
 - projection onto a surface
 
-For an example that involves sEEG data, channel locations in
+For a complementary example that involves sEEG data, channel locations in
 MNI space, or projection into a volume, see :ref:`tut_working_with_seeg`.
 """
 # Authors: Eric Larson <larson.eric.d@gmail.com>


Test patch: