From a91e32e77db9fbc961c0daa125fcde8a5bcc0857 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauld.mccarthy@gmail.com>
Date: Tue, 19 Jan 2016 15:04:28 +0000
Subject: [PATCH] Slice spacing/zrange values are hopefully set to sensible
 things when Z axis/display space change.

---
 fsl/fsleyes/gl/lightboxcanvas.py   | 112 ++++++++++++++++++-----------
 fsl/fsleyes/views/lightboxpanel.py |  30 ++++----
 2 files changed, 88 insertions(+), 54 deletions(-)

diff --git a/fsl/fsleyes/gl/lightboxcanvas.py b/fsl/fsleyes/gl/lightboxcanvas.py
index 5a9e7f1b4..27a296dd9 100644
--- a/fsl/fsleyes/gl/lightboxcanvas.py
+++ b/fsl/fsleyes/gl/lightboxcanvas.py
@@ -21,6 +21,7 @@ import fsl.fsleyes.gl.slicecanvas            as slicecanvas
 import fsl.fsleyes.gl.resources              as glresources
 import fsl.fsleyes.gl.routines               as glroutines
 import fsl.fsleyes.gl.textures               as textures
+import fsl.data.image                        as fslimage
 
 
 log = logging.getLogger(__name__)
@@ -71,6 +72,7 @@ class LightBoxCanvas(slicecanvas.SliceCanvas):
        canvasToWorld
        worldToCanvas
        getTotalRows
+       calcSliceSpacing
     """
 
     
@@ -232,6 +234,54 @@ class LightBoxCanvas(slicecanvas.SliceCanvas):
         """Returns the total number of rows that may be displayed. """
         return self._totalRows
 
+
+    def calcSliceSpacing(self, overlay):
+        """Calculates and returns a Z-axis slice spacing value suitable
+        for the given overlay. 
+        """
+        displayCtx = self.displayCtx
+        opts       = displayCtx.getOpts(overlay)
+        overlay    = displayCtx.getReferenceImage(overlay)
+        zmin, zmax = opts.bounds.getLimits(self.zax)
+
+        # If the overlay does not have a
+        # reference NIFTI1 image, choose
+        # an arbitrary slice spacing. 
+        if   overlay is None:            return (zmax - zmin) / 50.0
+
+        # Otherwise return a spacing
+        # appropriate for the current
+        # display space
+        if   opts.transform == 'id':     return 1
+        elif opts.transform == 'pixdim': return overlay.pixdim[self.zax]
+        elif opts.transform == 'affine': return min(overlay.pixdim[:3])
+
+        # This overlay is being displayed with a
+        # custrom transformation matrix  - check
+        # to see what display space we're in
+        displaySpace = displayCtx.displaySpace
+
+        if isinstance(displaySpace, fslimage.Nifti1):
+            return self.calcSliceSpacing(displaySpace)
+        else:
+            return min(overlay.pixdim[:3])
+
+
+    def _zAxisChanged(self, *a):
+        """Overrides :meth:`.SliceCanvas._zAxisChanged`. Calls that
+        method, and then resets the :attr:`sliceSpacing` and :attr:`zrange`
+        properties to sensible values.
+        """
+        slicecanvas.SliceCanvas._zAxisChanged(self, *a)
+
+        overlay = self.displayCtx.getSelectedOverlay()
+
+        if overlay is None:
+            return
+
+        self.sliceSpacing = self.calcSliceSpacing(overlay)
+        self.zrange.x     = self.displayCtx.bounds.getRange(self.zax)
+
         
     def _topRowChanged(self, *a):
         """Called when the :attr:`topRow` property changes.  Adjusts display
@@ -346,7 +396,7 @@ class LightBoxCanvas(slicecanvas.SliceCanvas):
            height == 0:
             return
 
-        self._nslices   = int(np.floor(zlen / self.sliceSpacing))
+        self._nslices   = int(np.ceil(zlen / self.sliceSpacing))
         self._totalRows = int(np.ceil(self._nslices / float(self.ncols)))
 
         if self._nslices == 0 or self._totalRows == 0:
@@ -418,47 +468,29 @@ class LightBoxCanvas(slicecanvas.SliceCanvas):
             self.setConstraint('zrange', 'minDistance', 0)
             self.zrange.x     = (0, 0)
             self.sliceSpacing = 0
-        else:
+            return
 
