From b72490951b89e53f1efde966d92c3bb02db5c92d Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauld.mccarthy@gmail.com>
Date: Thu, 17 Dec 2015 14:56:55 +0000
Subject: [PATCH] VectorOpts.clipThreshold is now a range called
 VectorOpts.clippingRange.

---
 fsl/data/strings.py                           |  5 +-
 fsl/fsleyes/controls/overlaydisplaypanel.py   | 22 +++++---
 fsl/fsleyes/controls/overlaydisplaytoolbar.py | 50 +++++++++++--------
 fsl/fsleyes/displaycontext/group.py           |  2 +-
 fsl/fsleyes/displaycontext/vectoropts.py      | 18 ++++---
 fsl/fsleyes/fsleyes_parseargs.py              |  9 ++--
 fsl/fsleyes/gl/gl21/gllinevector_funcs.py     | 24 ++++++---
 fsl/fsleyes/gl/gl21/glrgbvector_funcs.py      | 21 +++++---
 fsl/fsleyes/gl/gl21/gltensor_funcs.py         | 35 +++++++------
 fsl/fsleyes/gl/gl21/glvector_frag.glsl        | 13 +++--
 fsl/fsleyes/gl/glvector.py                    |  4 +-
 fsl/fsleyes/tooltips.py                       |  6 +--
 12 files changed, 126 insertions(+), 83 deletions(-)

