diff --git a/fsl/data/strings.py b/fsl/data/strings.py
index 41e200cf746562b9d4236982e13674215c5093a3..75e0d41a7bbf7041a73a7db4ba47bda38a524cc6 100644
--- a/fsl/data/strings.py
+++ b/fsl/data/strings.py
@@ -195,17 +195,17 @@ properties = TypeDict({
     'OrthoEditProfile.selectionOverlayColour' : 'Selection overlay',
     'OrthoEditProfile.selectionCursorColour'  : 'Selection cursor',
     
-
     'Display.name'              : 'Overlay name',
+    'Display.overlayType'       : 'Overlay data type',
     'Display.enabled'           : 'Enabled',
     'Display.alpha'             : 'Opacity',
     'Display.brightness'        : 'Brightness',
     'Display.contrast'          : 'Contrast',
     'Display.interpolation'     : 'Interpolation',
-    'Display.resolution'        : 'Resolution',
-    'Display.volume'            : 'Volume',
-    'Display.transform'         : 'Overlay transform',
-    'Display.overlayType'       : 'Overlay data type',
+
+    'ImageOpts.resolution' : 'Resolution',
+    'ImageOpts.transform'  : 'Image transform',
+    'ImageOpts.volume'     : 'Volume',
     
     'VolumeOpts.displayRange'  : 'Display range',
     'VolumeOpts.clippingRange' : 'Clipping range',
@@ -228,6 +228,9 @@ properties = TypeDict({
 
     'LineVectorOpts.directed'  : 'Interpret vectors as directed',
     'LineVectorOpts.lineWidth' : 'Line width',
+
+    'ModelOpts.colour'  : 'Colour',
+    'ModelOpts.outline' : 'Show outline only',
 })
 
 
@@ -255,7 +258,6 @@ modes = TypeDict({
 })
 
 
-
 choices = TypeDict({
 
     'SceneOpts.colourBarLocation.top'    : 'Top',
@@ -285,14 +287,20 @@ choices = TypeDict({
     'VectorOpts.displayType.rgb'  : 'RGB',
 
     'VectorOpts.modulate.none'    : 'No modulation',
-    
-    'Display.transform.affine' : 'Use qform/sform transformation matrix',
-    'Display.transform.pixdim' : 'Use pixdims only',
-    'Display.transform.id'     : 'Do not use qform/sform or pixdims',
+
+    'ImageOpts.transform.affine' : 'Use qform/sform transformation matrix',
+    'ImageOpts.transform.pixdim' : 'Use pixdims only',
+    'ImageOpts.transform.id'     : 'Do not use qform/sform or pixdims',
 
     'Display.interpolation.none'   : 'No interpolation', 
     'Display.interpolation.linear' : 'Linear interpolation', 
-    'Display.interpolation.spline' : 'Spline interpolation', 
+    'Display.interpolation.spline' : 'Spline interpolation',
+
+    'Display.overlayType.volume'     : '3D/4D volume',
+    'Display.overlayType.mask'       : '3D/4D mask image',
+    'Display.overlayType.rgbvector'  : '3-direction vector image (RGB)',
+    'Display.overlayType.linevector' : '3-direction vector image (Line)',
+    'Display.overlayType.model'      : '3D model' 
 })
 
 
diff --git a/fsl/fslview/displaycontext/maskopts.py b/fsl/fslview/displaycontext/maskopts.py
index f88038ce9665ccccb9943f9ecff54269bc47142f..67ad27eb097b14b62b5de1d9fae370d09981dd69 100644
--- a/fsl/fslview/displaycontext/maskopts.py
+++ b/fsl/fslview/displaycontext/maskopts.py
@@ -1,21 +1,19 @@
 #!/usr/bin/env python
 #
-# maskdisplay.py -
+# maskopts.py -
 #
 # Author: Paul McCarthy <pauldmccarthy@gmail.com>
 #
 
-
 import numpy as np
 
-
 import props
 
-import fsl.data.image   as fslimage
 import fsl.data.strings as strings
-import display          as fsldisplay
+import                     volumeopts
 
-class MaskOpts(fsldisplay.DisplayOpts):
+
+class MaskOpts(volumeopts.ImageOpts):
 
     colour     = props.Colour()
     invert     = props.Boolean(default=False)
@@ -24,11 +22,7 @@ class MaskOpts(fsldisplay.DisplayOpts):
         labels=[strings.choices['VolumeOpts.displayRange.min'],
                 strings.choices['VolumeOpts.displayRange.max']]) 
 
-    def __init__(self, overlay, display, overlayList, displayCtx, parent=None):
-
-        if not isinstance(overlay, fslimage.Image):
-            raise RuntimeError('{} can only be used with an {} overlay'.format(
-                type(self).__name__, fslimage.Image.__name__))
+    def __init__(self, overlay, *args, **kwargs):
 
         if np.prod(overlay.shape) > 2 ** 30:
             sample = overlay.data[..., overlay.shape[-1] / 2]
@@ -56,9 +50,4 @@ class MaskOpts(fsldisplay.DisplayOpts):
         self.threshold.xhi  = self.dataMax + dMinDistance 
         self.setConstraint('threshold', 'minDistance', dMinDistance)
 
-        fsldisplay.DisplayOpts.__init__(self,
-                                        overlay,
-                                        display,
-                                        overlayList,
-                                        displayCtx,
-                                        parent)
+        volumeopts.ImageOpts.__init__(self, overlay, *args, **kwargs)
diff --git a/fsl/fslview/displaycontext/modelopts.py b/fsl/fslview/displaycontext/modelopts.py
index 93acf16835f3e146e85745d4f80b2d4f4d26c6aa..b0370c212046f3bdd04ea864b2095eba13fba90a 100644
--- a/fsl/fslview/displaycontext/modelopts.py
+++ b/fsl/fslview/displaycontext/modelopts.py
@@ -15,7 +15,7 @@ import display as fsldisplay
 class ModelOpts(fsldisplay.DisplayOpts):
 
     colour  = props.Colour()
-    outline = props.Boolean(default=True)
+    outline = props.Boolean(default=False)
 
 
     def __init__(self, *args, **kwargs):
diff --git a/fsl/fslview/displaycontext/vectoropts.py b/fsl/fslview/displaycontext/vectoropts.py
index 6c957f5797d6bafc73acb6d7f1c6a7be543ee469..44a8cd1e7393acf3b8da322627b1f99569f596f5 100644
--- a/fsl/fslview/displaycontext/vectoropts.py
+++ b/fsl/fslview/displaycontext/vectoropts.py
@@ -12,10 +12,10 @@ import props
 
 import fsl.data.image   as fslimage
 import fsl.data.strings as strings
-import display          as fsldisplay
+import                     volumeopts
 
 
-class VectorOpts(fsldisplay.DisplayOpts):
+class VectorOpts(volumeopts.ImageOpts):
 
 
     xColour = props.Colour(default=(1.0, 0.0, 0.0))
@@ -53,38 +53,29 @@ class VectorOpts(fsldisplay.DisplayOpts):
     """Hide voxels for which the modulation value is below this threshold."""
 
     
-    def __init__(self,
-                 overlay,
-                 display,
-                 overlayList,
-                 displayCtx,
-                 parent=None,
-                 *args,
-                 **kwargs):
+    def __init__(self, *args, **kwargs):
         """Create a ``VectorOpts`` instance for the given image.
 
-        See the :class:`.DisplayOpts` documentation for more details.
+        See the :class:`.ImageOpts` documentation for more details.
         """
+        
+        volumeopts.ImageOpts.__init__(self, *args, **kwargs)
 
-        if not isinstance(overlay, fslimage.Image):
-            raise RuntimeError('{} can only be used with an {} overlay'.format(
-                type(self).__name__, fslimage.Image.__name__)) 
+        self.overlayList.addListener('overlays',
+                                     self.name,
+                                     self.__overlayListChanged)
         
-        fsldisplay.DisplayOpts.__init__(self,
-                                        overlay,
-                                        display,
-                                        overlayList,
-                                        displayCtx,
-                                        parent,
-                                        *args,
-                                        **kwargs)
-
-        overlayList.addListener('overlays',
-                                self.name,
-                                self.__overlayListChanged)
         self.__overlayListChanged()
 
 
+    def destroy(self):
+        volumeopts.ImageOpts.destroy(self)
+        self.overlayList.removeListener('overlays', self.name)
+
+        for overlay in self.overlayList:
+            overlay.removeListeneR('name', self.name)
+
+        
     def __overlayListChanged(self, *a):
         """Called when the overlay list changes. Updates the ``modulate``
         property so that it contains a list of overlays which could be used
@@ -136,9 +127,6 @@ class VectorOpts(fsldisplay.DisplayOpts):
         else:                  self.modulate = 'none'
 
 
-# TODO RGBVector/LineVector subclasses for any type
-# specific options (e.g. line width for linevector)
-
 class LineVectorOpts(VectorOpts):
 
     lineWidth = props.Int(minval=1, maxval=10, default=1)
diff --git a/fsl/fslview/displaycontext/volumeopts.py b/fsl/fslview/displaycontext/volumeopts.py
index 9da91ca50c485379d6a812e989c74bebbc8caf6e..5f1f295614d4cc8b0252016e00e1371b4e58cd66 100644
--- a/fsl/fslview/displaycontext/volumeopts.py
+++ b/fsl/fslview/displaycontext/volumeopts.py
@@ -16,6 +16,7 @@ import props
 
 import fsl.data.image         as fslimage
 import fsl.data.strings       as strings
+import fsl.utils.transform    as transform
 import fsl.fslview.colourmaps as fslcm
 
 import display as fsldisplay
@@ -24,17 +25,180 @@ import display as fsldisplay
 log = logging.getLogger(__name__)
 
 
-# TODO Define a super/mixin class which
-# has a displayRange and colour map. This
-# will allow other bits of code which
-# need display range/cmap options to
-# test for their presence without having
-# to explicitly test against the VolumeOpts
-# class (and other future *Opts classes
-# which have a display range/cmap).
+class ImageOpts(fsldisplay.DisplayOpts):
+    """A class which describes how an :class:`.Image` should be displayed. 
+    """
+
+    
+    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."""
+
+
+    transform = props.Choice(
+        ('affine', 'pixdim', 'id'),
+        labels=[strings.choices['ImageOpts.transform.affine'],
+                strings.choices['ImageOpts.transform.pixdim'],
+                strings.choices['ImageOpts.transform.id']],
+        default='pixdim')
+    """This property defines how the overlay should be transformd into the display
+    coordinate system.
+    
+      - ``affine``: Use the affine transformation matrix stored in the image
+        (the ``qform``/``sform`` fields in NIFTI1 headers).
+                    
+      - ``pixdim``: Scale voxel sizes by the ``pixdim`` fields in the image
+        header.
+    
+      - ``id``: Perform no scaling or transformation - voxels will be
+        interpreted as :math:`1mm^3` isotropic, with the origin at voxel
+        (0,0,0).
+    """
+
+ 
+    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
+
+        # The display<->* transformation matrices
+        # are created in the _transformChanged method
+        self.__xforms = {}
+        self.__setupTransforms()
+
+        # is this a 4D volume?
+        if self.is4D():
+            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
+        self.resolution = min(overlay.pixdim[:3])
+        self.setConstraint('resolution', 'minval', self.resolution)
+
+
+    def destroy(self):
+        self.removeListener('transform',  self.name)
+
+                            
+    def __setupTransforms(self):
+        """Calculates transformation matrices between all of the possible
+        spaces in which the overlay may be displayed.
+
+        These matrices are accessible via the :meth:`getTransform` method.
+        """
+
+        # TODO This is obviously volumetric specific
+
+        if not isinstance(self.__overlay, fslimage.Image):
+            log.warn('Non-volumetric types not supported yet')
+            return
 
+        image          = self.__overlay
 
-class VolumeOpts(fsldisplay.DisplayOpts):
+        voxToIdMat     = np.eye(4)
+        voxToPixdimMat = np.diag(list(image.pixdim[:3]) + [1.0])
+        voxToAffineMat = image.voxToWorldMat.T
+        
+        idToVoxMat        = transform.invert(voxToIdMat)
+        idToPixdimMat     = transform.concat(idToVoxMat, voxToPixdimMat)
+        idToAffineMat     = transform.concat(idToVoxMat, voxToAffineMat)
+
+        pixdimToVoxMat    = transform.invert(voxToPixdimMat)
+        pixdimToIdMat     = transform.concat(pixdimToVoxMat, voxToIdMat)
+        pixdimToAffineMat = transform.concat(pixdimToVoxMat, voxToAffineMat)
+
+        affineToVoxMat    = image.worldToVoxMat.T
+        affineToIdMat     = transform.concat(affineToVoxMat, voxToIdMat)
+        affineToPixdimMat = transform.concat(affineToVoxMat, voxToPixdimMat)
+        
+        self.__xforms['id',  'id']     = np.eye(4)
+        self.__xforms['id',  'pixdim'] = idToPixdimMat 
+        self.__xforms['id',  'affine'] = idToAffineMat
+
+        self.__xforms['pixdim', 'pixdim'] = np.eye(4)
+        self.__xforms['pixdim', 'id']     = pixdimToIdMat
+        self.__xforms['pixdim', 'affine'] = pixdimToAffineMat
+ 
+        self.__xforms['affine', 'affine'] = np.eye(4)
+        self.__xforms['affine', 'id']     = affineToIdMat
+        self.__xforms['affine', 'pixdim'] = affineToPixdimMat 
+
+
+    def getTransform(self, from_, to, xform=None):
+        """Return a matrix which may be used to transform coordinates
+        from ``from_`` to ``to``. Valid values for ``from_`` and ``to``
+        are:
+          - ``id``:      Voxel coordinates
+        
+          - ``pixdim``:  Voxel coordinates, scaled by voxel dimensions
+        
+          - ``affine``:  World coordinates, as defined by the NIFTI1
+                         ``qform``/``sform``. See
+                         :attr:`~fsl.data.image.Image.voxToWorldMat`.
+        
+          - ``voxel``:   Equivalent to ``id``.
+        
+          - ``display``: Equivalent to the current value of :attr:`transform`.
+        
+          - ``world``;   Equivalent to ``affine``.
+
+        If the ``xform`` parameter is provided, and one of ``from_`` or ``to``
+        is ``display``, the value of ``xform`` is used instead of the current
+        value of :attr:`transform`.
+        """
+
+        # TODO non-volumetric types
+        if not isinstance(self.__overlay, fslimage.Image):
+            raise RuntimeError('Non-volumetric types not supported yet')
+
+        if xform is None:
+            xform = self.transform
+
+        if   from_ == 'display': from_ = xform
+        elif from_ == 'world':   from_ = 'affine'
+        elif from_ == 'voxel':   from_ = 'id'
+        
+        if   to    == 'display': to    = xform
+        elif to    == 'world':   to    = 'affine'
+        elif to    == 'voxel':   to    = 'id'
+
+        return self.__xforms[from_, to]
+
+
+    def getLastTransform(self):
+        """Returns the most recent value of the :attr:`transform` property,
+        before its current value.
+        """
+        return self.__oldTransform
+
+
+    def __transformChanged(self, *a):
+        """Called when the :attr:`transform` property is changed."""
+
+        # Store references to the previous display related transformation
+        # matrices, just in case anything (hint the DisplayContext object)
+        # needs them for any particular reason (hint: so the DisplayContext
+        # can preserve the current display location, in terms of image world
+        # space, when the transform of the selected image changes)
+        self.__oldTransform = self.__transform
+        self.__transform    = self.transform
+
+
+class VolumeOpts(ImageOpts):
     """A class which describes how an :class:`.Image` should be displayed.
 
     This class doesn't have much functionality - it is up to things which
@@ -93,10 +257,6 @@ class VolumeOpts(fsldisplay.DisplayOpts):
     def __init__(self, overlay, display, overlayList, displayCtx, parent=None):
         """Create a :class:`VolumeOpts` instance for the specified image."""
 
-        if not isinstance(overlay, fslimage.Image):
-            raise RuntimeError('{} can only be used with an {} overlay'.format(
-                type(self).__name__, fslimage.Image.__name__)) 
-
         # Attributes controlling image display. Only
         # determine the real min/max for small images -
         # if it's memory mapped, we have no idea how big
@@ -133,12 +293,12 @@ class VolumeOpts(fsldisplay.DisplayOpts):
         
         self.setConstraint('displayRange', 'minDistance', dMinDistance)
 
-        fsldisplay.DisplayOpts.__init__(self,
-                                        overlay,
-                                        display,
-                                        overlayList,
-                                        displayCtx,
-                                        parent)
+        ImageOpts.__init__(self,
+                           overlay,
+                           display,
+                           overlayList,
+                           displayCtx,
+                           parent)
 
         # The displayRange property of every child VolumeOpts
         # instance is linked to the corresponding 
@@ -166,6 +326,8 @@ class VolumeOpts(fsldisplay.DisplayOpts):
 
     def destroy(self):
 
+        ImageOpts.destroy(self)
+
         if self.getParent() is not None:
             display = self.display
             display.removeListener('brightness',   self.name)