-            # Get the new Z range from the
-            # display context bounding box.
-            #
-            # And calculate the minimum possible
-            # slice spacing - the smallest pixdim
-            # across all overlays in the list.
-            newZRange = self.displayCtx.bounds.getRange(self.zax)
-            newZGap   = sys.float_info.max
-            
-            for overlay in self.overlayList:
+        # Get the new Z range from the
+        # display context bounding box.
+        #
+        # And calculate the minimum possible
+        # slice spacing - the smallest pixdim
+        # across all overlays in the list.
+        newZRange = self.displayCtx.bounds.getRange(self.zax)
+        newZGap   = sys.float_info.max
 
-                overlay = self.displayCtx.getReferenceImage(overlay)
-                
-                if overlay is None:
-                    continue
-                
-                opts = self.displayCtx.getOpts(overlay)
-
-                if   opts.transform == 'id':
-                    zgap = 1
-                elif opts.transform == 'pixdim':
-                    zgap = overlay.pixdim[self.zax]
-                else:
-                    zgap = min(overlay.pixdim[:3])
-
-                if zgap < newZGap:
-                    newZGap = zgap
-
-            # If there were no volumetric overlays
-            # in the overlay list, choose an arbitrary
-            # default slice spacing
-            if newZGap == sys.float_info.max:
-                newZGap = (newZRange[1] - newZRange[0]) / 50.0
-
-            # Update the zrange and slice
-            # spacing constraints
-            self.zrange.setLimits(0, *newZRange)
-            self.setConstraint('zrange',       'minDistance', newZGap)
-            self.setConstraint('sliceSpacing', 'minval',      newZGap)
+        for overlay in self.overlayList:
+
+            zgap = self.calcSliceSpacing(overlay)
+
+            if zgap < newZGap:
+                newZGap = zgap
+
+        # Update the zrange and slice
+        # spacing constraints
+        self.zrange.setLimits(0, *newZRange)
+        self.setConstraint('zrange',       'minDistance', newZGap)
+        self.setConstraint('sliceSpacing', 'minval',      newZGap)
 
 
     def _overlayBoundsChanged(self, *a):
diff --git a/fsl/fsleyes/views/lightboxpanel.py b/fsl/fsleyes/views/lightboxpanel.py
index 89ff0095e..375a86d55 100644
--- a/fsl/fsleyes/views/lightboxpanel.py
+++ b/fsl/fsleyes/views/lightboxpanel.py
@@ -261,11 +261,16 @@ class LightBoxPanel(canvaspanel.CanvasPanel):
                                  self._name,
                                  self.__transformChanged)
 
-        # If the current zrange is [0, 0] we'll
-        # assume that it needs to be initialised.
-        sceneOpts = self.getSceneOptions()
-        if sceneOpts.zrange == [0.0, 0.0]:
-            sceneOpts.zrange = self._displayCtx.bounds.getRange(sceneOpts.zax)
+        # If the current zrange is [0, 0]
+        # we'll assume that the spacing/
+        # zrange need to be initialised.
+        lbCanvas = self.__lbCanvas
+        opts     = self.getSceneOptions()
+        
+        if opts.zrange == [0.0, 0.0]:
+            
+            opts.sliceSpacing = lbCanvas.calcSliceSpacing(selectedOverlay)
+            opts.zrange       = self._displayCtx.bounds.getRange(opts.zax)
 
 
     def __transformChanged(self, *a):
@@ -288,15 +293,12 @@ class LightBoxPanel(canvaspanel.CanvasPanel):
         loBounds = opts.bounds.getLo()
         hiBounds = opts.bounds.getHi()
 
-        if opts.transform == 'id':
-            sceneOpts.sliceSpacing = 1
-        elif opts.transform == 'pixdim':
-            sceneOpts.sliceSpacing = overlay.pixdim[sceneOpts.zax]
-        else:
-            sceneOpts.sliceSpacing = min(overlay.pixdim[sceneOpts.zax])
-            
-        sceneOpts.zrange.x = (loBounds[sceneOpts.zax],
-                              hiBounds[sceneOpts.zax])
+        # Reset the spacing/zrange. Not
+        # sure if this is the best idea,
+        # but it's here for the time being.
+        sceneOpts.sliceSpacing = self.__lbCanvas.calcSliceSpacing(overlay)
+        sceneOpts.zrange.x     = (loBounds[sceneOpts.zax],
+                                  hiBounds[sceneOpts.zax])
 
         self.__onResize()
 
-- 
GitLab