Skip to content
Snippets Groups Projects
Commit b48b9427 authored by Paul McCarthy's avatar Paul McCarthy
Browse files

Overlay-specific location preservation logic in DisplayContext is now

in *Opts classes.
parent abd7561c
No related branches found
No related tags found
No related merge requests found
...@@ -77,6 +77,12 @@ class DisplayOpts(props.SyncableHasProperties): ...@@ -77,6 +77,12 @@ class DisplayOpts(props.SyncableHasProperties):
'The getDisplayBounds method must be implemented by subclasses') 'The getDisplayBounds method must be implemented by subclasses')
def transformDisplayLocation(self, propName, oldLoc):
"""
"""
return oldLoc
class Display(props.SyncableHasProperties): class Display(props.SyncableHasProperties):
""" """
""" """
......
...@@ -10,9 +10,7 @@ import logging ...@@ -10,9 +10,7 @@ import logging
import props import props
import fsl.utils.transform as transform import display as fsldisplay
import display as fsldisplay
import volumeopts
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -88,9 +86,6 @@ class DisplayContext(props.SyncableHasProperties): ...@@ -88,9 +86,6 @@ class DisplayContext(props.SyncableHasProperties):
overlayList.addListener('overlays', overlayList.addListener('overlays',
self.__name, self.__name,
self.__overlayListChanged) self.__overlayListChanged)
self.addListener( 'bounds',
self.__name,
self.__boundsChanged)
def getDisplay(self, overlay): def getDisplay(self, overlay):
...@@ -261,33 +256,35 @@ class DisplayContext(props.SyncableHasProperties): ...@@ -261,33 +256,35 @@ class DisplayContext(props.SyncableHasProperties):
def __displayOptsChanged(self, value, valid, opts, name): def __displayOptsChanged(self, value, valid, opts, name):
"""Called when the :class:`.DisplayOpts` properties of any overlay """Called when the :class:`.DisplayOpts` properties of any overlay
change. Updates the :attr:`bounds` property. change. If the bounds o the Updates the :attr:`bounds` property and,
if the currently selected
""" """
# This check is ugly, and is required due to # This check is ugly, and is required due to
# an ugly circular relationship which exists # an ugly circular relationship which exists
# between parent/child DCs and the transform/ # between parent/child DCs and the *Opts/
# location properties: # location properties:
# #
# 1. When the transform property of a child DC # 1. When a property of a child DC DisplayOpts
# Display object changes (this should always # object changes (e.g. ImageOpts.transform)
# be due to user input), that change is # this should always be due to user input),
# propagated to the parent DC Display object. # that change is propagated to the parent DC
# DisplayOpts object.
# #
# 2. This results in the DC._transformChanged # 2. This results in the DC._displayOptsChanged
# method (this method) being called on the # method (this method) being called on the
# parent DC. # parent DC.
# #
# 3. Said method correctly updates the DC.location # 3. Said method correctly updates the DC.location
# property, so that the world location of the # property, so that the world location of the
# selected image is preserved. # selected overlay is preserved.
# #
# 4. This location update is propagated back to # 4. This location update is propagated back to
# the child DC.location property, which is # the child DC.location property, which is
# updated to have the new correct location # updated to have the new correct location
# value. # value.
# #
# 5. Then, the child DC._transformChanged method # 5. Then, the child DC._displayOpteChanged method
# is called, which goes and updates the child # is called, which goes and updates the child
# DC.location property to contain a bogus # DC.location property to contain a bogus
# value. # value.
...@@ -301,37 +298,40 @@ class DisplayContext(props.SyncableHasProperties): ...@@ -301,37 +298,40 @@ class DisplayContext(props.SyncableHasProperties):
if self.getParent().location == self.location: if self.getParent().location == self.location:
return return
overlay = opts.display.getOverlay()
overlay = opts.display.getOverlay() # Save a copy of the location before
refImage = self.getReferenceImage(overlay) # updating the bounds, as the change
# to the bounds may result in the
if refImage is not None: # location being modified
opts = self.getOpts(refImage) oldDispLoc = self.location.xyz
if (overlay != self.getSelectedOverlay()) or \ # Update the display context bounds
not isinstance(opts, volumeopts.ImageOpts): # to take into account any changes
self.__updateBounds() # to individual overlay bounds
self.__updateBounds()
# The main purpose of this method is to preserve
# the current display location in terms of the
# currently selected overlay, when the overlay
# bounds have changed. We don't care about changes
# to the options for other overlays.
if (overlay != self.getSelectedOverlay()):
return return
# If the currently selected overlay is an Image, and its # Now we want to update the display location
# transformation matrix property has changed, we want the # so that it is preserved with respect to the
# current display location to be preserved, in terms of # currently selected overlay.
# the corresponding image world coordinates. newDispLoc = opts.transformDisplayLocation(name, oldDispLoc)
log.debug('Preserving display location in '
'terms of overlay {} ({}.{}): {} -> {}'.format(
overlay,
type(opts).__name__,
name,
oldDispLoc,
newDispLoc))
# Calculate the image world location using
# the old display<-> world transform, then
# transform it back to the new world->display
# transform.
imgWorldLoc = transform.transform(
[self.location.xyz],
opts.getTransform(opts.getLastTransform(), 'world'))[0]
newDispLoc = transform.transform(
[imgWorldLoc],
opts.getTransform('world', 'display'))[0]
# Update the display coordinate
# system bounds, and the location
self.__updateBounds()
self.location.xyz = newDispLoc self.location.xyz = newDispLoc
...@@ -362,14 +362,9 @@ class DisplayContext(props.SyncableHasProperties): ...@@ -362,14 +362,9 @@ class DisplayContext(props.SyncableHasProperties):
self.bounds[:] = [minBounds[0], maxBounds[0], self.bounds[:] = [minBounds[0], maxBounds[0],
minBounds[1], maxBounds[1], minBounds[1], maxBounds[1],
minBounds[2], maxBounds[2]] minBounds[2], maxBounds[2]]
# Update the constraints on the :attr:`location`
def __boundsChanged(self, *a): # property to be aligned with the new bounds
"""Called when the :attr:`bounds` property changes.
Updates the constraints on the :attr:`location` property.
"""
self.location.setLimits(0, self.bounds.xlo, self.bounds.xhi) self.location.setLimits(0, self.bounds.xlo, self.bounds.xhi)
self.location.setLimits(1, self.bounds.ylo, self.bounds.yhi) self.location.setLimits(1, self.bounds.ylo, self.bounds.yhi)
self.location.setLimits(2, self.bounds.zlo, self.bounds.zhi) self.location.setLimits(2, self.bounds.zlo, self.bounds.zhi)
...@@ -112,14 +112,58 @@ class ModelOpts(fsldisplay.DisplayOpts): ...@@ -112,14 +112,58 @@ class ModelOpts(fsldisplay.DisplayOpts):
return opts.getTransform(self.coordSpace, self.transform) return opts.getTransform(self.coordSpace, self.transform)
def __refImageChanged(self, *a): def transformDisplayLocation(self, propName, oldLoc):
newLoc = oldLoc
if propName == 'refImage':
refImage = self.refImage
oldRefImage = self.getLastValue('refImage')
if oldRefImage == 'none':
refOpts = self.displayCtx.getOpts(refImage)
newLoc = transform.transform(
[oldLoc],
refOpts.getTransform(self.coordSpace, 'display'))[0]
elif refImage == 'none':
oldRefOpts = self.displayCtx.getOpts(oldRefImage)
newLoc = transform.transform(
[oldLoc],
oldRefOpts.getTransform('display', self.coordSpace))[0]
elif propName == 'coordSpace':
if self.refImage != 'none':
refOpts = self.displayCtx.getOpts(self.refImage)
worldLoc = transform.transform(
[oldLoc],
refOpts.getTransform(
self.getLastValue('coordSpace'),
'world'))[0]
newLoc = transform.transform(
[worldLoc],
refOpts.getTransform(
'world',
self.coordSpace))[0]
elif propName == 'transform':
if self.refImage != 'none':
refOpts = self.displayCtx.getOpts(self.refImage)
newLoc = refOpts.transformDisplayLocation(propName, oldLoc)
return newLoc
def __refImageChanged(self, *a):
if self.__oldRefImage != 'none': if self.__oldRefImage != 'none':
opts = self.displayCtx.getOpts(self.__oldRefImage) opts = self.displayCtx.getOpts(self.__oldRefImage)
self.unbindProps('transform', opts) self.unbindProps('transform', opts)
self.__oldRefImage = self.refImage self.__oldRefImage = self.refImage
if self.refImage != 'none': if self.refImage != 'none':
opts = self.displayCtx.getOpts(self.refImage) opts = self.displayCtx.getOpts(self.refImage)
self.bindProps('transform', opts) self.bindProps('transform', opts)
......
...@@ -70,7 +70,7 @@ class ImageOpts(fsldisplay.DisplayOpts): ...@@ -70,7 +70,7 @@ class ImageOpts(fsldisplay.DisplayOpts):
overlay = self.overlay overlay = self.overlay
# The display<->* transformation matrices # The display<->* transformation matrices
# are created in the _transformChanged method # are created in the _setupTransforms method
self.__xforms = {} self.__xforms = {}
self.__setupTransforms() self.__setupTransforms()
...@@ -78,19 +78,13 @@ class ImageOpts(fsldisplay.DisplayOpts): ...@@ -78,19 +78,13 @@ class ImageOpts(fsldisplay.DisplayOpts):
if self.overlay.is4DImage(): if self.overlay.is4DImage():
self.setConstraint('volume', 'maxval', overlay.shape[3] - 1) self.setConstraint('volume', 'maxval', overlay.shape[3] - 1)
self.addListener('transform', self.name, self.__transformChanged)
self.__oldTransform = None
self.__transform = self.transform
self.__transformChanged()
# limit resolution to the image dimensions # limit resolution to the image dimensions
self.resolution = min(overlay.pixdim[:3]) self.resolution = min(overlay.pixdim[:3])
self.setConstraint('resolution', 'minval', self.resolution) self.setConstraint('resolution', 'minval', self.resolution)
def destroy(self): def destroy(self):
self.removeListener('transform', self.name) pass
def getDisplayBounds(self): def getDisplayBounds(self):
...@@ -185,24 +179,23 @@ class ImageOpts(fsldisplay.DisplayOpts): ...@@ -185,24 +179,23 @@ class ImageOpts(fsldisplay.DisplayOpts):
return self.__xforms[from_, to] return self.__xforms[from_, to]
def getLastTransform(self): def transformDisplayLocation(self, propName, oldLoc):
"""Returns the most recent value of the :attr:`transform` property,
before its current value.
"""
return self.__oldTransform
def __transformChanged(self, *a): if propName != 'transform':
"""Called when the :attr:`transform` property is changed.""" return oldLoc
# Calculate the image world location using the
# Store references to the previous display related transformation # old display<-> world transform, then transform
# matrices, just in case anything (hint the DisplayContext object) # it back to the new world->display transform.
# needs them for any particular reason (hint: so the DisplayContext worldLoc = transform.transform(
# can preserve the current display location, in terms of image world [oldLoc],
# space, when the transform of the selected image changes) self.getTransform(self.getLastValue('transform'), 'world'))[0]
self.__oldTransform = self.__transform
self.__transform = self.transform newLoc = transform.transform(
[worldLoc],
self.getTransform('world', 'display'))[0]
return newLoc
class VolumeOpts(ImageOpts): class VolumeOpts(ImageOpts):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment