Repository: mne-tools/mne-python

Issue:
Default topomap interpolation a bit uglier than some time before
I noticed that current topomap interpolation with `extrapolate='head'` produces more artifacts at the edges than in the previous version. Take a look at this:
### before
![image](https://user-images.githubusercontent.com/8452354/97121321-b7cba880-171d-11eb-9030-45863668659e.png)

### now
![image](https://user-images.githubusercontent.com/8452354/97121378-0711d900-171e-11eb-9e1c-d88ed2d6b53c.png)

Now the topos look ok only in `extrapolate='local'` mode (so I've learnt to like the non-circular convex hull mask).
I think I may be partly responsible for this. I have a vague memory of adding more extrapolation points some time ago to `extrapolate='head'`, but I can't find the PR now.
I'll try to take a look at this in the upcoming week.


Discussion:
@mmagnuski we're making a push to release in a couple of weeks, do you have time to look into this in that time?
I might have some time for this next week, I'll try to look into it!
I still remember about this issue, I might find some time for it tomorrow.

Resolving patch:
diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py
--- a/mne/viz/topomap.py
+++ b/mne/viz/topomap.py
@@ -567,9 +567,10 @@ def _get_extra_points(pos, extrapolate, origin, radii):
     else:
         assert extrapolate == 'head'
         # return points on the head circle
-        angle = np.arcsin(distance / 2 / np.mean(radii))
-        points_l = np.arange(0, 2 * np.pi, angle)
-        use_radii = radii * 1.1
+        angle = np.arcsin(distance / np.mean(radii))
+        n_pnts = max(12, int(np.round(2 * np.pi / angle)))
+        points_l = np.linspace(0, 2 * np.pi, n_pnts, endpoint=False)
+        use_radii = radii * 1.1 + distance
         points_x = np.cos(points_l) * use_radii[0] + x
         points_y = np.sin(points_l) * use_radii[1] + y
         new_pos = np.stack([points_x, points_y], axis=1)


Test patch:
diff --git a/mne/viz/tests/test_topomap.py b/mne/viz/tests/test_topomap.py
--- a/mne/viz/tests/test_topomap.py
+++ b/mne/viz/tests/test_topomap.py
@@ -203,14 +203,14 @@ def test_plot_topomap_basic(monkeypatch):
 
     # border=0 and border='mean':
     # ---------------------------
-    ch_names = list('abcde')
-    ch_pos = np.array([[0, 0, 1], [1, 0, 0], [-1, 0, 0],
-                       [0, -1, 0], [0, 1, 0]])
-    ch_pos_dict = {name: pos for name, pos in zip(ch_names, ch_pos)}
+    ch_pos = np.array(sum(([[0, 0, r], [r, 0, 0], [-r, 0, 0],
+                            [0, -r, 0], [0, r, 0]]
+                           for r in np.linspace(0.2, 1.0, 5)), []))
+    rng = np.random.RandomState(23)
+    data = np.full(len(ch_pos), 5) + rng.randn(len(ch_pos))
+    info = create_info(len(ch_pos), 250, 'eeg')
+    ch_pos_dict = {name: pos for name, pos in zip(info['ch_names'], ch_pos)}
     dig = make_dig_montage(ch_pos_dict, coord_frame='head')
-
-    data = np.full(5, 5) + np.random.RandomState(23).randn(5)
-    info = create_info(ch_names, 250, ['eeg'] * 5)
     info.set_montage(dig)
 
     # border=0
@@ -218,7 +218,7 @@ def test_plot_topomap_basic(monkeypatch):
     img_data = ax.get_array().data
 
     assert np.abs(img_data[31, 31] - data[0]) < 0.12
-    assert np.abs(img_data[10, 55]) < 0.3
+    assert np.abs(img_data[0, 0]) < 1.5
 
     # border='mean'
     ax, _ = plot_topomap(data, info, extrapolate='head', border='mean',
@@ -226,7 +226,7 @@ def test_plot_topomap_basic(monkeypatch):
     img_data = ax.get_array().data
 
     assert np.abs(img_data[31, 31] - data[0]) < 0.12
-    assert img_data[10, 54] > 5
+    assert img_data[0, 0] > 5
 
     # error when not numeric or str:
     error_msg = 'border must be an instance of numeric or str'