Skip to content

Commit 000dac9

Browse files
authored
MAINT: Unify 90% similar code [circle front] (#8068)
1 parent 7f30d1d commit 000dac9

2 files changed

Lines changed: 132 additions & 175 deletions

File tree

mne/utils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
_check_rank, _check_option, _check_depth, _check_combine,
1717
_check_path_like, _check_src_normal, _check_stc_units,
1818
_check_pyqt5_version, _check_sphere, _check_time_format,
19-
_check_freesurfer_home, _suggest)
19+
_check_freesurfer_home, _suggest, _require_version)
2020
from .config import (set_config, get_config, get_config_path, set_cache_dir,
2121
set_memmap_min_size, get_subjects_dir, _get_stim_channel,
2222
sys_info, _get_extra_data_path, _get_root_dir,

mne/viz/_3d.py

Lines changed: 131 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#
1111
# License: Simplified BSD
1212

13-
from distutils.version import LooseVersion
1413
from itertools import cycle
1514
import os.path as op
1615
import sys
@@ -39,7 +38,8 @@
3938
read_ras_mni_t, _print_coord_trans)
4039
from ..utils import (get_subjects_dir, logger, _check_subject, verbose, warn,
4140
has_nibabel, check_version, fill_doc, _pl, get_config,
42-
_ensure_int, _validate_type, _check_option)
41+
_ensure_int, _validate_type, _check_option,
42+
_require_version)
4343
from .utils import (mne_analyze_colormap, _get_color_list,
4444
plt_show, tight_layout, figure_nobar, _check_time_unit)
4545
from .misc import _check_mri
@@ -1675,32 +1675,62 @@ def plot_source_estimates(stc, subject=None, surface='inflated', hemi='lh',
16751675
plot_mpl = True
16761676
else: # 'mayavi'
16771677
raise
1678-
1678+
kwargs = dict(
1679+
subject=subject, surface=surface, hemi=hemi, colormap=colormap,
1680+
time_label=time_label, smoothing_steps=smoothing_steps,
1681+
subjects_dir=subjects_dir, views=views, clim=clim,
1682+
figure=figure, initial_time=initial_time, time_unit=time_unit,
1683+
background=background, time_viewer=time_viewer, colorbar=colorbar,
1684+
transparent=transparent)
16791685
if plot_mpl:
1680-
return _plot_mpl_stc(stc, subject=subject, surface=surface, hemi=hemi,
1681-
colormap=colormap, time_label=time_label,
1682-
smoothing_steps=smoothing_steps,
1683-
subjects_dir=subjects_dir, views=views, clim=clim,
1684-
figure=figure, initial_time=initial_time,
1685-
time_unit=time_unit, background=background,
1686-
spacing=spacing, time_viewer=time_viewer,
1687-
colorbar=colorbar, transparent=transparent)
1688-
1689-
if _get_3d_backend() == "mayavi":
1686+
return _plot_mpl_stc(stc, spacing=spacing, **kwargs)
1687+
return _plot_stc(
1688+
stc, overlay_alpha=alpha, brain_alpha=alpha, vector_alpha=alpha,
1689+
cortex=cortex, foreground=foreground, size=size, scale_factor=None,
1690+
show_traces=show_traces, **kwargs)
1691+
1692+
1693+
def _plot_stc(stc, subject, surface, hemi, colormap, time_label,
1694+
smoothing_steps, subjects_dir, views, clim, figure, initial_time,
1695+
time_unit, background, time_viewer, colorbar, transparent,
1696+
brain_alpha, overlay_alpha, vector_alpha, cortex, foreground,
1697+
size, scale_factor, show_traces):
1698+
from .backends.renderer import _get_3d_backend
1699+
from ..source_estimate import (
1700+
_BaseSourceEstimate, SourceEstimate, VectorSourceEstimate)
1701+
_validate_type(stc, _BaseSourceEstimate)
1702+
vec = stc._data_ndim == 3
1703+
if vec:
1704+
allowed = VectorSourceEstimate
1705+
else:
1706+
allowed = SourceEstimate
1707+
_validate_type(stc, allowed, 'stc')
1708+
subjects_dir = get_subjects_dir(subjects_dir=subjects_dir,
1709+
raise_error=True)
1710+
subject = _check_subject(stc.subject, subject, True)
1711+
1712+
backend = _get_3d_backend()
1713+
del _get_3d_backend
1714+
using_mayavi = backend == "mayavi"
1715+
if using_mayavi:
16901716
from surfer import Brain
1717+
_require_version('surfer', 'stc.plot', '0.9')
16911718
else: # PyVista
16921719
from ._brain import _Brain as Brain
1693-
_check_option('hemi', hemi, ['lh', 'rh', 'split', 'both'])
16941720

1721+
_check_option('hemi', hemi, ['lh', 'rh', 'split', 'both'])
16951722
time_label, times = _handle_time(time_label, time_unit, stc.times)
1723+
16961724
# convert control points to locations in colormap
1697-
mapdata = _process_clim(clim, colormap, transparent, stc.data)
1725+
mapdata = _process_clim(clim, colormap, transparent, stc.data,
1726+
allow_pos_lims=not vec)
1727+
16981728
# XXX we should only need to do this for PySurfer/Mayavi, the PyVista
16991729
# plotter should be smart enough to do this separation in the cmap-to-ctab
17001730
# conversion. But this will need to be another refactoring that will
17011731
# hopefully restore this line:
17021732
#
1703-
# if _get_3d_backend() == 'mayavi':
1733+
# if using_mayavi:
17041734
_separate_map(mapdata)
17051735
colormap = mapdata['colormap']
17061736
diverging = 'pos_lims' in mapdata['clim']
@@ -1713,59 +1743,94 @@ def plot_source_estimates(stc, subject=None, surface='inflated', hemi='lh',
17131743
else:
17141744
hemis = [hemi]
17151745

1716-
if title is None:
1717-
title = subject if len(hemis) > 1 else '%s - %s' % (subject, hemis[0])
1746+
if overlay_alpha is None:
1747+
overlay_alpha = brain_alpha
1748+
if overlay_alpha == 0:
1749+
smoothing_steps = 1 # Disable smoothing to save time.
1750+
1751+
title = subject if len(hemis) > 1 else '%s - %s' % (subject, hemis[0])
17181752
kwargs = {
17191753
"subject_id": subject, "hemi": hemi, "surf": surface,
17201754
"title": title, "cortex": cortex, "size": size,
17211755
"background": background, "foreground": foreground,
17221756
"figure": figure, "subjects_dir": subjects_dir,
1723-
"views": views,
1757+
"views": views, "alpha": brain_alpha,
17241758
}
1725-
if _get_3d_backend() in ['pyvista', 'notebook']:
1759+
if backend in ['pyvista', 'notebook']:
17261760
kwargs["show"] = not time_viewer
17271761
else:
17281762
kwargs.update(_check_pysurfer_antialias(Brain))
17291763
with warnings.catch_warnings(record=True): # traits warnings
17301764
brain = Brain(**kwargs)
17311765
del kwargs
1766+
if scale_factor is None:
1767+
# Configure the glyphs scale directly
1768+
width = np.mean([np.ptp(brain.geo[hemi].coords[:, 1])
1769+
for hemi in hemis if hemi in brain.geo])
1770+
scale_factor = 0.025 * width / scale_pts[-1]
1771+
1772+
if transparent is None:
1773+
transparent = True
1774+
sd_kwargs = dict(transparent=transparent, verbose=False)
17321775
center = 0. if diverging else None
17331776
for hemi in hemis:
1734-
hemi_idx = 0 if hemi == 'lh' else 1
17351777
data = getattr(stc, hemi + '_data')
1736-
vertices = stc.vertices[hemi_idx]
1737-
if len(data) > 0:
1738-
if transparent is None:
1739-
transparent = True
1740-
kwargs = {
1741-
"array": data, "colormap": colormap,
1742-
"vertices": vertices,
1743-
"smoothing_steps": smoothing_steps,
1744-
"time": times, "time_label": time_label,
1745-
"alpha": alpha, "hemi": hemi,
1746-
"colorbar": colorbar, "initial_time": initial_time,
1747-
"transparent": transparent, "center": center,
1748-
"verbose": False
1749-
}
1750-
if _get_3d_backend() == "mayavi":
1751-
kwargs["min"] = scale_pts[0]
1752-
kwargs["mid"] = scale_pts[1]
1753-
kwargs["max"] = scale_pts[2]
1754-
else: # pyvista
1755-
kwargs["fmin"] = scale_pts[0]
1756-
kwargs["fmid"] = scale_pts[1]
1757-
kwargs["fmax"] = scale_pts[2]
1758-
kwargs["clim"] = clim
1759-
with warnings.catch_warnings(record=True): # traits warnings
1760-
brain.add_data(**kwargs)
1761-
1762-
_check_time_viewer_compatibility(brain, time_viewer, show_traces)
1763-
return brain
1778+
vertices = stc.vertices[0 if hemi == 'lh' else 1]
1779+
alpha = overlay_alpha
1780+
if len(data) == 0:
1781+
continue
1782+
kwargs = {
1783+
"array": data, "colormap": colormap,
1784+
"vertices": vertices,
1785+
"smoothing_steps": smoothing_steps,
1786+
"time": times, "time_label": time_label,
1787+
"alpha": alpha, "hemi": hemi,
1788+
"colorbar": colorbar,
1789+
"vector_alpha": vector_alpha,
1790+
"scale_factor": scale_factor,
1791+
"verbose": False,
1792+
"initial_time": initial_time,
1793+
"transparent": transparent, "center": center,
1794+
"verbose": False
1795+
}
1796+
if using_mayavi:
1797+
kwargs["min"] = scale_pts[0]
1798+
kwargs["mid"] = scale_pts[1]
1799+
kwargs["max"] = scale_pts[2]
1800+
else: # pyvista
1801+
kwargs["fmin"] = scale_pts[0]
1802+
kwargs["fmid"] = scale_pts[1]
1803+
kwargs["fmax"] = scale_pts[2]
1804+
kwargs["clim"] = clim
1805+
with warnings.catch_warnings(record=True): # traits warnings
1806+
brain.add_data(**kwargs)
1807+
brain.scale_data_colormap(fmin=scale_pts[0], fmid=scale_pts[1],
1808+
fmax=scale_pts[2], **sd_kwargs)
17641809

1810+
need_peeling = (brain_alpha < 1.0 and
1811+
sys.platform != 'darwin' and
1812+
vec)
1813+
if using_mayavi:
1814+
for hemi in hemis:
1815+
for b in brain._brain_list:
1816+
for layer in b['brain'].data.values():
1817+
glyphs = layer['glyphs']
1818+
if glyphs is None:
1819+
continue
1820+
glyphs.glyph.glyph.scale_factor = scale_factor
1821+
glyphs.glyph.glyph.clamping = False
1822+
glyphs.glyph.glyph.range = (0., 1.)
17651823

1766-
def _check_time_viewer_compatibility(brain, time_viewer, show_traces):
1767-
from .backends.renderer import _get_3d_backend
1768-
using_mayavi = _get_3d_backend() == "mayavi"
1824+
# depth peeling patch
1825+
if need_peeling:
1826+
for ff in brain._figures:
1827+
for f in ff:
1828+
if f.scene is not None and sys.platform != 'darwin':
1829+
f.scene.renderer.use_depth_peeling = True
1830+
elif need_peeling:
1831+
brain.enable_depth_peeling()
1832+
1833+
# time_viewer and show_traces
17691834
_check_option('time_viewer', time_viewer, (True, False, 'auto'))
17701835
_check_option('show_traces', show_traces,
17711836
(True, False, 'auto', 'separate'))
@@ -1778,18 +1843,11 @@ def _check_time_viewer_compatibility(brain, time_viewer, show_traces):
17781843
brain._times is not None and
17791844
len(brain._times) > 1
17801845
)
1781-
1782-
if _get_3d_backend() == "mayavi" and all([time_viewer, show_traces]):
1783-
raise NotImplementedError("Point picking is not available"
1784-
" for the mayavi 3d backend.")
1785-
if using_mayavi:
1786-
if not check_version('surfer', '0.9'):
1787-
raise RuntimeError('This function requires pysurfer version '
1788-
'>= 0.9')
1789-
17901846
if show_traces and not time_viewer:
17911847
raise ValueError('show_traces cannot be used when time_viewer=False')
1792-
1848+
if using_mayavi and show_traces:
1849+
raise NotImplementedError("show_traces=True is not available "
1850+
"for the mayavi 3d backend.")
17931851
if time_viewer:
17941852
if using_mayavi:
17951853
from surfer import TimeViewer
@@ -1798,6 +1856,8 @@ def _check_time_viewer_compatibility(brain, time_viewer, show_traces):
17981856
from ._brain import _TimeViewer as TimeViewer
17991857
TimeViewer(brain, show_traces=show_traces)
18001858

1859+
return brain
1860+
18011861

18021862
def _glass_brain_crosshairs(params, x, y, z):
18031863
for ax, a, b in ((params['ax_y'], x, z),
@@ -2343,118 +2403,15 @@ def plot_vector_source_estimates(stc, subject=None, hemi='lh', colormap='hot',
23432403
If the current magnitude overlay is not desired, set ``overlay_alpha=0``
23442404
and ``smoothing_steps=1``.
23452405
"""
2346-
from .backends.renderer import _get_3d_backend
2347-
# Import here to avoid circular imports
2348-
if _get_3d_backend() == "mayavi":
2349-
from surfer import Brain
2350-
from surfer import __version__ as surfer_version
2351-
else: # PyVista
2352-
from ._brain import _Brain as Brain
2353-
from ..source_estimate import VectorSourceEstimate
2354-
2355-
_validate_type(stc, VectorSourceEstimate, "stc", "Vector Source Estimate")
2356-
subjects_dir = get_subjects_dir(subjects_dir=subjects_dir,
2357-
raise_error=True)
2358-
subject = _check_subject(stc.subject, subject, True)
2359-
_check_option('hemi', hemi, ['lh', 'rh', 'split', 'both'])
2360-
time_label, times = _handle_time(time_label, time_unit, stc.times)
2361-
2362-
# convert control points to locations in colormap
2363-
mapdata = _process_clim(clim, colormap, transparent, stc.data,
2364-
allow_pos_lims=False)
2365-
colormap = mapdata['colormap']
2366-
scale_pts = mapdata['clim']['lims'] # pos_lims not allowed
2367-
transparent = mapdata['transparent']
2368-
del mapdata
2369-
2370-
if hemi in ['both', 'split']:
2371-
hemis = ['lh', 'rh']
2372-
else:
2373-
hemis = [hemi]
2374-
2375-
if overlay_alpha is None:
2376-
overlay_alpha = brain_alpha
2377-
if overlay_alpha == 0:
2378-
smoothing_steps = 1 # Disable smoothing to save time.
2379-
2380-
title = subject if len(hemis) > 1 else '%s - %s' % (subject, hemis[0])
2381-
kwargs = {
2382-
"subject_id": subject, "hemi": hemi, "surf": 'white',
2383-
"title": title, "cortex": cortex, "size": size,
2384-
"background": background, "foreground": foreground,
2385-
"figure": figure, "subjects_dir": subjects_dir,
2386-
"views": views, "alpha": brain_alpha,
2387-
}
2388-
if _get_3d_backend() in ['pyvista', 'notebook']:
2389-
kwargs["show"] = not time_viewer
2390-
else:
2391-
kwargs.update(_check_pysurfer_antialias(Brain))
2392-
with warnings.catch_warnings(record=True): # traits warnings
2393-
brain = Brain(**kwargs)
2394-
del kwargs
2395-
if scale_factor is None:
2396-
# Configure the glyphs scale directly
2397-
width = np.mean([np.ptp(brain.geo[hemi].coords[:, 1])
2398-
for hemi in hemis if hemi in brain.geo])
2399-
scale_factor = 0.025 * width / scale_pts[-1]
2400-
2401-
sd_kwargs = dict(transparent=transparent, verbose=False)
2402-
for hemi in hemis:
2403-
hemi_idx = 0 if hemi == 'lh' else 1
2404-
data = getattr(stc, hemi + '_data')
2405-
vertices = stc.vertices[hemi_idx]
2406-
if len(data) > 0:
2407-
kwargs = {
2408-
"array": data, "colormap": colormap,
2409-
"vertices": vertices,
2410-
"smoothing_steps": smoothing_steps,
2411-
"time": times, "time_label": time_label,
2412-
"alpha": overlay_alpha, "hemi": hemi,
2413-
"colorbar": colorbar,
2414-
"vector_alpha": vector_alpha,
2415-
"scale_factor": scale_factor,
2416-
"verbose": False,
2417-
}
2418-
if initial_time is not None:
2419-
kwargs['initial_time'] = initial_time
2420-
if _get_3d_backend() == "mayavi":
2421-
if surfer_version >= LooseVersion('0.9'):
2422-
kwargs["transparent"] = transparent
2423-
kwargs["min"] = scale_pts[0]
2424-
kwargs["mid"] = scale_pts[1]
2425-
kwargs["max"] = scale_pts[2]
2426-
else:
2427-
kwargs["transparent"] = transparent
2428-
kwargs["fmin"] = scale_pts[0]
2429-
kwargs["fmid"] = scale_pts[1]
2430-
kwargs["fmax"] = scale_pts[2]
2431-
with warnings.catch_warnings(record=True): # traits warnings
2432-
brain.add_data(**kwargs)
2433-
brain.scale_data_colormap(fmin=scale_pts[0], fmid=scale_pts[1],
2434-
fmax=scale_pts[2], **sd_kwargs)
2435-
2436-
if _get_3d_backend() == "mayavi":
2437-
for hemi in hemis:
2438-
for b in brain._brain_list:
2439-
for layer in b['brain'].data.values():
2440-
glyphs = layer['glyphs']
2441-
glyphs.glyph.glyph.scale_factor = scale_factor
2442-
glyphs.glyph.glyph.clamping = False
2443-
glyphs.glyph.glyph.range = (0., 1.)
2444-
2445-
# depth peeling patch
2446-
if brain_alpha < 1.0:
2447-
for ff in brain._figures:
2448-
for f in ff:
2449-
if f.scene is not None and sys.platform != 'darwin':
2450-
f.scene.renderer.use_depth_peeling = True
2451-
else:
2452-
if brain_alpha < 1.0 and sys.platform != 'darwin':
2453-
brain.enable_depth_peeling()
2454-
2455-
_check_time_viewer_compatibility(brain, time_viewer, show_traces)
2456-
2457-
return brain
2406+
return _plot_stc(
2407+
stc, subject=subject, surface='white', hemi=hemi, colormap=colormap,
2408+
time_label=time_label, smoothing_steps=smoothing_steps,
2409+
subjects_dir=subjects_dir, views=views, clim=clim, figure=figure,
2410+
initial_time=initial_time, time_unit=time_unit, background=background,
2411+
time_viewer=time_viewer, colorbar=colorbar, transparent=transparent,
2412+
brain_alpha=brain_alpha, overlay_alpha=overlay_alpha,
2413+
vector_alpha=vector_alpha, cortex=cortex, foreground=foreground,
2414+
size=size, scale_factor=scale_factor, show_traces=show_traces)
24582415

24592416

24602417
@verbose

0 commit comments

Comments
 (0)