Repository: mne-tools/mne-python

Issue:
Specify vmin vmax color range in animated topomap
#### Describe the new feature or enhancement
Add the possibility to specify the value bounds of the colormap range in the _mne.Evoked.animate_topomap_ function.
The _mne.viz.plot_topomap_ function implements vmin and vmax for static topomaps but there seems to be no equivalent in the animated version of the function.

#### Describe your proposed implementation
 _mne.viz.plot_topomap_  function implements vmin and vmax parameters (lines 1700-1704 from topomap.py)
```
vmin = np.min(vlims)
    vmax = np.max(vlims)
    cmap = _setup_cmap(cmap, n_axes=n_times, norm=vmin >= 0)
```

animate_topomap could implement vmin and vmax parameters in the same way. They could be passed on from to mne.Evoked.animate_topomap to the params dictionnary (line 2361-2369 of topomap.py)
```
params = dict(data=data, pos=pos, all_times=evoked.times, frame=0,
                  frames=frames, butterfly=butterfly, blit=blit,
                  pause=False, times=times, time_unit=time_unit,
                  clip_origin=clip_origin,vmin=vmin,vmax=vmax)
[...]
 animate_func = partial(_animate, ax=ax, ax_line=ax_line, params=params)
```
as the __animate_ function already seems to allow vmin and vmax into params : (lines 2267-2268 of __animate_ function
    vmin = params['vmin']
    vmax = params['vmax']

Sorry if this isn't clear, this is my first PR 👀 

#### Additional comments
*Add any other context or screenshots about the feature request here.*



Discussion:
Hello! 👋 Thanks for opening your first issue here! ❤️ We will try to get back to you soon. 🚴🏽‍♂️

Sounds very legitimate. Would you consider sending a PR?

Thx

If you don't have time, we can also try to address this during this year's code sprint :)
I can definitely try to do a PR.
I can find some time but I may lack the skills on how to handle the PR
properly, I'll look it up!
edit : This is the closest I found to a guide, is this in the right direction ? https://mne.tools/stable/install/contributing.html
Le jeu. 9 juin 2022 à 12:21, Richard Höchenberger ***@***.***>
a écrit :

> If you don't have time, we can also try to address this during this year's
> code sprint :)
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/mne-tools/mne-python/issues/10725#issuecomment-1150943885>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AI2ZARP5RYVRHU6E5WBJZ43VOHAR5ANCNFSM5YJLL2GA>
> .
> You are receiving this because you authored the thread.Message ID:
> ***@***.***>
>

> is this in the right direction ? https://mne.tools/stable/install/contributing.html

yes!  As you're working on this, please also take note of anything missing from that page that you needed to know in order to make your first PR. That will help us improve the contributing guide for future first-time contributors!
@matsvanes is working on this

Resolving patch:
diff --git a/mne/evoked.py b/mne/evoked.py
--- a/mne/evoked.py
+++ b/mne/evoked.py
@@ -457,7 +457,8 @@ def plot_joint(self, times="peaks", title='', picks=None,
     def animate_topomap(self, ch_type=None, times=None, frame_rate=None,
                         butterfly=False, blit=True, show=True, time_unit='s',
                         sphere=None, *, image_interp=_INTERPOLATION_DEFAULT,
-                        extrapolate=_EXTRAPOLATE_DEFAULT, verbose=None):
+                        extrapolate=_EXTRAPOLATE_DEFAULT, vmin=None, vmax=None,
+                        verbose=None):
         """Make animation of evoked data as topomap timeseries.
 
         The animation can be paused/resumed with left mouse button.
@@ -498,6 +499,9 @@ def animate_topomap(self, ch_type=None, times=None, frame_rate=None,
         %(extrapolate_topomap)s
 
             .. versionadded:: 0.22
+        %(vmin_vmax_topomap)s
+
+            .. versionadded:: 1.1.0
         %(verbose)s
 
         Returns
@@ -515,7 +519,7 @@ def animate_topomap(self, ch_type=None, times=None, frame_rate=None,
             self, ch_type=ch_type, times=times, frame_rate=frame_rate,
             butterfly=butterfly, blit=blit, show=show, time_unit=time_unit,
             sphere=sphere, image_interp=image_interp,
-            extrapolate=extrapolate, verbose=verbose)
+            extrapolate=extrapolate, vmin=vmin, vmax=vmax, verbose=verbose)
 
     def as_type(self, ch_type='grad', mode='fast'):
         """Compute virtual evoked using interpolated fields.
diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py
--- a/mne/viz/topomap.py
+++ b/mne/viz/topomap.py
@@ -2294,11 +2294,13 @@ def _init_anim(ax, ax_line, ax_cbar, params, merge_channels, sphere, ch_type,
     logger.info('Initializing animation...')
     data = params['data']
     items = list()
+    vmin = params['vmin'] if 'vmin' in params else None
+    vmax = params['vmax'] if 'vmax' in params else None
     if params['butterfly']:
         all_times = params['all_times']
         for idx in range(len(data)):
             ax_line.plot(all_times, data[idx], color='k', lw=1)
-        vmin, vmax = _setup_vmin_vmax(data, None, None)
+        vmin, vmax = _setup_vmin_vmax(data, vmin, vmax)
         ax_line.set(yticks=np.around(np.linspace(vmin, vmax, 5), -1),
                     xlim=all_times[[0, -1]])
         params['line'] = ax_line.axvline(all_times[0], color='r')
@@ -2309,7 +2311,7 @@ def _init_anim(ax, ax_line, ax_cbar, params, merge_channels, sphere, ch_type,
     norm = True if np.min(data) > 0 else False
     cmap = 'Reds' if norm else 'RdBu_r'
 
-    vmin, vmax = _setup_vmin_vmax(data, None, None, norm)
+    vmin, vmax = _setup_vmin_vmax(data, vmin, vmax, norm)
 
     outlines = _make_head_outlines(sphere, params['pos'], 'head',
                                    params['clip_origin'])
@@ -2429,7 +2431,7 @@ def _key_press(event, params):
 
 def _topomap_animation(evoked, ch_type, times, frame_rate, butterfly, blit,
                        show, time_unit, sphere, image_interp,
-                       extrapolate, *, verbose=None):
+                       extrapolate, *, vmin, vmax, verbose=None):
     """Make animation of evoked data as topomap timeseries.
 
     See mne.evoked.Evoked.animate_topomap.
@@ -2458,6 +2460,9 @@ def _topomap_animation(evoked, ch_type, times, frame_rate, butterfly, blit,
     data = evoked.data[picks, :]
     data *= _handle_default('scalings')[ch_type]
 
+    norm = np.min(data) >= 0
+    vmin, vmax = _setup_vmin_vmax(data, vmin, vmax, norm)
+
     fig = plt.figure(figsize=(6, 5))
     shape = (8, 12)
     colspan = shape[1] - 1
@@ -2476,7 +2481,7 @@ def _topomap_animation(evoked, ch_type, times, frame_rate, butterfly, blit,
     params = dict(data=data, pos=pos, all_times=evoked.times, frame=0,
                   frames=frames, butterfly=butterfly, blit=blit,
                   pause=False, times=times, time_unit=time_unit,
-                  clip_origin=clip_origin)
+                  clip_origin=clip_origin, vmin=vmin, vmax=vmax)
     init_func = partial(_init_anim, ax=ax, ax_cbar=ax_cbar, ax_line=ax_line,
                         params=params, merge_channels=merge_channels,
                         sphere=sphere, ch_type=ch_type,


Test patch: