diff --git a/fsl/fsleyes/gl/annotations.py b/fsl/fsleyes/gl/annotations.py index 06b9a42b820fbcc75832b3751f86d8b8c858b5f9..e73348e827acb1be0bf7bd7a78bf9d94ed007c2b 100644 --- a/fsl/fsleyes/gl/annotations.py +++ b/fsl/fsleyes/gl/annotations.py @@ -95,21 +95,27 @@ class Annotations(object): def line(self, *args, **kwargs): """Queues a line for drawing - see the :class:`Line` class. """ hold = kwargs.pop('hold', False) - return self.obj(Line(*args, **kwargs), hold) + obj = Line(self.__xax, self.__yax, *args, **kwargs) + + return self.obj(obj, hold) def rect(self, *args, **kwargs): """Queues a rectangle for drawing - see the :class:`Rectangle` class. """ hold = kwargs.pop('hold', False) - return self.obj(Rect(*args, **kwargs), hold) + obj = Rect(self.__xax, self.__yax, *args, **kwargs) + + return self.obj(obj, hold) def grid(self, *args, **kwargs): """Queues a voxel grid for drawing - see the :class:`VoxelGrid` class. """ hold = kwargs.pop('hold', False) - return self.obj(VoxelGrid(*args, **kwargs), hold) + obj = VoxelGrid(self.__xax, self.__yax, *args, **kwargs) + + return self.obj(obj, hold) def selection(self, *args, **kwargs): @@ -117,7 +123,9 @@ class Annotations(object): class. """ hold = kwargs.pop('hold', False) - return self.obj(VoxelSelection(*args, **kwargs), hold) + obj = VoxelSelection(self.__xax, self.__yax, *args, **kwargs) + + return self.obj(obj, hold) def obj(self, obj, hold=False): @@ -236,9 +244,13 @@ class AnnotationObject(globject.GLSimpleObject): :meth:`globject.GLObject.draw` method. """ - def __init__(self, xform=None, colour=None, width=None): + def __init__(self, xax, yax, xform=None, colour=None, width=None): """Create an ``AnnotationObject``. + :arg xax: Initial display X axis + + :arg yax: Initial display Y axis + :arg xform: Transformation matrix which will be applied to all vertex coordinates. @@ -246,7 +258,7 @@ class AnnotationObject(globject.GLSimpleObject): :arg width: Line width to use for the annotation. """ - globject.GLSimpleObject.__init__(self) + globject.GLSimpleObject.__init__(self, xax, yax) self.colour = colour self.width = width @@ -269,13 +281,17 @@ class Line(AnnotationObject): 2D line. """ - def __init__(self, xy1, xy2, *args, **kwargs): + def __init__(self, xax, yax, xy1, xy2, *args, **kwargs): """Create a ``Line`` annotation. The ``xy1`` and ``xy2`` coordinate tuples should be in relation to the axes which map to the horizontal/vertical screen axes on the target canvas. + :arg xax: Initial display X axis + + :arg yax: Initial display Y axis + :arg xy1: Tuple containing the (x, y) coordinates of one endpoint. :arg xy2: Tuple containing the (x, y) coordinates of the second @@ -284,7 +300,7 @@ class Line(AnnotationObject): All other arguments are passed through to :meth:`AnnotationObject.__init__`. """ - AnnotationObject.__init__(self, *args, **kwargs) + AnnotationObject.__init__(self, xax, yax, *args, **kwargs) self.xy1 = xy1 self.xy2 = xy2 @@ -310,18 +326,22 @@ class Rect(AnnotationObject): 2D rectangle. """ - def __init__(self, xy, w, h, *args, **kwargs): + def __init__(self, xax, yax, xy, w, h, *args, **kwargs): """Create a :class:`Rect` annotation. - :arg xy: Tuple specifying bottom left of the rectangle, in the display - coordinate system. - :arg w: Rectangle width. - :arg h: Rectangle height. + :arg xax: Initial display X axis + + :arg yax: Initial display Y axis + + :arg xy: Tuple specifying bottom left of the rectangle, in the display + coordinate system. + :arg w: Rectangle width. + :arg h: Rectangle height. All other arguments are passed through to :meth:`AnnotationObject.__init__`. """ - AnnotationObject.__init__(self, *args, **kwargs) + AnnotationObject.__init__(self, xax, yax, *args, **kwargs) self.xy = xy self.w = w self.h = h @@ -371,6 +391,8 @@ class VoxelGrid(AnnotationObject): def __init__(self, + xax, + yax, selectMask, displayToVoxMat, voxToDisplayMat, @@ -379,6 +401,10 @@ class VoxelGrid(AnnotationObject): **kwargs): """Create a ``VoxelGrid`` annotation. + :arg xax: Initial display X axis + + :arg yax: Initial display Y axis + :arg selectMask: A 3D numpy array, the same shape as the image being annotated (or a sub-space of the image - see the ``offsets`` argument), which is @@ -403,7 +429,7 @@ class VoxelGrid(AnnotationObject): """ kwargs['xform'] = voxToDisplayMat - AnnotationObject.__init__(self, *args, **kwargs) + AnnotationObject.__init__(self, xax, yax, *args, **kwargs) if offsets is None: offsets = [0, 0, 0] @@ -453,6 +479,8 @@ class VoxelSelection(AnnotationObject): def __init__(self, + xax, + yax, selection, displayToVoxMat, voxToDisplayMat, @@ -461,6 +489,10 @@ class VoxelSelection(AnnotationObject): **kwargs): """Create a ``VoxelSelection`` annotation. + :arg xax: Initial display X axis + + :arg yax: Initial display Y axis + :arg selection: A :class:`.Selection` instance which defines the voxels to be highlighted. @@ -484,7 +516,7 @@ class VoxelSelection(AnnotationObject): :meth:`AnnotationObject.__init__` method. """ - AnnotationObject.__init__(self, *args, **kwargs) + AnnotationObject.__init__(self, xax, yax, *args, **kwargs) if offsets is None: offsets = [0, 0, 0] diff --git a/fsl/fsleyes/gl/gllabel.py b/fsl/fsleyes/gl/gllabel.py index b01e27ad31bcd4d451cdff23f2312001def1cc39..a33ecb0a1d4091846d31f72ae6548d753e3a164a 100644 --- a/fsl/fsleyes/gl/gllabel.py +++ b/fsl/fsleyes/gl/gllabel.py @@ -43,20 +43,25 @@ class GLLabel(globject.GLImageObject): """ - def __init__(self, image, display): + def __init__(self, image, display, xax, yax): """Create a ``GLLabel``. :arg image: The :class:`.Image` instance. :arg display: The associated :class:`.Display` instance. + :arg xax: Initial display X axis + :arg yax: Initial display Y axis """ - globject.GLImageObject.__init__(self, image, display) - - lutTexName = '{}_lut'.format(self.name) + globject.GLImageObject.__init__(self, image, display, xax, yax) + lutTexName = '{}_lut'.format(self.name) self.lutTexture = textures.LookupTableTexture(lutTexName) self.imageTexture = None + # The shader attribute will be created + # by the gllabel_funcs module + self.shader = None + self.addListeners() self.refreshLutTexture() @@ -86,7 +91,9 @@ class GLLabel(globject.GLImageObject): """Returns ``True`` if this ``GLLabel`` is ready to be drawn, ``False`` otherwise. """ - return self.imageTexture is not None and self.imageTexture.ready() + return self.shader is not None and \ + self.imageTexture is not None and \ + self.imageTexture.ready() def addListeners(self): diff --git a/fsl/fsleyes/gl/gllinevector.py b/fsl/fsleyes/gl/gllinevector.py index 1549f9bdd5e9eb1313455054f429d2668b074814..fce6f809f1bc95820a2b9e1ac1850031ec482309 100644 --- a/fsl/fsleyes/gl/gllinevector.py +++ b/fsl/fsleyes/gl/gllinevector.py @@ -53,12 +53,13 @@ class GLLineVector(glvector.GLVector): """ - def __init__(self, image, display): + def __init__(self, image, display, xax, yax): """Create a ``GLLineVector`` instance. :arg image: An :class:`.Image` or :class:`.TensorImage` instance. - :arg display: The associated :class:`.Display` instance. + :arg xax: Initial display X axis + :arg yax: Initial display Y axis """ # If the overlay is a TensorImage, use the @@ -70,6 +71,8 @@ class GLLineVector(glvector.GLVector): glvector.GLVector.__init__(self, image, display, + xax, + yax, vectorImage=vecImage, init=lambda: fslgl.gllinevector_funcs.init( self)) diff --git a/fsl/fsleyes/gl/glmodel.py b/fsl/fsleyes/gl/glmodel.py index 0dd61055d9b2631825d14e2eab06a83d2104b9b2..0e348889ee4716e777f7d36b571e24ed68c7e74b 100644 --- a/fsl/fsleyes/gl/glmodel.py +++ b/fsl/fsleyes/gl/glmodel.py @@ -65,16 +65,20 @@ class GLModel(globject.GLObject): """ - def __init__(self, overlay, display): + def __init__(self, overlay, display, xax, yax): """Create a ``GLModel``. :arg overlay: A :class:`.Model` overlay. :arg display: A :class:`.Display` instance defining how the ``overlay`` is to be displayed. + + :arg xax: Initial display X axis + + :arg yax: Initial display Y axis """ - globject.GLObject.__init__(self) + globject.GLObject.__init__(self, xax, yax) self.shader = None self.overlay = overlay diff --git a/fsl/fsleyes/gl/globject.py b/fsl/fsleyes/gl/globject.py index 3c03d030e594c6b14bb1c216246b07a4e20bd83b..72fbc67e0dfc7730cdc7c262d8f5179be7afcd52 100644 --- a/fsl/fsleyes/gl/globject.py +++ b/fsl/fsleyes/gl/globject.py @@ -50,7 +50,7 @@ def getGLObjectType(overlayType): return typeMap.get(overlayType, None) -def createGLObject(overlay, display): +def createGLObject(overlay, display, xax, yax): """Create :class:`GLObject` instance for the given overlay, as specified by the :attr:`.Display.overlayType` property. @@ -58,10 +58,14 @@ def createGLObject(overlay, display): :arg display: A :class:`.Display` instance describing how the overlay should be displayed. + + :arg xax: Initial display X axis + + :arg yax: Initial display Y axis """ ctr = getGLObjectType(display.overlayType) - if ctr is not None: return ctr(overlay, display) + if ctr is not None: return ctr(overlay, display, xax, yax) else: return None @@ -104,7 +108,11 @@ class GLObject(notifier.Notifier): Sub-class implementations must do the following: - - Call :meth:`__init__`. + - Call :meth:`__init__`. A ``GLObject.__init__`` sub-class method must + have the following signature:: + + def __init__(self, overlay, display, xax, yax) + - Call :meth:`notify` whenever its OpenGL representation changes. @@ -132,23 +140,26 @@ class GLObject(notifier.Notifier): """ - def __init__(self): - """Create a :class:`GLObject`. The constructor adds one attribute + def __init__(self, xax, yax): + """Create a :class:`GLObject`. The constructor adds one attribute to this instance, ``name``, which is simply a unique name for this - instance, and gives default values to the ``xax``, ``yax``, and - ``zax`` attributes. + instance, and gives values to the ``xax``, ``yax``, and ``zax`` + attributes. Subclass implementations must call this method, and should also perform any necessary OpenGL initialisation, such as creating textures. + + :arg xax: Initial display X axis + :arg yax: Initial display Y axis """ # Give this instance a name, and set # initial values for the display axes self.name = '{}_{}'.format(type(self).__name__, id(self)) - self.xax = 0 - self.yax = 1 - self.zax = 2 + self.xax = xax + self.yax = yax + self.zax = 3 - xax - yax log.memory('{}.init ({})'.format(type(self).__name__, id(self))) @@ -285,9 +296,9 @@ class GLSimpleObject(GLObject): be called. """ - def __init__(self): + def __init__(self, xax, yax): """Create a ``GLSimpleObject``. """ - GLObject.__init__(self) + GLObject.__init__(self, xax, yax) def ready(self): @@ -315,7 +326,7 @@ class GLImageObject(GLObject): of :class:`.Nifti1` instances. """ - def __init__(self, image, display): + def __init__(self, image, display, xax, yax): """Create a ``GLImageObject``. This constructor adds the following attributes to this instance: @@ -333,9 +344,13 @@ class GLImageObject(GLObject): :arg image: The :class:`.Nifti1` instance :arg display: An associated :class:`.Display` instance. + + :arg xax: Initial display X axis + + :arg yax: Initial display Y axis """ - GLObject.__init__(self) + GLObject.__init__(self, xax, yax) self.image = image self.display = display self.displayOpts = display.getDisplayOpts() diff --git a/fsl/fsleyes/gl/glrgbvector.py b/fsl/fsleyes/gl/glrgbvector.py index 5d290dd506ace290f48abe8b9bebda278f5970ad..4c25bd4f51f9639617c81ad32f3d6120c65e4420 100644 --- a/fsl/fsleyes/gl/glrgbvector.py +++ b/fsl/fsleyes/gl/glrgbvector.py @@ -59,11 +59,13 @@ class GLRGBVector(glvector.GLVector): """ - def __init__(self, image, display): + def __init__(self, image, display, xax, yax): """Create a ``GLRGBVector``. :arg image: An :class:`.Image` or :class:`.TensorImage` instance. :arg display: The associated :class:`.Display` instance. + :arg xax: Initial display X axis + :arg yax: Initial display Y axis """ # If the overlay is a TensorImage, use the @@ -75,6 +77,8 @@ class GLRGBVector(glvector.GLVector): glvector.GLVector.__init__(self, image, display, + xax, + yax, prefilter=np.abs, vectorImage=vecImage, init=lambda: fslgl.glrgbvector_funcs.init( diff --git a/fsl/fsleyes/gl/gltensor.py b/fsl/fsleyes/gl/gltensor.py index 96f8e7d68bbd91d840a1136ea41e9baf33005279..064adc63da314730aa0fc9fe847cfafa28de6ec5 100644 --- a/fsl/fsleyes/gl/gltensor.py +++ b/fsl/fsleyes/gl/gltensor.py @@ -28,7 +28,7 @@ class GLTensor(glvector.GLVector): """ - def __init__(self, image, display): + def __init__(self, image, display, xax, yax): """Create a ``GLTensor``. Calls the :func:`.gl21.gltensor_funcs.init` function. @@ -36,10 +36,16 @@ class GLTensor(glvector.GLVector): :arg display: The :class:`.Display` instance associated with the ``image``. + + :arg xax: Initial display X axis + + :arg yax: Initial display Y axis """ glvector.GLVector.__init__(self, image, display, + xax, + yax, prefilter=np.abs, vectorImage=image.V1(), init=lambda: fslgl.gltensor_funcs.init( diff --git a/fsl/fsleyes/gl/glvector.py b/fsl/fsleyes/gl/glvector.py index a05935eff28486b9befa3428c0df3ed63b7242e6..f47b6fc7a64423e5123da84baa999727b569cb11 100644 --- a/fsl/fsleyes/gl/glvector.py +++ b/fsl/fsleyes/gl/glvector.py @@ -96,6 +96,8 @@ class GLVector(globject.GLImageObject): def __init__(self, image, display, + xax, + yax, prefilter=None, vectorImage=None, init=None): @@ -116,6 +118,10 @@ class GLVector(globject.GLImageObject): :arg display: A :class:`.Display` object which describes how the image is to be displayed. + :arg xax: Initial display X axis + + :arg yax: Initial display Y axis + :arg prefilter: An optional function which filters the data before it is stored as a 3D texture. See :class:`.ImageTexture`. Whether or not this function @@ -145,7 +151,7 @@ class GLVector(globject.GLImageObject): raise ValueError('Image must be 4 dimensional, with 3 volumes ' 'representing the XYZ vector angles') - globject.GLImageObject.__init__(self, image, display) + globject.GLImageObject.__init__(self, image, display, xax, yax) name = self.name diff --git a/fsl/fsleyes/gl/glvolume.py b/fsl/fsleyes/gl/glvolume.py index e04fa19dde427adec8045e175daf1044bbc8dde1..3eb8125ac25889f55ea633c6b813993eed9d02bb 100644 --- a/fsl/fsleyes/gl/glvolume.py +++ b/fsl/fsleyes/gl/glvolume.py @@ -142,16 +142,20 @@ class GLVolume(globject.GLImageObject): """ - def __init__(self, image, display): + def __init__(self, image, display, xax, yax): """Create a ``GLVolume`` object. :arg image: An :class:`.Image` object. :arg display: A :class:`.Display` object which describes how the image is to be displayed. + + :arg xax: Initial display X axis + + :arg yax: Initial display Y axis """ - globject.GLImageObject.__init__(self, image, display) + globject.GLImageObject.__init__(self, image, display, xax, yax) # Add listeners to this image so the view can be # updated when its display properties are changed diff --git a/fsl/fsleyes/gl/slicecanvas.py b/fsl/fsleyes/gl/slicecanvas.py index 0b3d700d17f916fdf69433430243e8093bbfbf12..5ce14d3dfaeec264e907b2c60285a78162f84ec4 100644 --- a/fsl/fsleyes/gl/slicecanvas.py +++ b/fsl/fsleyes/gl/slicecanvas.py @@ -802,10 +802,12 @@ class SliceCanvas(props.HasProperties): self._glObjects.pop(overlay) return - globj = globject.createGLObject(overlay, display) + globj = globject.createGLObject(overlay, + display, + self.xax, + self.yax) if globj is not None: - globj.setAxes(self.xax, self.yax) globj.register(self.name, self._refresh) self._glObjects[overlay] = globj