diff --git a/fsl/data/strings.py b/fsl/data/strings.py
index f52a33c3d..d54391c7a 100644
--- a/fsl/data/strings.py
+++ b/fsl/data/strings.py
@@ -555,7 +555,7 @@ properties = TypeDict({
     'VectorOpts.suppressZ'     : 'Suppress Z value',
     'VectorOpts.modulateImage' : 'Modulate by',
     'VectorOpts.clipImage'     : 'Clip by',
-    'VectorOpts.clipThreshold' : 'Clipping threshold',
+    'VectorOpts.clippingRange' : 'Clipping range',
 
     'RGBVectorOpts.interpolation' : 'Interpolation',
 
@@ -619,6 +619,9 @@ choices = TypeDict({
 
     'VectorOpts.modulateImage.none' : 'No modulation',
     'VectorOpts.clipImage.none'     : 'No clipping',
+    
+    'VectorOpts.clippingRange.min' : 'Min.',
+    'VectorOpts.clippingRange.max' : 'Max.', 
 
     'ModelOpts.refImage.none'     : 'No reference image',
 
diff --git a/fsl/fsleyes/controls/overlaydisplaypanel.py b/fsl/fsleyes/controls/overlaydisplaypanel.py
index bcc874128..d30f90fcb 100644
--- a/fsl/fsleyes/controls/overlaydisplaypanel.py
+++ b/fsl/fsleyes/controls/overlaydisplaypanel.py
@@ -326,7 +326,8 @@ _DISPLAY_PROPS = td.TypeDict({
 
     'RGBVectorOpts' : [
         props.Widget('resolution', showLimits=False),
-        props.Widget('interpolation'),
+        props.Widget('interpolation',
+                     labels=strings.choices['VolumeOpts.interpolation']),
         props.Widget('xColour'),
         props.Widget('yColour'),
         props.Widget('zColour'),
@@ -335,9 +336,11 @@ _DISPLAY_PROPS = td.TypeDict({
         props.Widget('suppressZ'),
         props.Widget('modulateImage', labels=_imageName),
         props.Widget('clipImage',     labels=_imageName),
-        props.Widget('clipThreshold',
+        props.Widget('clippingRange',
                      showLimits=False,
-                     spin=False,
+                     slider=True,
+                     labels=[strings.choices['VectorOpts.clippingRange.min'],
+                             strings.choices['VectorOpts.clippingRange.max']],
                      dependencies=['clipImage'],
                      enabledWhen=lambda o, ci: ci is not None)],
 
@@ -353,9 +356,11 @@ _DISPLAY_PROPS = td.TypeDict({
         props.Widget('lineWidth', showLimits=False),
         props.Widget('modulateImage', labels=_imageName),
         props.Widget('clipImage',     labels=_imageName),
-        props.Widget('clipThreshold',
+        props.Widget('clippingRange',
                      showLimits=False,
-                     spin=False,
+                     slider=True,
+                     labels=[strings.choices['VectorOpts.clippingRange.min'],
+                             strings.choices['VectorOpts.clippingRange.max']],
                      dependencies=['clipImage'],
                      enabledWhen=lambda o, ci: ci is not None)],
 
@@ -386,10 +391,11 @@ _DISPLAY_PROPS = td.TypeDict({
         props.Widget('suppressZ'),
         props.Widget('modulateImage', labels=_imageName),
         props.Widget('clipImage',     labels=_imageName),
-        
-        props.Widget('clipThreshold',
+        props.Widget('clippingRange',
                      showLimits=False,
-                     spin=False,
+                     slider=True,
+                     labels=[strings.choices['VectorOpts.clippingRange.min'],
+                             strings.choices['VectorOpts.clippingRange.max']],
                      dependencies=['clipImage'],
                      enabledWhen=lambda o, ci: ci is not None)],
 
diff --git a/fsl/fsleyes/controls/overlaydisplaytoolbar.py b/fsl/fsleyes/controls/overlaydisplaytoolbar.py
index f860b7ced..b39e47f44 100644
--- a/fsl/fsleyes/controls/overlaydisplaytoolbar.py
+++ b/fsl/fsleyes/controls/overlaydisplaytoolbar.py
@@ -331,7 +331,7 @@ class OverlayDisplayToolBar(fsltoolbar.FSLEyesToolBar):
         """        
         
         modSpec   = _TOOLBAR_PROPS[opts]['modulateImage']
-        thresSpec = _TOOLBAR_PROPS[opts]['clipThreshold']
+        thresSpec = _TOOLBAR_PROPS[opts]['clippingRange']
 
         panel = wx.Panel(self)
         sizer = wx.FlexGridSizer(2, 2)
@@ -342,8 +342,7 @@ class OverlayDisplayToolBar(fsltoolbar.FSLEyesToolBar):
         modLabel    = wx.StaticText(panel)
         thresLabel  = wx.StaticText(panel)
 
-        modLabel  .SetLabel(strings.properties[opts, 'modulateImage'])
-        thresLabel.SetLabel(strings.properties[opts, 'clipThreshold'])
+        modLabel.SetLabel(strings.properties[opts, 'modulateImage'])
 
         sizer.Add(modLabel)
         sizer.Add(modWidget,   flag=wx.EXPAND)
@@ -434,13 +433,13 @@ _TOOLTIPS = td.TypeDict({
 
     'RGBVectorOpts.modulateImage' : fsltooltips.properties['VectorOpts.'
                                                            'modulateImage'],
-    'RGBVectorOpts.clipThreshold' : fsltooltips.properties['VectorOpts.'
-                                                           'clipThreshold'],
+    'RGBVectorOpts.clippingRange' : fsltooltips.properties['VectorOpts.'
+                                                           'clippingRange'],
 
     'LineVectorOpts.modulateImage' : fsltooltips.properties['VectorOpts.'
                                                             'modulateImage'],
-    'LineVectorOpts.clipThreshold' : fsltooltips.properties['VectorOpts.'
-                                                            'clipThreshold'],
+    'LineVectorOpts.clippingRange' : fsltooltips.properties['VectorOpts.'
+                                                            'clippingRange'],
     'LineVectorOpts.lineWidth'    : fsltooltips.properties['LineVectorOpts.'
                                                            'lineWidth'],
 
@@ -451,8 +450,8 @@ _TOOLTIPS = td.TypeDict({
 
     'TensorOpts.modulateImage' : fsltooltips.properties['VectorOpts.'
                                                         'modulateImage'],
-    'TensorOpts.clipThreshold' : fsltooltips.properties['VectorOpts.'
-                                                        'clipThreshold'], 
+    'TensorOpts.clippingRange' : fsltooltips.properties['VectorOpts.'
+                                                        'clippingRange'], 
 })
 """This dictionary contains tooltips for :class:`.Display` and
 :class:`.DisplayOpts` properties. It is referenced in the
@@ -536,22 +535,28 @@ _TOOLBAR_PROPS = td.TypeDict({
             'modulateImage',
             labels=_modImageLabel,
             tooltip=_TOOLTIPS['RGBVectorOpts.modulateImage']),
-        'clipThreshold' : props.Widget(
-            'clipThreshold',
+        'clippingRange' : props.Widget(
+            'clippingRange',
             showLimits=False,
-            spin=False,
-            tooltip=_TOOLTIPS['RGBVectorOpts.clipThreshold'])},
+            slider=True,
+            labels=[strings.choices['VectorOpts.clippingRange.min'],
+                    strings.choices['VectorOpts.clippingRange.max']],
+            dependencies=['clipImage'],
+            enabledWhen=lambda o, ci: ci is not None)},
 
     'LineVectorOpts' : {
         'modulateImage' : props.Widget(
             'modulateImage',
             labels=_modImageLabel,
             tooltip=_TOOLTIPS['LineVectorOpts.modulateImage']),
-        'clipThreshold' : props.Widget(
-            'clipThreshold',
+        'clippingRange' : props.Widget(
+            'clippingRange',
             showLimits=False,
-            spin=False,
-            tooltip=_TOOLTIPS['LineVectorOpts.clipThreshold']), 
+            slider=True,
+            labels=[strings.choices['VectorOpts.clippingRange.min'],
+                    strings.choices['VectorOpts.clippingRange.max']],
+            dependencies=['clipImage'],
+            enabledWhen=lambda o, ci: ci is not None),
         'lineWidth' : props.Widget(
             'lineWidth',
             showLimits=False,
@@ -582,11 +587,14 @@ _TOOLBAR_PROPS = td.TypeDict({
             'modulateImage',
             labels=_modImageLabel,
             tooltip=_TOOLTIPS['TensorOpts.modulateImage']),
-        'clipThreshold' : props.Widget(
-            'clipThreshold',
+        'clippingRange' : props.Widget(
+            'clippingRange',
             showLimits=False,
-            spin=False,
-            tooltip=_TOOLTIPS['TensorOpts.clipThreshold'])}
+            slider=True,
+            labels=[strings.choices['VectorOpts.clippingRange.min'],
+                    strings.choices['VectorOpts.clippingRange.max']],
+            dependencies=['clipImage'],
+            enabledWhen=lambda o, ci: ci is not None)}
 })
 """This dictionary defines specifications for all controls shown on an
 :class:`OverlayDisplayToolBar`. 
diff --git a/fsl/fsleyes/displaycontext/group.py b/fsl/fsleyes/displaycontext/group.py
index 3485e7432..dd9edf2bb 100644
--- a/fsl/fsleyes/displaycontext/group.py
+++ b/fsl/fsleyes/displaycontext/group.py
@@ -76,7 +76,7 @@ class OverlayGroup(props.HasProperties):
                             'suppressZ',
                             'modulateImage',
                             'clipImage',
-                            'clipThreshold'],
+                            'clippingRange'],
         'LineVectorOpts' : ['lineWidth',
                             'directed'],
         'RGBVectorOpts'  : ['interpolation'],
diff --git a/fsl/fsleyes/displaycontext/vectoropts.py b/fsl/fsleyes/displaycontext/vectoropts.py
index 2487eb2f7..cdc412d41 100644
--- a/fsl/fsleyes/displaycontext/vectoropts.py
+++ b/fsl/fsleyes/displaycontext/vectoropts.py
@@ -60,15 +60,13 @@ class VectorOpts(volumeopts.Nifti1Opts):
     """Clip voxels from the vector image according to another image. Any image
     which is in the :class:`.OverlayList`, and which has the same voxel
     dimensions as the vector image can be selected for clipping. The
-    :attr:`clipThreshold` dictates the value below which vector voxels are
+    :attr:`clippingRange` dictates the value below which vector voxels are
     clipped.
     """ 
 
     
-    clipThreshold = props.Real(default=0.0, minval=0, maxval=1)
-    """Hide voxels for which the modulation value is below this threshold,
-    as a percentage of the :attr:`modulate` image data range.
-    """
+    clippingRange = props.Bounds(ndims=1)
+    """Hide voxels for which the clip image value is outside of this range. """
 
     
     def __init__(self, *args, **kwargs):
@@ -105,20 +103,24 @@ class VectorOpts(volumeopts.Nifti1Opts):
         
     def __clipImageChanged(self, *a):
         """Called when the :attr:`clipImage` property changes. Updates
-        the range of the :attr:`clipThreshold` property.
+        the range of the :attr:`clippingRange` property.
         """
 
         image = self.clipImage
 
         if image is None:
+            self.clippingRange.xmin = 0
+            self.clippingRange.xmax = 1
+            self.clippingRange.x    = [0, 1]
             return
 
         opts   = self.displayCtx.getOpts(image)
         minval = opts.dataMin
         maxval = opts.dataMax
 
-        self.setConstraint('clipThreshold', 'minval',  minval)
-        self.setConstraint('clipThreshold', 'maxval',  maxval) 
+        self.clippingRange.xmin =  minval
+        self.clippingRange.xmax =  maxval
+        self.clippingRange.x    = [minval, maxval]
 
         
     def __overlayListChanged(self, *a):
diff --git a/fsl/fsleyes/fsleyes_parseargs.py b/fsl/fsleyes/fsleyes_parseargs.py
index a6d3f1f30..a8341c310 100644
--- a/fsl/fsleyes/fsleyes_parseargs.py
+++ b/fsl/fsleyes/fsleyes_parseargs.py
@@ -207,7 +207,7 @@ OPTIONS = td.TypeDict({
                         'suppressZ',
                         'modulateImage',
                         'clipImage',
-                        'clipThreshold'],
+                        'clippingRange'],
     'LineVectorOpts' : ['lineWidth',
                         'directed'],
     'RGBVectorOpts'  : ['interpolation'],
@@ -347,7 +347,7 @@ ARGUMENTS = td.TypeDict({
     'VectorOpts.suppressZ'     : ('zs', 'suppressZ'),
     'VectorOpts.modulateImage' : ('md', 'modulateImage'),
     'VectorOpts.clipImage'     : ('cl', 'clipImage'),
-    'VectorOpts.clipThreshold' : ('ct', 'clipThreshold'),
+    'VectorOpts.clippingRange' : ('cp', 'clipRange'),
 
     'LineVectorOpts.lineWidth'    : ('lvw', 'lineWidth'),
     'LineVectorOpts.directed'     : ('lvi', 'directed'),
@@ -453,9 +453,8 @@ HELP = td.TypeDict({
     'VectorOpts.suppressZ'     : 'Suppress Z magnitude',
     'VectorOpts.modulateImage' : 'Modulate vector brightness',
     'VectorOpts.clipImage'     : 'Clip vector voxels',
-    'VectorOpts.clipThreshold' : 'Hide voxels where clip image '
-                                 'value is below this threshold '
-                                 '(expressed as a percentage)',
+    'VectorOpts.clippingRange' : 'Hide voxels where clip image '
+                                 'value is outside of this range.',
 
     'LineVectorOpts.lineWidth'    : 'Line width',
     'LineVectorOpts.directed'     : 'Interpret vectors as directed',
diff --git a/fsl/fsleyes/gl/gl21/gllinevector_funcs.py b/fsl/fsleyes/gl/gl21/gllinevector_funcs.py
index 436c75e92..2c8a44cc7 100644
--- a/fsl/fsleyes/gl/gl21/gllinevector_funcs.py
+++ b/fsl/fsleyes/gl/gl21/gllinevector_funcs.py
@@ -101,9 +101,9 @@ def compileShaders(self):
                     'directed',     'imageDims']
 
     fragUniforms = ['imageTexture',   'modulateTexture', 'clipTexture',
-                    'clipThreshold',  'xColourTexture',  'yColourTexture',
-                    'zColourTexture', 'voxValXform',     'cmapXform',
-                    'imageShape',     'useSpline']
+                    'clipLow',        'clipHigh',        'xColourTexture',
+                    'yColourTexture', 'zColourTexture',  'voxValXform',
+                    'cmapXform',      'imageShape',      'useSpline']
 
     self.shaderVars = shaders.getShaderVars(self.shaders,
                                             vertAtts,
@@ -125,14 +125,21 @@ def updateShaderState(self):
     voxValXform   = self.imageTexture.voxValXform
     useSpline     = False
     imageShape    = np.array(image.shape[:3], dtype=np.float32)
-    clipThreshold = opts.clipThreshold
+    clippingRange = opts.clippingRange.x
 
     voxValXform = np.array(voxValXform, dtype=np.float32).ravel('C')
     cmapXform   = np.array(cmapXform,   dtype=np.float32).ravel('C')
 
-    invClipValXform = self.clipTexture .invVoxValXform
-    clipThreshold   = clipThreshold * invClipValXform[0, 0] + \
-                                      invClipValXform[3, 0] 
+
+    if opts.clipImage is not None:
+        invClipValXform = self.clipTexture .invVoxValXform
+        clipLow         = clippingRange[0] * invClipValXform[0, 0] + \
+                                             invClipValXform[3, 0]
+        clipHigh        = clippingRange[1] * invClipValXform[0, 0] + \
+                                             invClipValXform[3, 0]
+    else:
+        clipLow  = 0
+        clipHigh = 1
 
     gl.glUseProgram(self.shaders)
 
@@ -142,7 +149,8 @@ def updateShaderState(self):
     gl.glUniformMatrix4fv(svars['voxValXform'], 1, False, voxValXform)
     gl.glUniformMatrix4fv(svars['cmapXform'],   1, False, cmapXform)
 
-    gl.glUniform1f(svars['clipThreshold'], clipThreshold)
+    gl.glUniform1f(svars['clipLow'],  clipLow)
+    gl.glUniform1f(svars['clipHigh'], clipHigh)
 
     gl.glUniform1i(svars['imageTexture'],    0)
     gl.glUniform1i(svars['modulateTexture'], 1)
diff --git a/fsl/fsleyes/gl/gl21/glrgbvector_funcs.py b/fsl/fsleyes/gl/gl21/glrgbvector_funcs.py
index d500cfec9..8209bbc6c 100644
--- a/fsl/fsleyes/gl/gl21/glrgbvector_funcs.py
+++ b/fsl/fsleyes/gl/gl21/glrgbvector_funcs.py
@@ -66,9 +66,9 @@ def compileShaders(self):
     vertAtts     = ['vertex', 'voxCoord', 'texCoord']
 
     fragUniforms = ['imageTexture',   'modulateTexture', 'clipTexture',
-                    'clipThreshold',  'xColourTexture',  'yColourTexture',
-                    'zColourTexture', 'voxValXform',     'cmapXform',
-                    'imageShape',     'useSpline']
+                    'clipLow',        'clipHigh',        'xColourTexture',
+                    'yColourTexture', 'zColourTexture',  'voxValXform',
+                    'cmapXform',      'imageShape',      'useSpline']
 
     for va in vertAtts:
         shaderVars[va] = gl.glGetAttribLocation(self.shaders, va)
@@ -97,14 +97,20 @@ def updateShaderState(self):
     cmapXform       = self.xColourTexture.getCoordinateTransform()
     useSpline       = opts.interpolation == 'spline'
     imageShape      = np.array(self.vectorImage.shape, dtype=np.float32)
-    clipThreshold   = opts.clipThreshold
+    clippingRange   = opts.clippingRange
 
     # Transform the clip threshold into
     # the texture value range, so the
     # fragment shader can compare texture
     # values directly to it.
-    clipThreshold = clipThreshold * invClipValXform[0, 0] + \
-                                    invClipValXform[3, 0]
+    if opts.clipImage is not None:
+        clipLow  = clippingRange[0] * \
+            invClipValXform[0, 0] + invClipValXform[3, 0]
+        clipHigh = clippingRange[1] * \
+            invClipValXform[0, 0] + invClipValXform[3, 0]
+    else:
+        clipLow  = 0
+        clipHigh = 1
     
     gl.glUseProgram(self.shaders)
 
@@ -114,7 +120,8 @@ def updateShaderState(self):
     gl.glUniformMatrix4fv(svars['voxValXform'], 1, False, voxValXform)
     gl.glUniformMatrix4fv(svars['cmapXform'],   1, False, cmapXform)
 
-    gl.glUniform1f(svars['clipThreshold'],   clipThreshold)
+    gl.glUniform1f(svars['clipLow'],         clipLow)
+    gl.glUniform1f(svars['clipHigh'],        clipHigh)
     gl.glUniform1i(svars['imageTexture'],    0)
     gl.glUniform1i(svars['modulateTexture'], 1)
     gl.glUniform1i(svars['clipTexture'],     2)
diff --git a/fsl/fsleyes/gl/gl21/gltensor_funcs.py b/fsl/fsleyes/gl/gl21/gltensor_funcs.py
index ba5c87cbe..2cda200a6 100644
--- a/fsl/fsleyes/gl/gl21/gltensor_funcs.py
+++ b/fsl/fsleyes/gl/gl21/gltensor_funcs.py
@@ -114,9 +114,9 @@ def compileShaders(self):
                     'eigValNorm',      'zax']
 
     fragUniforms = ['imageTexture',   'modulateTexture', 'clipTexture',
-                    'clipThreshold',  'xColourTexture',  'yColourTexture',
-                    'zColourTexture', 'voxValXform',     'cmapXform',
-                    'imageShape',     'useSpline']
+                    'clipLow',        'clipHigh',        'xColourTexture',
+                    'yColourTexture', 'zColourTexture',  'voxValXform',
+                    'cmapXform',      'imageShape',     'useSpline']
 
 
     self.shaderVars = shaders.getShaderVars(self.shaders,
@@ -180,7 +180,7 @@ def updateShaderState(self):
     # Other miscellaneous uniforms
     imageShape    = np.array(self.image.shape[:3], dtype=np.float32)
     resolution    = opts.tensorResolution
-    clipThreshold = opts.clipThreshold
+    clippingRange = opts.clippingRange
     tensorScale   = opts.tensorScale
     lighting      = 1 if opts.lighting else 0
     useSpline     = 0
@@ -189,16 +189,23 @@ def updateShaderState(self):
     eigValNorm  = 0.5 / abs(l1.data).max()
     eigValNorm *= tensorScale / 100.0
 
-    invClipValXform = self.clipTexture .invVoxValXform
-    clipThreshold   = clipThreshold * invClipValXform[0, 0] + \
-                                      invClipValXform[3, 0] 
-
-    gl.glUniform3fv(svars['imageShape'], 1,  imageShape)
-    gl.glUniform1f( svars['resolution'],     resolution)
-    gl.glUniform1f( svars['eigValNorm'],     eigValNorm)
-    gl.glUniform1f( svars['lighting'],       lighting)
-    gl.glUniform1f( svars['clipThreshold'],  clipThreshold)
-    gl.glUniform1f( svars['useSpline'],      useSpline)
+    if opts.clipImage is not None:
+        invClipValXform = self.clipTexture .invVoxValXform
+        clipLow         = clippingRange[0] * invClipValXform[0, 0] + \
+                                             invClipValXform[3, 0]
+        clipHigh        = clippingRange[1] * invClipValXform[0, 0] + \
+                                             invClipValXform[3, 0]
+    else:
+        clipLow  = 0
+        clipHigh = 1
+
+    gl.glUniform3fv(svars['imageShape'], 1, imageShape)
+    gl.glUniform1f( svars['resolution'],    resolution)
+    gl.glUniform1f( svars['eigValNorm'],    eigValNorm)
+    gl.glUniform1f( svars['lighting'],      lighting)
+    gl.glUniform1f( svars['clipLow'],       clipLow)
+    gl.glUniform1f( svars['clipHigh'],      clipHigh)
+    gl.glUniform1f( svars['useSpline'],     useSpline)
     
     # Vertices of a unit sphere. The vertex
     # shader will transform these vertices
diff --git a/fsl/fsleyes/gl/gl21/glvector_frag.glsl b/fsl/fsleyes/gl/gl21/glvector_frag.glsl
index 6dd2fe212..256b3649c 100644
--- a/fsl/fsleyes/gl/gl21/glvector_frag.glsl
+++ b/fsl/fsleyes/gl/gl21/glvector_frag.glsl
@@ -29,10 +29,13 @@ uniform sampler3D clipTexture;
 
 
 /*
- * If the clipping value is below this
- * threshold, the fragment is clipped.
+ * If the clipping value is outside of
+ * this range, the fragment is clipped.
+ * These values should be in the texture 
+ * data range of the clipTexture.
  */
-uniform float clipThreshold;
+uniform float clipLow;
+uniform float clipHigh;
 
 /*
  * Colour map for the X vector component.
@@ -125,8 +128,8 @@ void main(void) {
     clipValue = texture3D(clipTexture,     fragTexCoord).x;
   }
 
-  /* Knock out voxels where the clipping value is below the threshold */
-  if (clipValue < clipThreshold) {
+  /* Knock out voxels where the clipping value is outside the clipping range */
+  if (clipValue < clipLow || clipValue > clipHigh) {
       gl_FragColor.a = 0.0;
       return;
   }
diff --git a/fsl/fsleyes/gl/glvector.py b/fsl/fsleyes/gl/glvector.py
index dbcccb828..bd40a0f5a 100644
--- a/fsl/fsleyes/gl/glvector.py
+++ b/fsl/fsleyes/gl/glvector.py
@@ -212,7 +212,7 @@ class GLVector(globject.GLImageObject):
         opts   .addListener('suppressZ',     name, cmapUpdate,    weak=False)
         opts   .addListener('modulateImage', name, modUpdate,     weak=False)
         opts   .addListener('clipImage',     name, clipUpdate,    weak=False)
-        opts   .addListener('clipThreshold', name, shaderUpdate,  weak=False)
+        opts   .addListener('clippingRange', name, shaderUpdate,  weak=False)
         opts   .addListener('resolution',    name, imageUpdate,   weak=False)
         opts   .addListener('transform',     name, update,        weak=False)
 
@@ -244,7 +244,7 @@ class GLVector(globject.GLImageObject):
         opts   .removeListener('suppressZ',     name)
         opts   .removeListener('modulateImage', name)
         opts   .removeListener('clipImage',     name)
-        opts   .removeListener('clipThreshold', name)
+        opts   .removeListener('clippingRange', name)
         opts   .removeListener('volume',        name)
         opts   .removeListener('resolution',    name)
         opts   .removeListener('transform' ,    name)
diff --git a/fsl/fsleyes/tooltips.py b/fsl/fsleyes/tooltips.py
index 7afa7babf..a03f67579 100644
--- a/fsl/fsleyes/tooltips.py
+++ b/fsl/fsleyes/tooltips.py
@@ -151,9 +151,9 @@ properties = TypeDict({
                                     'not shown. The clipping image must have '
                                     'the same voxel dimensions as the vector '
                                     'image. ', 
-    'VectorOpts.clipThreshold'    : 'Vector values which have a corresponding '
-                                    'clipping image value that is less than '
-                                    'this threshold are not displayed. ',
+    'VectorOpts.clippingRange'    : 'Vector values which have a corresponding '
+                                    'clipping image value that is outside of '
+                                    'this range are not displayed. ',
     'LineVectorOpts.lineWidth'    : 'The width of each vector line, in '
                                     'display pixels.',
     'LineVectorOpts.directed'     : 'If unchecked, the vector data is assumed '
-- 
GitLab