diff --git a/fsl/data/strings.py b/fsl/data/strings.py index 128c87bbfa563c011ebefd0494c894323dcf3cbe..3c91eafb0630fca4c36679f600acca55104583fc 100644 --- a/fsl/data/strings.py +++ b/fsl/data/strings.py @@ -202,10 +202,9 @@ properties = TypeDict({ 'Profile.mode' : 'Profile', - 'CanvasPanel.syncLocation' : 'Sync location', - 'CanvasPanel.syncOverlayOrder' : 'Sync overlay order', - 'CanvasPanel.syncVolume' : 'Sync volume', - 'CanvasPanel.profile' : 'Mode', + 'CanvasPanel.syncLocation' : 'Sync location', + 'CanvasPanel.syncOverlayOrder' : 'Sync overlay order', + 'CanvasPanel.profile' : 'Mode', 'SceneOpts.showCursor' : 'Show location cursor', 'SceneOpts.showColourBar' : 'Show colour bar', diff --git a/fsl/fslview/displaycontext/volumeopts.py b/fsl/fslview/displaycontext/volumeopts.py index 9a90a753632ead44e9aff573fceeab5c38736f1a..a96729b82165c9dfd5d72032d84ba13c9ff48abc 100644 --- a/fsl/fslview/displaycontext/volumeopts.py +++ b/fsl/fslview/displaycontext/volumeopts.py @@ -29,12 +29,12 @@ class ImageOpts(fsldisplay.DisplayOpts): """ - resolution = props.Real(maxval=10, default=1, clamped=True) - """Data resolution in world space. The minimum value is set in __init__.""" + volume = props.Int(minval=0, maxval=0, default=0, clamped=True) + """If the data is 4D , the current volume to display.""" - volume = props.Int(minval=0, maxval=0, default=0, clamped=True) - """If the data is 4D , the current volume to display.""" + resolution = props.Real(maxval=10, default=1, clamped=True) + """Data resolution in world space. The minimum value is set in __init__.""" transform = props.Choice( @@ -60,11 +60,6 @@ class ImageOpts(fsldisplay.DisplayOpts): def __init__(self, *args, **kwargs): - nounbind = kwargs.get('nounbind', []) - nounbind.extend(('transform', 'resolution', 'volume')) - - kwargs['nounbind'] = nounbind - fsldisplay.DisplayOpts.__init__(self, *args, **kwargs) overlay = self.overlay @@ -239,31 +234,6 @@ class VolumeOpts(ImageOpts): invert = props.Boolean(default=False) """Invert the colour map.""" - - _tooltips = { - 'name' : 'The name of this image', - 'enabled' : 'Enable/disable this image', - 'alpha' : 'Opacity, between 0.0 (transparent) ' - 'and 100.0 (opaque)', - 'displayRange' : 'Minimum/maximum display values', - 'clipLow' : 'Do not show image values which are ' - 'lower than the display range', - 'clipHigh' : 'Do not show image values which are ' - 'higher than the display range', - 'interpolation' : 'Interpolate between voxel values at ' - 'each displayed real world location', - 'resolution' : 'Data resolution in voxels', - 'volume' : 'Volume number (for 4D images)', - 'transform' : 'The transformation matrix which specifies the ' - 'conversion from voxel coordinates to a real ' - 'world location', - 'imageType' : 'the type of data contained in the image', - 'cmap' : 'Colour map'} - - - _propHelp = _tooltips - - def __init__(self, overlay, @@ -310,9 +280,6 @@ class VolumeOpts(ImageOpts): self.setConstraint('displayRange', 'minDistance', dMinDistance) - nounbind = kwargs.get('nounbind', []) - nounbind.extend(('interpolation')) - ImageOpts.__init__(self, overlay, display, diff --git a/fsl/fslview/gl/gllabel.py b/fsl/fslview/gl/gllabel.py index 05ff11afa3f4d5912eff9fa1ac5034f68e5871f3..08e2341288c04d1bcc51d641f086cdb729f60bae 100644 --- a/fsl/fslview/gl/gllabel.py +++ b/fsl/fslview/gl/gllabel.py @@ -28,8 +28,7 @@ class GLLabel(globject.GLImageObject): imageTexName, textures.ImageTexture, imageTexName, - image, - display) + image) fslgl.gllabel_funcs.init(self) diff --git a/fsl/fslview/gl/glvector.py b/fsl/fslview/gl/glvector.py index 79b0b49c2aea227add3e83c0e036a41074c2e8fe..cde79f0a459dc74ae25eddeb4ba95c651e817d9a 100644 --- a/fsl/fslview/gl/glvector.py +++ b/fsl/fslview/gl/glvector.py @@ -136,7 +136,6 @@ class GLVector(globject.GLImageObject): textures.ImageTexture, texName, self.image, - display=self.display, nvals=3, normalise=True, prefilter=realPrefilter) @@ -220,7 +219,6 @@ class GLVector(globject.GLImageObject): textures.ImageTexture, texName, modImage, - display=modDisplay, normalise=norm) diff --git a/fsl/fslview/gl/glvolume.py b/fsl/fslview/gl/glvolume.py index 7dd1a2aa7bc1b0d5e3b717a9ea5cf48f6395afe7..64572f20e09eb7f9eb72eb45ca00b4436b1ba55f 100644 --- a/fsl/fslview/gl/glvolume.py +++ b/fsl/fslview/gl/glvolume.py @@ -85,70 +85,17 @@ class GLVolume(globject.GLImageObject): self.addDisplayListeners() # Create an image texture and a colour map texture - texName = '{}_{}'.format(type(self).__name__, id(self.image)) + self.texName = '{}_{}'.format(type(self).__name__, id(self.image)) - # The image texture may be used elsewhere, - # so we'll use the resource management - # module rather than creating one directly - self.imageTexture = glresources.get( - texName, - textures.ImageTexture, - texName, - self.image, - self.display) - - self.colourTexture = textures.ColourMapTexture(texName) - + self.imageTexture = None + self.colourTexture = textures.ColourMapTexture(self.texName) + + self.refreshImageTexture() self.refreshColourTexture() fslgl.glvolume_funcs.init(self) - def preDraw(self): - """Sets up the GL state to draw a slice from this :class:`GLVolume` - instance. - """ - - # Set up the image and colour textures - self.imageTexture .bindTexture(gl.GL_TEXTURE0) - self.colourTexture.bindTexture(gl.GL_TEXTURE1) - - fslgl.glvolume_funcs.preDraw(self) - - - def draw(self, zpos, xform=None): - """Draws a 2D slice of the image at the given real world Z location. - This is performed via a call to the OpenGL version-dependent `draw` - function, contained in one of the :mod:`~fsl.fslview.gl.gl14` or - :mod:`~fsl.fslview.gl.gl21` packages. - - If `xform` is not None, it is applied as an affine transformation to - the vertex coordinates of the rendered image data. - - Note: Calls to this method must be preceded by a call to - :meth:`preDraw`, and followed by a call to :meth:`postDraw`. - """ - - fslgl.glvolume_funcs.draw(self, zpos, xform) - - - def drawAll(self, zposes, xforms): - """Calls the module-specific ``drawAll`` function. """ - - fslgl.glvolume_funcs.drawAll(self, zposes, xforms) - - - def postDraw(self): - """Clears the GL state after drawing from this :class:`GLVolume` - instance. - """ - - self.imageTexture .unbindTexture() - self.colourTexture.unbindTexture() - - fslgl.glvolume_funcs.postDraw(self) - - def destroy(self): """This should be called when this :class:`GLVolume` object is no longer needed. It performs any needed clean up of OpenGL data (e.g. @@ -165,6 +112,32 @@ class GLVolume(globject.GLImageObject): self.removeDisplayListeners() fslgl.glvolume_funcs.destroy(self) + + def refreshImageTexture(self): + + opts = self.displayOpts + texName = self.texName + + unsynced = (not opts.isSyncedToParent('volume') or + not opts.isSyncedToParent('transform') or + not opts.isSyncedToParent('resolution') or + not opts.isSyncedToParent('interpolation')) + + if unsynced: + texName = '{}_unsync_{}'.format(texName, id(opts)) + + if self.imageTexture is not None: + glresources.delete(self.imageTexture.getTextureName()) + + # The image texture may be used elsewhere, + # so we'll use the resource management + # module rather than creating one directly + self.imageTexture = glresources.get( + texName, + textures.ImageTexture, + texName, + self.image) + def refreshColourTexture(self): """Configures the colour texture used to colour image voxels.""" @@ -209,31 +182,44 @@ class GLVolume(globject.GLImageObject): fslgl.glvolume_funcs.updateShaderState(self) self.onUpdate() - def interpUpdate(*a): - - if opts.interpolation == 'none': interp = gl.GL_NEAREST - else: interp = gl.GL_LINEAR - - self.imageTexture.setInterpolation(interp) + def shaderCompile(*a): + fslgl.glvolume_funcs.compileShaders( self) fslgl.glvolume_funcs.updateShaderState(self) self.onUpdate() - def shaderCompile(*a): - fslgl.glvolume_funcs.compileShaders( self) + def imageRefresh(*a): + self.refreshImageTexture() + self.onUpdate() + + def imageUpdate(*a): + volume = opts.volume + resolution = opts.resolution + + if opts.interpolation == 'none': interp = gl.GL_NEAREST + else: interp = gl.GL_LINEAR + + self.imageTexture.set(volume=volume, + interp=interp, + resolution=resolution) + fslgl.glvolume_funcs.updateShaderState(self) self.onUpdate() def update(*a): self.onUpdate() - display.addListener('softwareMode', lName, shaderCompile) - display.addListener('alpha', lName, colourUpdate) - opts .addListener('interpolation', lName, interpUpdate) - opts .addListener('resolution', lName, update) - opts .addListener('displayRange', lName, colourUpdate) - opts .addListener('clippingRange', lName, shaderUpdate) - opts .addListener('cmap', lName, colourUpdate) - opts .addListener('invert', lName, colourUpdate) + display.addListener( 'softwareMode', lName, shaderCompile) + display.addListener( 'alpha', lName, colourUpdate) + opts .addListener( 'displayRange', lName, colourUpdate) + opts .addListener( 'clippingRange', lName, shaderUpdate) + opts .addListener( 'cmap', lName, colourUpdate) + opts .addListener( 'invert', lName, colourUpdate) + opts .addListener( 'volume', lName, imageUpdate) + opts .addListener( 'resolution', lName, imageUpdate) + opts .addListener( 'interpolation', lName, imageUpdate) + opts .addSyncChangeListener('volume', lName, imageRefresh) + opts .addSyncChangeListener('resolution', lName, imageRefresh) + opts .addSyncChangeListener('interpolation', lName, imageRefresh) def removeDisplayListeners(self): @@ -246,11 +232,60 @@ class GLVolume(globject.GLImageObject): lName = self.name - display.removeListener('softwareMode', lName) - display.removeListener('alpha', lName) - opts .removeListener('interpolation', lName) - opts .removeListener('resolution', lName) - opts .removeListener('displayRange', lName) - opts .removeListener('clippingRange', lName) - opts .removeListener('cmap', lName) - opts .removeListener('invert', lName) + display.removeListener( 'softwareMode', lName) + display.removeListener( 'alpha', lName) + opts .removeListener( 'displayRange', lName) + opts .removeListener( 'clippingRange', lName) + opts .removeListener( 'cmap', lName) + opts .removeListener( 'invert', lName) + opts .removeListener( 'volume', lName) + opts .removeListener( 'resolution', lName) + opts .removeListener( 'interpolation', lName) + opts .removeSyncChangeListener('volume', lName) + opts .removeSyncChangeListener('resolution', lName) + opts .removeSyncChangeListener('interpolation', lName) + + + def preDraw(self): + """Sets up the GL state to draw a slice from this :class:`GLVolume` + instance. + """ + + # Set up the image and colour textures + self.imageTexture .bindTexture(gl.GL_TEXTURE0) + self.colourTexture.bindTexture(gl.GL_TEXTURE1) + + fslgl.glvolume_funcs.preDraw(self) + + + def draw(self, zpos, xform=None): + """Draws a 2D slice of the image at the given real world Z location. + This is performed via a call to the OpenGL version-dependent `draw` + function, contained in one of the :mod:`~fsl.fslview.gl.gl14` or + :mod:`~fsl.fslview.gl.gl21` packages. + + If `xform` is not None, it is applied as an affine transformation to + the vertex coordinates of the rendered image data. + + Note: Calls to this method must be preceded by a call to + :meth:`preDraw`, and followed by a call to :meth:`postDraw`. + """ + + fslgl.glvolume_funcs.draw(self, zpos, xform) + + + def drawAll(self, zposes, xforms): + """Calls the module-specific ``drawAll`` function. """ + + fslgl.glvolume_funcs.drawAll(self, zposes, xforms) + + + def postDraw(self): + """Clears the GL state after drawing from this :class:`GLVolume` + instance. + """ + + self.imageTexture .unbindTexture() + self.colourTexture.unbindTexture() + + fslgl.glvolume_funcs.postDraw(self) diff --git a/fsl/fslview/gl/textures/imagetexture.py b/fsl/fslview/gl/textures/imagetexture.py index 943a2bc737a96389c53a7dc126b16c1b2455a939..922f65f97922267fafa697f2262908f5246b3187 100644 --- a/fsl/fslview/gl/textures/imagetexture.py +++ b/fsl/fslview/gl/textures/imagetexture.py @@ -24,14 +24,6 @@ class ImageTexture(texture.Texture): """This class contains the logic required to create and manage a 3D texture which represents a :class:`~fsl.data.image.Image` instance. - On creation, an ``ImageTexture`` instance registers some listeners on the - properties of the :class:`~fsl.data.image.Image` instance (and the - :class:`~fsl.fslview.displaycontext.Display` instance if one is provided), - so that it may re-generate the texture data when these properties - change. For example, if the - :attr:`~fsl.fslview.displaycontext.Display.resolution` property changes, - the image data is re-sampled accordingly. - Once created, the following attributes are available on an :class:`ImageTexture` object: @@ -46,7 +38,6 @@ class ImageTexture(texture.Texture): def __init__(self, name, image, - display=None, nvals=1, normalise=False, prefilter=None, @@ -56,10 +47,6 @@ class ImageTexture(texture.Texture): :arg name: A name for the texture. :arg image: The :class:`~fsl.data.image.Image` instance. - - :arg display: A :class:`~fsl.fslview.displaycontext.Display` - instance which defines how the image is to be - displayed, or ``None`` if this image has no display. :arg nvals: Number of values per voxel. For example. a normal MRI or fMRI image contains only one value for each voxel. @@ -83,97 +70,91 @@ class ImageTexture(texture.Texture): 'size {} requested for ' 'image shape {}'.format(nvals, image.shape)) - self.image = image - self.display = display - self.nvals = nvals - self.prefilter = prefilter + self.image = image + self.nvals = nvals + self.__interp = None + self.__prefilter = None + self.__resolution = None + self.__volume = None + self.__normalise = None - dtype = image.data.dtype + self.__name = '{}_{}'.format(type(self).__name__, id(self)) + self.image.addListener('data', + self.__name, + lambda *a: self.__imageDataChanged()) - # If the normalise flag is true, or the data is - # of a type which cannot be stored natively as - # an OpenGL texture, the data is cast to a - # standard type, and normalised - see - # _determineTextureType and _prepareTextureData - self.normalise = normalise or dtype not in (np.uint8, - np.int8, - np.uint16, - np.int16) + self.__imageDataChanged(False) - texFmt, intFmt, texDtype, voxValXform = self._determineTextureType() - - self.texFmt = texFmt - self.texIntFmt = intFmt - self.texDtype = texDtype - self.voxValXform = voxValXform - self.invVoxValXform = transform.invert(voxValXform) - - self._addListeners() - - self.setInterpolation(interp) - self.refresh() + self.set(interp=interp, + prefilter=prefilter, + resolution=None, + volume=None, + normalise=normalise) def destroy(self): - """Deletes the texture identifier, and removes any property - listeners which were registered on the ``.Image`` and ``.Display`` - instances. - """ + """Deletes the texture identifier """ texture.Texture.destroy(self) - self._removeListeners() + self.image.removeListener('data', self.__name) - - def setPrefilter(self, prefilter): - """Updates the method used to pre-filter the data, and refreshes the - texture. - - See :meth:`__init__`. - """ - - changed = self.prefilter is not prefilter - self.prefilter = prefilter - - if changed: - self.refresh() - - def _addListeners(self): - """Adds listeners to some properties of the ``Image`` and ``Display`` - instances, so this ``ImageTexture`` can re-generate texture data - when necessary. - """ + def __imageDataChanged(self, refresh=True): - image = self.image - display = self.display - name = '{}_{}'.format(type(self).__name__, id(self)) + data = self.image.data - image.addListener('data', name, self.refresh) + if self.__prefilter is not None: + data = self.__prefilter(data) + + self.__dataMin = float(data.min()) + self.__dataMax = float(data.max()) - if display is not None: - opts = display.getDisplayOpts() - - opts.addListener('volume', name, self.refresh) - opts.addListener('resolution', name, self.refresh) + if refresh: + self.refresh() - def _removeListeners(self): - """Called by the :meth:`destroy` method. Removes the property - listeners which were configured by the :meth:`_addListeners` - method. - """ + def set(self, **kwargs): + interp = kwargs.get('interp', self.__interp) + prefilter = kwargs.get('prefilter', self.__prefilter) + resolution = kwargs.get('resolution', self.__resolution) + volume = kwargs.get('volume', self.__volume) + normalise = kwargs.get('normalise', self.__normalise) + + changed = (interp != self.__interp or + prefilter != self.__prefilter or + resolution != self.__resolution or + volume != self.__volume or + normalise != self.__normalise) + + if not changed: + return + + if prefilter != self.__prefilter: + self.__imageDataChanged() + + self.__interp = interp + self.__prefilter = prefilter + self.__resolution = resolution + self.__volume = volume + + # If the data is of a type which cannot be + # stored natively as an OpenGL texture, the + # data is cast to a standard type, and + # normalised - see _determineTextureType + # and _prepareTextureData + dtype = self.image.data.dtype + self.__normalise = normalise or dtype not in (np.uint8, + np.int8, + np.uint16, + np.int16) + self.refresh() - image = self.image - display = self.display - name = '{}_{}'.format(type(self).__name__, id(self)) - - image.removeListener('data', name) - if display is not None: - opts = display.getDisplayOpts() - - opts .removeListener('volume', name) - opts .removeListener('resolution', name) + def setInterp( self, interp): self.set(interp=interp) + def setPrefilter( self, prefilter): self.set(prefilter=prefilter) + def setResolution(self, resolution): self.set(resolution=resolution) + def setVolume( self, volume): self.set(volume=volume) + def setNormalise( self, normalise): self.set(normalise=normalise) def _determineTextureType(self): @@ -205,28 +186,34 @@ class ImageTexture(texture.Texture): to the appropriate range, and calculate a transformation matrix to transform back to the data range. - This method returns a tuple containing the following: + This method sets the following attributes on thius ``ImageTexture`` + instance: - - The texture format (e.g. ``GL_RGB``, ``GL_LUMINANCE``) + - ``texFmt``: The texture format (e.g. ``GL_RGB``, + ``GL_LUMINANCE``) - - The internal texture format used by OpenGL for storage (e.g. - ``GL_RGB16``, ``GL_LUMINANCE8``). + - ``texIntFmt``: The internal texture format used by OpenGL for + storage (e.g. ``GL_RGB16``, ``GL_LUMINANCE8``). - - The raw type of the texture data (e.g. ``GL_UNSIGNED_SHORT``) + - ``texDtype``: The raw type of the texture data (e.g. + ``GL_UNSIGNED_SHORT``) - - An affine transformation matrix which encodes an offset and a - scale, which may be used to transform the texture data from - the range [0.0, 1.0] to its original range. + - ``voxValXform``: An affine transformation matrix which encodes + an offset and a scale, which may be used to + transform the texture data from the range + [0.0, 1.0] to its original range. + + - ``invVoxValXform``: Inverse of ``voxValXform``. """ data = self.image.data - if self.prefilter is not None: - data = self.prefilter(data) + if self.__prefilter is not None: + data = self.__prefilter(data) dtype = data.dtype - dmin = float(data.min()) - dmax = float(data.max()) + dmin = self.__dataMin + dmax = self.__dataMax # Signed data types are a pain in the arse. # @@ -235,7 +222,7 @@ class ImageTexture(texture.Texture): # for signed types. # Texture data type - if self.normalise: texDtype = gl.GL_UNSIGNED_BYTE + if self.__normalise: texDtype = gl.GL_UNSIGNED_BYTE elif dtype == np.uint8: texDtype = gl.GL_UNSIGNED_BYTE elif dtype == np.int8: texDtype = gl.GL_UNSIGNED_BYTE elif dtype == np.uint16: texDtype = gl.GL_UNSIGNED_SHORT @@ -254,28 +241,28 @@ class ImageTexture(texture.Texture): # Internal texture format if self.nvals == 1: - if self.normalise: intFmt = gl.GL_LUMINANCE8 + if self.__normalise: intFmt = gl.GL_LUMINANCE8 elif dtype == np.uint8: intFmt = gl.GL_LUMINANCE8 elif dtype == np.int8: intFmt = gl.GL_LUMINANCE8 elif dtype == np.uint16: intFmt = gl.GL_LUMINANCE16 elif dtype == np.int16: intFmt = gl.GL_LUMINANCE16 elif self.nvals == 2: - if self.normalise: intFmt = gl.GL_LUMINANCE8_ALPHA8 + if self.__normalise: intFmt = gl.GL_LUMINANCE8_ALPHA8 elif dtype == np.uint8: intFmt = gl.GL_LUMINANCE8_ALPHA8 elif dtype == np.int8: intFmt = gl.GL_LUMINANCE8_ALPHA8 elif dtype == np.uint16: intFmt = gl.GL_LUMINANCE16_ALPHA16 elif dtype == np.int16: intFmt = gl.GL_LUMINANCE16_ALPHA16 elif self.nvals == 3: - if self.normalise: intFmt = gl.GL_RGB8 + if self.__normalise: intFmt = gl.GL_RGB8 elif dtype == np.uint8: intFmt = gl.GL_RGB8 elif dtype == np.int8: intFmt = gl.GL_RGB8 elif dtype == np.uint16: intFmt = gl.GL_RGB16 elif dtype == np.int16: intFmt = gl.GL_RGB16 elif self.nvals == 4: - if self.normalise: intFmt = gl.GL_RGBA8 + if self.__normalise: intFmt = gl.GL_RGBA8 elif dtype == np.uint8: intFmt = gl.GL_RGBA8 elif dtype == np.int8: intFmt = gl.GL_RGBA8 elif dtype == np.uint16: intFmt = gl.GL_RGBA16 @@ -284,13 +271,13 @@ class ImageTexture(texture.Texture): # Offsets/scales which can be used to transform from # the texture data (which may be offset or normalised) # back to the original voxel data - if self.normalise: offset = dmin + if self.__normalise: offset = dmin elif dtype == np.uint8: offset = 0 elif dtype == np.int8: offset = -128 elif dtype == np.uint16: offset = 0 elif dtype == np.int16: offset = -32768 - if self.normalise: scale = dmax - dmin + if self.__normalise: scale = dmax - dmin elif dtype == np.uint8: scale = 255 elif dtype == np.int8: scale = 255 elif dtype == np.uint16: scale = 65535 @@ -337,11 +324,15 @@ class ImageTexture(texture.Texture): sTexDtype, sTexFmt, sIntFmt, - self.normalise, + self.__normalise, scale, offset)) - return texFmt, intFmt, texDtype, voxValXform + self.texFmt = texFmt + self.texIntFmt = intFmt + self.texDtype = texDtype + self.voxValXform = voxValXform + self.invVoxValXform = transform.invert(voxValXform) def _prepareTextureData(self): @@ -363,31 +354,28 @@ class ImageTexture(texture.Texture): be used as-is). """ - image = self.image - display = self.display + image = self.image + data = image.data + dtype = data.dtype - dtype = image.data.dtype + volume = self.__volume + resolution = self.__resolution + prefilter = self.__prefilter + normalise = self.__normalise - if display is None: - data = image.data - - else: - opts = display.getDisplayOpts() + if volume is None: + volume = 0 + + if image.is4DImage() and self.nvals == 1: + data = data[..., volume] + + if resolution is not None: + data = glroutines.subsample(data, resolution, image.pixdim)[0] - if self.nvals == 1 and image.is4DImage(): - data = glroutines.subsample(image.data, - opts.resolution, - image.pixdim, - opts.volume)[0] - else: - data = glroutines.subsample(image.data, - opts.resolution, - image.pixdim)[0] - - if self.prefilter is not None: - data = self.prefilter(data) + if prefilter is not None: + data = prefilter(data) - if self.normalise: + if normalise: dmin = float(data.min()) dmax = float(data.max()) if dmax != dmin: @@ -402,23 +390,13 @@ class ImageTexture(texture.Texture): return data - - def setInterpolation(self, interp): - """Sets the texture interpolation method.""" - - self.bindTexture() - - gl.glTexParameteri(gl.GL_TEXTURE_3D, gl.GL_TEXTURE_MAG_FILTER, interp) - gl.glTexParameteri(gl.GL_TEXTURE_3D, gl.GL_TEXTURE_MIN_FILTER, interp) - - self.unbindTexture() - def refresh(self, *a): """(Re-)generates the OpenGL image texture used to store the image data. """ + self._determineTextureType() data = self._prepareTextureData() # It is assumed that, for textures with more than one @@ -446,6 +424,14 @@ class ImageTexture(texture.Texture): self.bindTexture() + # set interpolation routine + gl.glTexParameteri(gl.GL_TEXTURE_3D, + gl.GL_TEXTURE_MAG_FILTER, + self.__interp) + gl.glTexParameteri(gl.GL_TEXTURE_3D, + gl.GL_TEXTURE_MIN_FILTER, + self.__interp) + # Clamp texture borders to the edge # values - it is the responsibility # of the rendering logic to not draw diff --git a/fsl/fslview/layouts.py b/fsl/fslview/layouts.py index bfdc2f170e25c42366e54c092cb87e9205ae8cd8..d293161643ab068378f57dcd27b33326b90bc0d1 100644 --- a/fsl/fslview/layouts.py +++ b/fsl/fslview/layouts.py @@ -203,9 +203,9 @@ VolumeOptsLayout = props.VGroup( MaskOptsLayout = props.VGroup( - (widget(VolumeOpts, 'resolution', showLimits=False), - widget(VolumeOpts, 'transform'), - widget(VolumeOpts, 'volume', showLimits=False), + (widget(MaskOpts, 'resolution', showLimits=False), + widget(MaskOpts, 'transform'), + widget(MaskOpts, 'volume', showLimits=False), widget(MaskOpts, 'colour'), widget(MaskOpts, 'invert'), widget(MaskOpts, 'threshold', showLimits=False))) @@ -214,7 +214,6 @@ MaskOptsLayout = props.VGroup( RGBVectorOptsLayout = props.VGroup(( widget(RGBVectorOpts, 'resolution', showLimits=False), widget(RGBVectorOpts, 'transform'), - widget(RGBVectorOpts, 'volume', showLimits=False), widget(RGBVectorOpts, 'interpolation'), props.HGroup(( widget(RGBVectorOpts, 'xColour'), @@ -232,7 +231,6 @@ RGBVectorOptsLayout = props.VGroup(( LineVectorOptsLayout = props.VGroup(( widget(LineVectorOpts, 'resolution', showLimits=False), widget(LineVectorOpts, 'transform'), - widget(LineVectorOpts, 'volume', showLimits=False), props.HGroup(( widget(LineVectorOpts, 'xColour'), widget(LineVectorOpts, 'yColour'), diff --git a/fsl/fslview/views/canvaspanel.py b/fsl/fslview/views/canvaspanel.py index c9e44f0e90a1acd17738ffcdd1a06cdacd7c3a45..4344dbde0747ce5651431e5b38e525f9b8718157 100644 --- a/fsl/fslview/views/canvaspanel.py +++ b/fsl/fslview/views/canvaspanel.py @@ -164,13 +164,8 @@ class CanvasPanel(viewpanel.ViewPanel): """ """ - syncLocation = displayctx.DisplayContext.getSyncProperty('location') - # syncVolume = displayctx.DisplayContext.getSyncProperty('volume') - - # TODO - syncVolume = props.Boolean(default=True) - syncOverlayOrder = displayctx.DisplayContext.getSyncProperty( - 'overlayOrder') + syncLocation = props.Boolean(default=True) + syncOverlayOrder = props.Boolean(default=True) def __init__(self, parent, @@ -212,10 +207,6 @@ class CanvasPanel(viewpanel.ViewPanel): self.bindProps('syncOverlayOrder', displayCtx, displayCtx.getSyncPropertyName('overlayOrder')) - - log.warn('Skipping parent syncVolume binding, ' - 'as it is not working yet.') - # If the displayCtx instance does not # have a parent, this means that it is @@ -223,7 +214,6 @@ class CanvasPanel(viewpanel.ViewPanel): else: self.disableProperty('syncLocation') self.disableProperty('syncOverlayOrder') - self.disableProperty('syncVolume') self.__canvasContainer = wx.Panel(self) self.__canvasPanel = wx.Panel(self.__canvasContainer)