Source code for fsleyes.views.scene3dpanel

#
# scene3dpanel.py - The Scene3DPanel class.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`Scene3DPanel` class, a FSLeyes view which
draws the scene in 3D.
"""


import logging

import wx

import numpy as np

import fsl.transform.affine                as affine
import fsleyes.displaycontext.scene3dopts  as scene3dopts
import fsleyes.gl.wxglscene3dcanvas        as scene3dcanvas
import fsleyes.profiles.scene3dviewprofile as scene3dviewprofile
import fsleyes.actions                     as actions
from . import                                 canvaspanel


log = logging.getLogger(__name__)


[docs]class Scene3DPanel(canvaspanel.CanvasPanel): """The ``Scene3DPanel`` is a :class:`.CanvasPanel` which draws the contents of the :class:`.OverlayList` as a 3D scene. The ``Scene3DPanel`` uses a :class:`.Scene3DCanvas`, which manages all of the GL state and drawing logic. A :class:`.Scene3DViewProfile` instance is used to manage all of the user interaction logic. The scene properties are described and changed via a :class:`.Scene3DOpts` instance, accessible through the :meth:`.CanvasPanel.sceneOpts` property. """
[docs] @staticmethod def defaultLayout(): """Returns a list of control panel types to be added for the default 3D panel layout. """ return ['OverlayDisplayToolBar', 'Scene3DToolBar', 'OverlayListPanel', 'LocationPanel']
[docs] @staticmethod def controlOrder(): """Returns a list of control panel names, specifying the order in which they should appear in the FSLeyes ortho panel settings menu. """ return ['OverlayListPanel', 'LocationPanel', 'OverlayInfoPanel', 'OverlayDisplayPanel', 'CanvasSettingsPanel', 'AtlasPanel', 'OverlayDisplayToolBar', 'Scene3DToolBar', 'FileTreePanel']
[docs] def __init__(self, parent, overlayList, displayCtx, frame): """Create a ``Scene3dPanel``. :arg parent: A :mod:`wx` parent object. :arg overlayList: A :class:`.OverlayList` instance. :arg displayCtx: A :class:`.DisplayContext` instance. :arg frame: The :class:`.FSLeyesFrame` instance. """ sceneOpts = scene3dopts.Scene3DOpts(self) canvaspanel.CanvasPanel.__init__(self, parent, overlayList, displayCtx, frame, sceneOpts) # In 3D, the displaySpace must always be # set to world, regardless of the parent # DC value. This can be overridden manually # however (e.g. through the python shell) displayCtx.detachDisplaySpace() displayCtx.defaultDisplaySpace = 'world' displayCtx.displaySpace = 'world' contentPanel = self.contentPanel self.__canvas = scene3dcanvas.WXGLScene3DCanvas(contentPanel, overlayList, displayCtx) opts = self.__canvas.opts opts.bindProps('pos', displayCtx, 'location') opts.bindProps('showCursor', sceneOpts) opts.bindProps('cursorColour', sceneOpts) opts.bindProps('bgColour', sceneOpts) opts.bindProps('showLegend', sceneOpts) opts.bindProps('legendColour', sceneOpts, 'fgColour') opts.bindProps('labelSize', sceneOpts) opts.bindProps('occlusion', sceneOpts) opts.bindProps('light', sceneOpts) opts.bindProps('lightPos', sceneOpts) opts.bindProps('lightDistance', sceneOpts) opts.bindProps('showLight', sceneOpts) opts.bindProps('zoom', sceneOpts) opts.bindProps('offset', sceneOpts) opts.bindProps('rotation', sceneOpts) opts.bindProps('highDpi', sceneOpts) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(self.__canvas, flag=wx.EXPAND, proportion=1) contentPanel.SetSizer(sizer) self.centrePanelLayout() self.initProfile(scene3dviewprofile.Scene3DViewProfile) self.syncLocation = True
[docs] def destroy(self): """Must be called when this ``Scene3DPanel`` is no longer in use. """ self.__canvas.destroy() self.__canvas = None canvaspanel.CanvasPanel.destroy(self)
[docs] def getGLCanvases(self): """Returns all of the :class:`.SliceCanvas` instances contained within this ``Scene3DPanel``. """ return [self.__canvas]
[docs] def getActions(self): """Overrides :meth:`.ViewPanel.getActions`. Returns a list of actions that can be executed on this ``Scene3DPanel``, and which will be added to its view menu. """ actionz = [self.screenshot, self.movieGif, self.showCommandLineArgs, self.applyCommandLineArgs, None, self.toggleDisplaySync, self.resetDisplay] names = [a.actionName if a is not None else None for a in actionz] return list(zip(names, actionz))
[docs] @actions.action def resetDisplay(self): """An action which resets the current camera configuration (zoom/pan/rotation). See the :meth:`.Scene3DViewProfile.resetDisplay` method. """ self.currentProfile.resetDisplay()
[docs] def getMovieFrame(self, overlay, opts): """Returns the current movie frame. If the :attr:`movieAxis` is ``3`` (e.g. time series), the volume index is returned. Otherwise the current rotation matrix is returned. """ if self.movieAxis == 3: return super(Scene3DPanel, self).getMovieFrame(overlay, opts) else: return np.copy(self.__canvas.opts.rotation)
[docs] def doMovieUpdate(self, overlay, opts): """Overrides :meth:`.CanvasPanel.doMovieUpdate`. For x/y/z axis movies, the scene is rotated. Otherwise (for time) the ``CanvasPanel`` implementation is called. """ if self.movieAxis >= 3: return canvaspanel.CanvasPanel.doMovieUpdate(self, overlay, opts) else: canvas = self.__canvas currot = canvas.opts.rotation rate = float(self.movieRate) rateMin = self.getAttribute('movieRate', 'minval') rateMax = self.getAttribute('movieRate', 'maxval') rate = 0.1 + 0.9 * (rate - rateMin) / (rateMax - rateMin) rate = rate * np.pi / 10 rots = [0, 0, 0] rots[self.movieAxis] = rate xform = affine.axisAnglesToRotMat(*rots) xform = affine.concat(xform, currot) canvas.opts.rotation = xform return np.copy(xform)