From fafbf849303a1980b81b5ec22779ca5d651a8501 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauld.mccarthy@gmail.com>
Date: Wed, 22 Oct 2014 15:08:21 +0100
Subject: [PATCH] Bugfixes to transform.py ('x' variable overwriting
 transformation matrix), LightBoxPanel (not passing displayCtx to
 LightBoxCanvas), fslview_parseargs (using imageList.bounds instead of
 displayCtx.bounds), ImageList (didn't remove bounds-related code),
 ImageDisplay (attempting to offset transformation matrices for affine, when
 only supposed to do it for id/pixdim)

---
 fsl/data/image.py                  | 62 +-----------------------------
 fsl/fslview/displaycontext.py      |  9 +++--
 fsl/fslview/views/lightboxpanel.py |  3 +-
 fsl/tools/fslview_parseargs.py     |  6 +--
 fsl/utils/transform.py             | 16 ++++----
 5 files changed, 19 insertions(+), 77 deletions(-)

diff --git a/fsl/data/image.py b/fsl/data/image.py
index 42fa6944f..3e3e5595a 100644
--- a/fsl/data/image.py
+++ b/fsl/data/image.py
@@ -8,7 +8,6 @@
 """Classes for representing 3D/4D images and collections of said images."""
 
 import os
-import sys
 import logging
 import tempfile
 import collections
@@ -322,9 +321,7 @@ class ImageList(props.HasProperties):
     """Class representing a collection of images to be displayed together.
 
     Contains a :class:`props.properties_types.List` property containing
-    :class:`Image` objects, and some other properties on which listeners may
-    register themselves to be notified when the properties of the image
-    collection changes (e.g. image bounds).
+    :class:`Image` objects.
 
     An :class:`ImageList` object has a few wrapper methods around the
     :attr:`images` property, allowing the :class:`ImageList` to be used
@@ -350,14 +347,6 @@ class ImageList(props.HasProperties):
 
         self.images = images
 
-        self.addListener(
-            'images',
-            self.__class__.__name__,
-            self._imageListChanged)
-
-        # initialise image bounds
-        self._imageListChanged()
-
         # set the _lastDir attribute,
         # used by the addImages method
         if len(images) == 0: self._lastDir = os.getcwd()
@@ -423,55 +412,6 @@ class ImageList(props.HasProperties):
         return True
 
 
-    def _imageListChanged(self, *a):
-        """Called whenever an item is added or removed from the :attr:`images`
-        list. Registers listeners with the properties of each image, and
-        calls the :meth:`_updateImageBounds` method.
-        """ 
-        
-        for img in self.images:
-
-            # This may be called multiple times on each image,
-            # but it doesn't matter, as any listener which has
-            # previously been registered with an image will
-            # just be replaced by the new one here.
-            img.addListener(
-                'transform',
-                self.__class__.__name__,
-                self._updateImageBounds,
-                overwrite=True)
-
-        self._updateImageBounds()
-
-    
-    def _updateImageBounds(self, *a):
-        """Called whenever an item is added or removed from the
-        :attr:`images` list, or an image property changes. Updates
-        the :attr:`bounds` property.
-        """
-
-        if len(self.images) == 0:
-            minBounds = [0.0, 0.0, 0.0]
-            maxBounds = [0.0, 0.0, 0.0]
-            
-        else:
-            minBounds = 3 * [ sys.float_info.max]
-            maxBounds = 3 * [-sys.float_info.max]
-
-        for img in self.images:
-
-            for ax in range(3):
-
-                lo, hi = img.imageBounds(ax)
-
-                if lo < minBounds[ax]: minBounds[ax] = lo
-                if hi > maxBounds[ax]: maxBounds[ax] = hi
-
-        self.bounds[:] = [minBounds[0], maxBounds[0],
-                          minBounds[1], maxBounds[1],
-                          minBounds[2], maxBounds[2]]
-
-
     # Wrappers around the images list property, allowing this
     # ImageList object to be used as if it is actually a list.
     def __len__(     self):               return self.images.__len__()
diff --git a/fsl/fslview/displaycontext.py b/fsl/fslview/displaycontext.py
index 118a9ddb4..401907b8f 100644
--- a/fsl/fslview/displaycontext.py
+++ b/fsl/fslview/displaycontext.py
@@ -325,9 +325,10 @@ class ImageDisplay(props.HasProperties):
 
         # for pixdim/identity transformations, we want the world
         # location (0, 0, 0) to map to voxel location (0, 0, 0)
-        for i in range(3):
-            self.voxToDisplayMat[3, i] =  pixdim[i] * 0.5
-            self.displayToVoxMat[3, i] = -0.5
+        if self.transform in ('id', 'pixdim'):
+            for i in range(3):
+                self.voxToDisplayMat[3, i] =  pixdim[i] * 0.5
+                self.displayToVoxMat[3, i] = -0.5
 
         # When transform is changed to 'affine', enable interpolation
         # and, when changed to 'pixdim' or 'id', disable interpolation
@@ -483,7 +484,7 @@ class DisplayContext(props.HasProperties):
             minBounds = 3 * [ sys.float_info.max]
             maxBounds = 3 * [-sys.float_info.max]
 
-        for img in self.images:
+        for img in self._imageList.images:
 
             display = img.getAttribute('display')
             xform   = display.voxToDisplayMat
diff --git a/fsl/fslview/views/lightboxpanel.py b/fsl/fslview/views/lightboxpanel.py
index bf01aafff..bcd3d2c7a 100644
--- a/fsl/fslview/views/lightboxpanel.py
+++ b/fsl/fslview/views/lightboxpanel.py
@@ -90,7 +90,8 @@ class LightBoxPanel(canvaspanel.CanvasPanel):
 
         self._scrollbar = wx.ScrollBar(self, style=wx.SB_VERTICAL)
         self._lbCanvas  = lightboxcanvas.LightBoxCanvas(self.getCanvasPanel(),
-                                                        imageList)
+                                                        imageList,
+                                                        displayCtx)
 
         # My properties are the canvas properties
         self.bindProps('sliceSpacing',  self._lbCanvas)
diff --git a/fsl/tools/fslview_parseargs.py b/fsl/tools/fslview_parseargs.py
index c07bc0e7e..74fa76398 100644
--- a/fsl/tools/fslview_parseargs.py
+++ b/fsl/tools/fslview_parseargs.py
@@ -255,9 +255,9 @@ def handleImageArgs(args):
             loc = imageList[0].voxToWorld([args.voxelloc])[0]
             
         else:
-            loc = [imageList.bounds.xlo + 0.5 * imageList.bounds.xlen,
-                   imageList.bounds.ylo + 0.5 * imageList.bounds.ylen,
-                   imageList.bounds.zlo + 0.5 * imageList.bounds.zlen]
+            loc = [displayCtx.bounds.xlo + 0.5 * displayCtx.bounds.xlen,
+                   displayCtx.bounds.ylo + 0.5 * displayCtx.bounds.ylen,
+                   displayCtx.bounds.zlo + 0.5 * displayCtx.bounds.zlen]
 
         displayCtx.location.xyz = loc
 
diff --git a/fsl/utils/transform.py b/fsl/utils/transform.py
index 9cf3b688e..8c08894ba 100644
--- a/fsl/utils/transform.py
+++ b/fsl/utils/transform.py
@@ -19,7 +19,7 @@ def invert(x):
     return linalg.inv(x)
 
 
-def axisBounds(shape, x, axis):
+def axisBounds(shape, xform, axis):
     """Returns the (lo, hi) bounds of the specified axis."""
     x, y, z = shape
 
@@ -38,7 +38,7 @@ def axisBounds(shape, x, axis):
     points[6, :] = [x,     y,   -0.5]
     points[7, :] = [x,     y,    z]
 
-    tx = transform(points, x)
+    tx = transform(points, xform)
 
     lo = tx[:, axis].min()
     hi = tx[:, axis].max()
@@ -46,7 +46,7 @@ def axisBounds(shape, x, axis):
     return (lo, hi)
 
 
-def axisLength(shape, x, axis):
+def axisLength(shape, xform, axis):
     """Return the length, in real world units, of the specified axis.
     """
         
@@ -54,13 +54,13 @@ def axisLength(shape, x, axis):
     points[:]       = [-0.5, -0.5, -0.5]
     points[1, axis] = shape[axis] - 0.5 
 
-    tx = transform(points, x)
+    tx = transform(points, xform)
 
     # euclidean distance between each boundary point
     return sum((tx[0, :] - tx[1, :]) ** 2) ** 0.5 
 
         
-def transform(p, x, axes=None):
+def transform(p, xform, axes=None):
     """Transforms the given set of points ``p`` according to the given affine
     transformation ``x``. The transformed points are returned as a
     :class:``numpy.float64`` array.
@@ -73,9 +73,9 @@ def transform(p, x, axes=None):
     y = p[:, 1]
     z = p[:, 2]
 
-    t[:, 0] = x * x[0, 0] + y * x[1, 0] + z * x[2, 0] + x[3, 0]
-    t[:, 1] = x * x[0, 1] + y * x[1, 1] + z * x[2, 1] + x[3, 1]
-    t[:, 2] = x * x[0, 2] + y * x[1, 2] + z * x[2, 2] + x[3, 2]
+    t[:, 0] = x * xform[0, 0] + y * xform[1, 0] + z * xform[2, 0] + xform[3, 0]
+    t[:, 1] = x * xform[0, 1] + y * xform[1, 1] + z * xform[2, 1] + xform[3, 1]
+    t[:, 2] = x * xform[0, 2] + y * xform[1, 2] + z * xform[2, 2] + xform[3, 2]
 
     if axes is None: axes = [0, 1, 2]
 
-- 
GitLab