Skip to content

Commit 55ac67e

Browse files
GuillaumeFavelierlarsoner
authored andcommitted
[MRG] Refactor renderer backend (#6142)
* Refactor renderer backend * Add mayavi import check in backends_3d fixture * First try to increase coverage with empty scene * Clean comments and add license * Update renderer.py * Close the empty scene manually * Try to fix hanging in windows tests by using off_screen_rendering * Testing azure results on mne/viz * Put testing 3d backend context manager private * FIX: Simpler reload * FIX: Simpler conditional * Try another strategy to improve coverage [testing] * Remove unused statements [testing] * Restore default mlab options [testing] * Revert last mayavi testing code
1 parent 4555011 commit 55ac67e

File tree

11 files changed

+317
-167
lines changed

11 files changed

+317
-167
lines changed

conftest.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,20 @@ def _bias_params(evoked, noise_cov, fwd):
108108
if not mne.forward.is_fixed_orient(fwd):
109109
want //= 3
110110
return evoked, fwd, noise_cov, data_cov, want
111+
112+
113+
@pytest.fixture(scope="module",
114+
params=["mayavi"])
115+
def backend_name(request):
116+
yield request.param
117+
118+
119+
@pytest.yield_fixture
120+
def backends_3d(backend_name):
121+
from mne.viz.backends.renderer import _use_test_3d_backend
122+
from mne.viz.backends.tests._utils import has_not_mayavi
123+
if backend_name == 'mayavi':
124+
if has_not_mayavi():
125+
pytest.skip("Test skipped, requires mayavi.")
126+
with _use_test_3d_backend(backend_name):
127+
yield

doc/python_reference.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ Visualization
263263
plot_alignment
264264
snapshot_brain_montage
265265
plot_arrowmap
266+
set_3d_backend
267+
get_3d_backend
268+
use_3d_backend
266269

267270

268271
Preprocessing

mne/viz/_3d.py

Lines changed: 46 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
combine_transforms, _get_trans, _ensure_trans,
3838
invert_transform, Transform)
3939
from ..utils import (get_subjects_dir, logger, _check_subject, verbose, warn,
40-
SilenceStdout, has_nibabel, check_version,
40+
has_nibabel, check_version,
4141
_ensure_int, _validate_type, _check_option)
4242
from .utils import (mne_analyze_colormap, _prepare_trellis, _get_color_list,
4343
plt_show, tight_layout, figure_nobar, _check_time_unit)
@@ -382,27 +382,22 @@ def plot_evoked_field(evoked, surf_maps, time=None, time_label='t = %0.0f ms',
382382
# Make a solid surface
383383
vlim = np.max(np.abs(data))
384384
alpha = alphas[ii]
385-
with warnings.catch_warnings(record=True): # traits
386-
renderer.surface(surface=surf, color=colors[ii],
387-
opacity=alpha)
385+
renderer.surface(surface=surf, color=colors[ii],
386+
opacity=alpha)
388387

389388
# Now show our field pattern
390-
with warnings.catch_warnings(record=True): # traits
391-
renderer.surface(surface=surf, vmin=-vlim, vmax=vlim,
392-
scalars=data, colormap=colormap)
389+
renderer.surface(surface=surf, vmin=-vlim, vmax=vlim,
390+
scalars=data, colormap=colormap)
393391

394392
# And the field lines on top
395-
with warnings.catch_warnings(record=True): # traits
396-
renderer.contour(surface=surf, scalars=data, contours=21,
397-
vmin=-vlim, vmax=vlim, opacity=alpha,
398-
colormap=colormap_lines)
393+
renderer.contour(surface=surf, scalars=data, contours=21,
394+
vmin=-vlim, vmax=vlim, opacity=alpha,
395+
colormap=colormap_lines)
399396

400397
if '%' in time_label:
401398
time_label %= (1e3 * evoked.times[time_idx])
402-
with warnings.catch_warnings(record=True): # traits
403-
renderer.text(x=0.01, y=0.01, text=time_label, width=0.4)
404-
with SilenceStdout(): # setting roll
405-
renderer.set_camera(azimuth=10, elevation=60)
399+
renderer.text(x=0.01, y=0.01, text=time_label, width=0.4)
400+
renderer.set_camera(azimuth=10, elevation=60)
406401
renderer.show()
407402
return renderer.scene()
408403

@@ -1031,10 +1026,9 @@ def plot_alignment(info=None, trans=None, subject=None, subjects_dir=None,
10311026
rh=(0.5,) * 3)
10321027
colors.update(skull_colors)
10331028
for key, surf in surfs.items():
1034-
with warnings.catch_warnings(record=True): # traits
1035-
renderer.surface(surface=surf, color=colors[key],
1036-
opacity=alphas[key],
1037-
backface_culling=(key != 'helmet'))
1029+
renderer.surface(surface=surf, color=colors[key],
1030+
opacity=alphas[key],
1031+
backface_culling=(key != 'helmet'))
10381032
if brain and 'lh' not in surfs: # one layer sphere
10391033
assert bem['coord_frame'] == FIFF.FIFFV_COORD_HEAD
10401034
center = bem['r0'].copy()
@@ -1086,38 +1080,33 @@ def plot_alignment(info=None, trans=None, subject=None, subjects_dir=None,
10861080

10871081
for data, color, alpha, scale in zip(datas, colors, alphas, scales):
10881082
if len(data) > 0:
1089-
with warnings.catch_warnings(record=True): # traits
1090-
renderer.sphere(center=data, color=color, scale=scale,
1091-
opacity=alpha, backface_culling=True)
1083+
renderer.sphere(center=data, color=color, scale=scale,
1084+
opacity=alpha, backface_culling=True)
10921085
if len(eegp_loc) > 0:
1093-
with warnings.catch_warnings(record=True): # traits
1094-
renderer.quiver3d(
1095-
x=eegp_loc[:, 0], y=eegp_loc[:, 1], z=eegp_loc[:, 2],
1096-
u=eegp_nn[:, 0], v=eegp_nn[:, 1], w=eegp_nn[:, 2],
1097-
color=defaults['eegp_color'], mode='cylinder',
1098-
scale=defaults['eegp_scale'], opacity=0.6,
1099-
glyph_height=defaults['eegp_height'],
1100-
glyph_center=(0., -defaults['eegp_height'], 0),
1101-
glyph_resolution=20,
1102-
backface_culling=True)
1086+
renderer.quiver3d(
1087+
x=eegp_loc[:, 0], y=eegp_loc[:, 1], z=eegp_loc[:, 2],
1088+
u=eegp_nn[:, 0], v=eegp_nn[:, 1], w=eegp_nn[:, 2],
1089+
color=defaults['eegp_color'], mode='cylinder',
1090+
scale=defaults['eegp_scale'], opacity=0.6,
1091+
glyph_height=defaults['eegp_height'],
1092+
glyph_center=(0., -defaults['eegp_height'], 0),
1093+
glyph_resolution=20,
1094+
backface_culling=True)
11031095
if len(meg_rrs) > 0:
11041096
color, alpha = (0., 0.25, 0.5), 0.25
11051097
surf = dict(rr=meg_rrs, tris=meg_tris)
1106-
with warnings.catch_warnings(record=True): # traits
1107-
renderer.surface(surface=surf, color=color,
1108-
opacity=alpha, backface_culling=True)
1098+
renderer.surface(surface=surf, color=color,
1099+
opacity=alpha, backface_culling=True)
11091100
if len(src_rr) > 0:
1110-
with warnings.catch_warnings(record=True): # traits
1111-
renderer.quiver3d(
1112-
x=src_rr[:, 0], y=src_rr[:, 1], z=src_rr[:, 2],
1113-
u=src_nn[:, 0], v=src_nn[:, 1], w=src_nn[:, 2],
1114-
color=(1., 1., 0.), mode='cylinder', scale=3e-3,
1115-
opacity=0.75, glyph_height=0.25,
1116-
glyph_center=(0., 0., 0.), glyph_resolution=20,
1117-
backface_culling=True)
1118-
with SilenceStdout():
1119-
renderer.set_camera(azimuth=90, elevation=90,
1120-
focalpoint=(0., 0., 0.), distance=0.6)
1101+
renderer.quiver3d(
1102+
x=src_rr[:, 0], y=src_rr[:, 1], z=src_rr[:, 2],
1103+
u=src_nn[:, 0], v=src_nn[:, 1], w=src_nn[:, 2],
1104+
color=(1., 1., 0.), mode='cylinder', scale=3e-3,
1105+
opacity=0.75, glyph_height=0.25,
1106+
glyph_center=(0., 0., 0.), glyph_resolution=20,
1107+
backface_culling=True)
1108+
renderer.set_camera(azimuth=90, elevation=90,
1109+
focalpoint=(0., 0., 0.), distance=0.6)
11211110
renderer.show()
11221111
return renderer.scene()
11231112

@@ -2339,12 +2328,11 @@ def plot_sparse_source_estimates(src, stcs, colors=None, linewidth=2,
23392328
color_converter = ColorConverter()
23402329

23412330
renderer = _Renderer(bgcolor=bgcolor, size=(600, 600), name=fig_name)
2342-
with warnings.catch_warnings(record=True): # traits warnings
2343-
surface = renderer.mesh(x=points[:, 0], y=points[:, 1],
2344-
z=points[:, 2], triangles=use_faces,
2345-
color=brain_color, opacity=opacity,
2346-
backface_culling=True, shading=True,
2347-
**kwargs)
2331+
surface = renderer.mesh(x=points[:, 0], y=points[:, 1],
2332+
z=points[:, 2], triangles=use_faces,
2333+
color=brain_color, opacity=opacity,
2334+
backface_culling=True, shading=True,
2335+
**kwargs)
23482336

23492337
# Show time courses
23502338
fig = plt.figure(fig_number)
@@ -2379,10 +2367,9 @@ def plot_sparse_source_estimates(src, stcs, colors=None, linewidth=2,
23792367

23802368
x, y, z = points[v]
23812369
nx, ny, nz = normals[v]
2382-
with warnings.catch_warnings(record=True): # traits
2383-
renderer.quiver3d(x=x, y=y, z=z, u=nx, v=ny, w=nz,
2384-
color=color_converter.to_rgb(c),
2385-
mode=mode, scale=scale_factor)
2370+
renderer.quiver3d(x=x, y=y, z=z, u=nx, v=ny, w=nz,
2371+
color=color_converter.to_rgb(c),
2372+
mode=mode, scale=scale_factor)
23862373

23872374
for k in ind:
23882375
vertno = vertnos[k]
@@ -2521,6 +2508,8 @@ def snapshot_brain_montage(fig, montage, hide_sensors=True):
25212508
# Update the backend
25222509
from .backends.renderer import _Renderer
25232510

2511+
if fig is None:
2512+
raise ValueError('The figure must have a scene')
25242513
if isinstance(montage, (Montage, DigMontage)):
25252514
chs = montage.dig_ch_pos
25262515
ch_names, xyz = zip(*[(ich, ixyz) for ich, ixyz in chs.items()])
@@ -2543,8 +2532,7 @@ def snapshot_brain_montage(fig, montage, hide_sensors=True):
25432532
if hide_sensors is True:
25442533
proj.visible(False)
25452534

2546-
with warnings.catch_warnings(record=True):
2547-
im = renderer.screenshot()
2535+
im = renderer.screenshot()
25482536
proj.visible(True)
25492537
return proj.xy, im
25502538

mne/viz/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@
2323
from .raw import plot_raw, plot_raw_psd, plot_raw_psd_topo
2424
from .ica import (plot_ica_scores, plot_ica_sources, plot_ica_overlay,
2525
_plot_sources_raw, _plot_sources_epochs, plot_ica_properties)
26-
from .montage import plot_montage
26+
from .montage import plot_montage
27+
from .backends.renderer import (set_3d_backend, get_3d_backend, use_3d_backend)
28+
from . import backends

mne/viz/backends/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Visualization backend."""
2+
3+
from . import renderer

0 commit comments

Comments
 (0)