diff --git a/fsl/fsleyes/displaycontext/canvasopts.py b/fsl/fsleyes/displaycontext/canvasopts.py index 94a99d17c0d0364c58edebf791fc218cd623d0d4..2f7e214eac651cf182c4f874e67aa8df203fbe00 100644 --- a/fsl/fsleyes/displaycontext/canvasopts.py +++ b/fsl/fsleyes/displaycontext/canvasopts.py @@ -36,7 +36,7 @@ class SliceCanvasOpts(props.HasProperties): zoom = props.Percentage(minval=100.0, - maxval=1000.0, + maxval=5000.0, default=100.0, clamped=True) """The :attr:`.DisplayContext.bounds` are divided by this zoom diff --git a/fsl/fsleyes/displaycontext/lightboxopts.py b/fsl/fsleyes/displaycontext/lightboxopts.py index 5189ec75790e175d9ef5e4df70d2c4d845753952..6c40f30ad2b5354df1357a78daa51f2dda7c5449 100644 --- a/fsl/fsleyes/displaycontext/lightboxopts.py +++ b/fsl/fsleyes/displaycontext/lightboxopts.py @@ -48,6 +48,7 @@ class LightBoxOpts(sceneopts.SceneOpts): """ sceneopts.SceneOpts.__init__(self, *args, **kwargs) self.setConstraint('zoom', 'minval', 10) + self.setConstraint('zoom', 'maxval', 1000) def _onPerformanceChange(self, *a): diff --git a/fsl/fsleyes/gl/lightboxcanvas.py b/fsl/fsleyes/gl/lightboxcanvas.py index 5879c5854215141249fb59a20a733076aae7520f..fd05154b074bf410d31be543e9866a3327940de5 100644 --- a/fsl/fsleyes/gl/lightboxcanvas.py +++ b/fsl/fsleyes/gl/lightboxcanvas.py @@ -478,7 +478,7 @@ class LightBoxCanvas(slicecanvas.SliceCanvas): self._genSliceLocations() - def _updateDisplayBounds(self): + def _updateDisplayBounds(self, *args, **kwargs): """Overrides :meth:`.SliceCanvas._updateDisplayBounds`. Called on canvas resizes, display bound changes and lightbox slice diff --git a/fsl/fsleyes/gl/slicecanvas.py b/fsl/fsleyes/gl/slicecanvas.py index 25b1425714dd24296ab4df19410ba4e39ce598a9..08068e11dc54dd3064ac495113b9c15ff794a75e 100644 --- a/fsl/fsleyes/gl/slicecanvas.py +++ b/fsl/fsleyes/gl/slicecanvas.py @@ -859,15 +859,18 @@ class SliceCanvas(props.HasProperties): """ ovlBounds = self.displayCtx.bounds + oldPos = self.pos.xy + self.disableNotification('pos') self.pos.setMin(0, ovlBounds.getLo(self.xax)) self.pos.setMax(0, ovlBounds.getHi(self.xax)) self.pos.setMin(1, ovlBounds.getLo(self.yax)) self.pos.setMax(1, ovlBounds.getHi(self.yax)) self.pos.setMin(2, ovlBounds.getLo(self.zax)) self.pos.setMax(2, ovlBounds.getHi(self.zax)) + self.enableNotification('pos') - self._updateDisplayBounds() + self._updateDisplayBounds(oldLoc=oldPos) def _zoomChanged(self, *a): @@ -931,10 +934,15 @@ class SliceCanvas(props.HasProperties): return (xmin, xmax, ymin, ymax) - def _updateDisplayBounds(self, xmin=None, xmax=None, ymin=None, ymax=None): + def _updateDisplayBounds(self, + xmin=None, + xmax=None, + ymin=None, + ymax=None, + oldLoc=None): """Called on canvas resizes, overlay bound changes, and zoom changes. - Calculates the bounding box, in world coordinates, to be displayed on + Calculates the bounding box, in display coordinates, to be displayed on the canvas. Stores this bounding box in the displayBounds property. If any of the parameters are not provided, the :attr:`.DisplayContext.bounds` are used. @@ -943,11 +951,20 @@ class SliceCanvas(props.HasProperties): .. note:: This method is used internally, and also by the :class:`.WXGLSliceCanvas` class. + .. warning:: This code assumes that, if the display coordinate system + has changed, the display context location has already + been updated. See the + :meth:`.DisplayContext.__displaySpaceChanged` method. + - :arg xmin: Minimum x (horizontal) value to be in the display bounds. - :arg xmax: Maximum x value to be in the display bounds. - :arg ymin: Minimum y (vertical) value to be in the display bounds. - :arg ymax: Maximum y value to be in the display bounds. + :arg xmin: Minimum x (horizontal) value to be in the display bounds. + :arg xmax: Maximum x value to be in the display bounds. + :arg ymin: Minimum y (vertical) value to be in the display bounds. + :arg ymax: Maximum y value to be in the display bounds. + :arg oldLoc: If provided, should be the ``(x, y)`` location shown on + this ``SliceCanvas`` - the new display bounds will be + adjusted so that this location remains the same, with + respect to the new field of view. """ if xmin is None: xmin = self.displayCtx.bounds.getLo(self.xax) @@ -991,11 +1008,44 @@ class SliceCanvas(props.HasProperties): ymin = ymin - 0.5 * (newDispHeight - dispHeight) ymax = ymax + 0.5 * (newDispHeight - dispHeight) + oldxmin, oldxmax, oldymin, oldymax = self.displayBounds[:] + + self.disableNotification('displayBounds') self.displayBounds.setLimits(0, xmin, xmax) - self.displayBounds.setLimits(1, ymin, ymax) + self.displayBounds.setLimits(1, ymin, ymax) + self.enableNotification('displayBounds') xmin, xmax, ymin, ymax = self._applyZoom(xmin, xmax, ymin, ymax) + if oldLoc and (oldxmax > oldxmin) and (oldymax > oldymin): + + # Calculate the normalised distance from the + # old cursor loaction to the old bound corner + oldxoff = (oldLoc[0] - oldxmin) / (oldxmax - oldxmin) + oldyoff = (oldLoc[1] - oldymin) / (oldymax - oldymin) + + # Re-set the new bounds to the current + # display location, offset by the same + # amount that it used to be (as + # calculated above). + # + # N.B. This code assumes that, if the display + # coordinate system has changed, the display + # context location has already been updated. + # See the DisplayContext.__displaySpaceChanged + # method. + xloc = self.displayCtx.location[self.xax] + yloc = self.displayCtx.location[self.yax] + + xlen = xmax - xmin + ylen = ymax - ymin + + xmin = xloc - oldxoff * xlen + ymin = yloc - oldyoff * ylen + + xmax = xmin + xlen + ymax = ymin + ylen + log.debug('Final display bounds: X: ({}, {}) Y: ({}, {})'.format( xmin, xmax, ymin, ymax))