def test_set_3d_backend_bad(monkeypatch, tmp_path): """Test that the error emitted when a bad backend name is used.""" match = "Allowed values are 'pyvistaqt' and 'notebook'" with pytest.raises(ValueError, match=match): set_3d_backend('invalid') # gh-9607 def fail(x): raise ModuleNotFoundError(x) monkeypatch.setattr('mne.viz.backends.renderer._reload_backend', fail) monkeypatch.setattr('mne.viz.backends.renderer.MNE_3D_BACKEND', None) match = 'Could not load any valid 3D.*\npyvistaqt: .*' assert get_3d_backend() is None with pytest.raises(RuntimeError, match=match): _get_renderer()
def plot_nirs_source_detector(data, info=None, radius=0.001, trans=None, subject=None, subjects_dir=None, surfaces='head', coord_frame='head', meg=None, eeg='original', fwd=None, dig=False, ecog=True, src=None, mri_fiducials=False, bem=None, seeg=True, fnirs=False, show_axes=False, fig=None, cmap=None, interaction='trackball', verbose=None): """ 3D visualisation of fNIRS response magnitude. This function plots the response amplitude for each channel. Each channel is represented by a line between the source and detector, the color of the line reflects the response magnitude. Parameters ---------- data : array Array of values to be plotted between source and detectors. One value should be specified per channel in the same order as `info.chs`. info : dict | None The measurement info. If None (default), no sensor information will be shown. radius : numbers Tube radius for connecting links. %(trans)s subject : str | None The subject name corresponding to FreeSurfer environment variable SUBJECT. Can be omitted if ``src`` is provided. %(subjects_dir)s surfaces : str | list Surfaces to plot. Supported values: * scalp: one of 'head', 'outer_skin' (alias for 'head'), 'head-dense', or 'seghead' (alias for 'head-dense') * skull: 'outer_skull', 'inner_skull', 'brain' (alias for 'inner_skull') * brain: one of 'pial', 'white', 'inflated', or 'brain' (alias for 'pial'). Defaults to 'head'. .. note:: For single layer BEMs it is recommended to use 'brain'. coord_frame : str Coordinate frame to use, 'head', 'meg', or 'mri'. meg : str | list | bool | None Can be "helmet", "sensors" or "ref" to show the MEG helmet, sensors or reference sensors respectively, or a combination like ``('helmet', 'sensors')`` (same as None, default). True translates to ``('helmet', 'sensors', 'ref')``. eeg : bool | str | list String options are: - "original" (default; equivalent to ``True``) Shows EEG sensors using their digitized locations (after transformation to the chosen ``coord_frame``) - "projected" The EEG locations projected onto the scalp, as is done in forward modeling Can also be a list of these options, or an empty list (``[]``, equivalent of ``False``). fwd : instance of Forward The forward solution. If present, the orientations of the dipoles present in the forward solution are displayed. dig : bool | 'fiducials' If True, plot the digitization points; 'fiducials' to plot fiducial points only. ecog : bool If True (default), show ECoG sensors. src : instance of SourceSpaces | None If not None, also plot the source space points. mri_fiducials : bool | str Plot MRI fiducials (default False). If ``True``, look for a file with the canonical name (``bem/{subject}-fiducials.fif``). If ``str`` it should provide the full path to the fiducials file. bem : list of dict | instance of ConductorModel | None Can be either the BEM surfaces (list of dict), a BEM solution or a sphere model. If None, we first try loading `'$SUBJECTS_DIR/$SUBJECT/bem/$SUBJECT-$SOURCE.fif'`, and then look for `'$SUBJECT*$SOURCE.fif'` in the same directory. For `'outer_skin'`, the subjects bem and bem/flash folders are searched. Defaults to None. seeg : bool If True (default), show sEEG electrodes. fnirs : bool If True (default), show fNIRS electrodes. show_axes : bool If True (default False), coordinate frame axis indicators will be shown: * head in pink. * MRI in gray (if ``trans is not None``). * MEG in blue (if MEG sensors are present). .. versionadded:: 0.16 fig : mayavi.mlab.Figure | None Mayavi Scene in which to plot the alignment. If ``None``, creates a new 600x600 pixel figure with black background. .. versionadded:: 0.16 cmap : str Colormap to be used. interaction : str Can be "trackball" (default) or "terrain", i.e. a turntable-style camera. .. versionadded:: 0.16 %(verbose)s Returns ------- fig : Figure The 3D figure. Notes ----- For more information see :func:`mne.viz.plot_alignment`. .. versionadded:: 0.15 """ # Determine range of values for creating colormap vmin = np.min(data) vmax = np.max(data) # If no colormap is specified choose depending on range of data if cmap is None: if (vmin >= 0) & (vmax >= 0): # For positive only data use magma cmap = 'Oranges' else: # Otherwise use blue to red and ensure zero sits at white vmin = -1. * np.max(np.abs(data)) vmax = np.max(np.abs(data)) cmap = 'RdBu_r' if isinstance(radius, (int, float)): radius = np.ones(len(info['chs'])) * radius # Plot requested alignment fig = plot_alignment(info=info, trans=trans, subject=subject, subjects_dir=subjects_dir, surfaces=surfaces, coord_frame=coord_frame, meg=meg, eeg=eeg, fwd=fwd, dig=dig, ecog=ecog, src=src, mri_fiducials=mri_fiducials, bem=bem, seeg=seeg, fnirs=fnirs, show_axes=show_axes, fig=fig, interaction=interaction, verbose=verbose) from mne.viz.backends.renderer import _get_renderer renderer = _get_renderer(fig) # Overlay channels between source and detectors for idx, ch in enumerate(info['chs']): locs = ch['loc'] renderer.tube(origin=[np.array([locs[3], locs[4], locs[5]])], destination=[np.array([locs[6], locs[7], locs[8]])], scalars=np.array([[1.0, 1.0]]) * data[idx], radius=radius[idx], colormap=cmap, vmin=vmin, vmax=vmax) t = renderer.tube(origin=[np.array([0, 0, 0])], destination=[np.array([0, 0, 0.001])], scalars=np.array([[vmin, vmax]]), radius=0.0001, colormap=cmap, vmin=vmin, vmax=vmax) renderer.scalarbar(t) return fig