glrgbvector.py 9.64 KB
Newer Older
Paul McCarthy's avatar
Paul McCarthy committed
1
2
#!/usr/bin/env python
#
3
# glrgbvector.py - The GLRGBVector class.
Paul McCarthy's avatar
Paul McCarthy committed
4
5
6
7
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This mdoule provides the :class:`GLRGBVector` class, for displaying 3D
8
vector :class:`.Image` overlays in RGB mode.
Paul McCarthy's avatar
Paul McCarthy committed
9
10
"""

11

12
13
import numpy               as np
import OpenGL.GL           as gl
Paul McCarthy's avatar
Paul McCarthy committed
14

15
16
import fsl.data.dtifit     as dtifit
import fsleyes.gl          as fslgl
17
import fsleyes.gl.routines as glroutines
18
import fsleyes.gl.glvector as glvector
Paul McCarthy's avatar
Paul McCarthy committed
19
20
21


class GLRGBVector(glvector.GLVector):
22
23
24
25
26
27
28
29
30
    """The ``GLRGBVector`` class encapsulates the logic required to render a
    ``x*y*z*3`` :class:`.Image` instance as a vector image, where the
    direction of the vector at each voxel is represented by a combination of
    three colours (one colour per axis).  The ``GLRGBVector`` class assumes
    that the :class:`.Display` instance associated with the ``Image`` overlay
    holds a reference to a :class:`.RGBVectorOpts` instance, which contains
    ``GLRGBVector``-specific display settings. The ``GLRGBVector`` is a
    sub-class of the :class:`.GLVector` class, and uses the functionality
    provided by ``GLVector``.
Paul McCarthy's avatar
Paul McCarthy committed
31

Paul McCarthy's avatar
Paul McCarthy committed
32

33
34
35
36
37
    A ``GLRGBVector`` can only show the magnitude of a vector, not its
    orientation. Therefore, the absolute values of the :class:`.Image`
    instance are stored in the :class:`.ImageTexture`. This is accomplished
    by passing a ``prefilter`` function to :meth:`.GLVector.__init__`, which
    forces the image values to be unsigned.
Paul McCarthy's avatar
Paul McCarthy committed
38

Paul McCarthy's avatar
Paul McCarthy committed
39

40
41
42
43
44
    The ``GLRGBVector`` uses two OpenGL version-specific modules, the
    :mod:`.gl14.glrgbvector_funcs` and :mod:`.gl21.glrgbvector_funcs` modules,
    to manage the vertex/fragment shader programs that are used in rendering.
    These modules are assumed to provide the following functions:

Paul McCarthy's avatar
Paul McCarthy committed
45
46
47
48
49
50
51
52
53

    ========================================== ===============================
    ``init(GLRGBVector)``                      Perform any necessary
                                               initialisation.
    ``destroy(GLRGBVector)``                   Perform any necessary clean up.
    ``compileShaders(GLRGBVector)``            Compiles vertex/fragment
                                               shaders.
    ``updateShaderState(GLRGBVector)``         Updates vertex/fragment
                                               shaders.
54
    ``preDraw(GLRGBVector, xform, bbox)``      Prepare the GL state for
Paul McCarthy's avatar
Paul McCarthy committed
55
56
                                               drawing.
    ``draw2D(GLRGBVector, zpos, xform, bbox)`` Draw the slice specified by
Paul McCarthy's avatar
Paul McCarthy committed
57
                                               ``zpos``.
Paul McCarthy's avatar
Paul McCarthy committed
58
59
60
    ``draw3D(GLRGBVector, zpos, xform)``       Draw the volume in 3D
    ``drawAll(GLRGBVector, zposes, xforms)``   Draw all slices specified by
                                               ``zposes``.
61
    ``postDraw(GLRGBVector, xform, bbox)``     Clean up the GL state after
Paul McCarthy's avatar
Paul McCarthy committed
62
63
                                               drawing.
    ========================================== ===============================
64
    """
Paul McCarthy's avatar
Paul McCarthy committed
65
66


67
    def __init__(self, image, overlayList, displayCtx, canvas, threedee):
68
69
        """Create a ``GLRGBVector``.

70
71
72
73
74
75
        :arg image:       An :class:`.Image` or :class:`.DTIFitTensor`
                          instance.
        :arg overlayList: The :class:`.OverlayList`
        :arg displayCtx:  The :class:`.DisplayContext` managing the scene.
        :arg canvas:      The canvas doing the drawing.
        :arg threedee:    2D or 3D rendering
76
        """
Paul McCarthy's avatar
Paul McCarthy committed
77

78
        # If the overlay is a DTIFitTensor, use the
79
80
        # V1 image is the vector data. Otherwise,
        # assume that the overlay is the vector image.
81
82
        if isinstance(image, dtifit.DTIFitTensor): vecImage = image.V1()
        else:                                      vecImage = image
83

84
        def prefilter(data):
85
86
87
88

            # Vector images stored as RGB24 data
            # type are assumed to map from [0, 255]
            # to [-1, 1], so cannot be normalised
89
            if vecImage.nvals > 1:
90
91
                return data

92
93
94
95
96
            # make absolute, and scale to unit
            # length if required. We must make
            # the data absolute, otherwise we
            # cannot perform interpolation on
            # the texture when displaying it.
97
98
99
100
101
102
103
104
105
106
107
            data = np.abs(data)
            if self.opts.unitLength:
                with np.errstate(invalid='ignore'):
                    x            = data[0, ...]
                    y            = data[1, ...]
                    z            = data[2, ...]
                    lens         = np.sqrt(x ** 2 + y ** 2 + z ** 2)
                    data[0, ...] = x / lens
                    data[1, ...] = y / lens
                    data[2, ...] = z / lens
            return data
Paul McCarthy's avatar
Paul McCarthy committed
108

109
        def prefilterRange(dmin, dmax):
110
            if self.opts.unitLength:
111
112
113
                return 0, 1
            else:
                return max((0, dmin)), max((abs(dmin), abs(dmax)))
114

115
116
        glvector.GLVector.__init__(self,
                                   image,
117
                                   overlayList,
118
                                   displayCtx,
119
                                   canvas,
Paul McCarthy's avatar
Paul McCarthy committed
120
                                   threedee,
121
122
                                   prefilter=prefilter,
                                   prefilterRange=prefilterRange,
123
124
125
                                   vectorImage=vecImage,
                                   init=lambda: fslgl.glrgbvector_funcs.init(
                                       self))
Paul McCarthy's avatar
Paul McCarthy committed
126

Paul McCarthy's avatar
Paul McCarthy committed
127
128
129
        self.opts.addListener('interpolation',
                              self.name,
                              self.__interpChanged)
130
131
132
133
        self.opts.addListener('unitLength',
                              self.name,
                              self.__unitLengthChanged)

Paul McCarthy's avatar
Paul McCarthy committed
134

Paul McCarthy's avatar
Paul McCarthy committed
135
136

    def destroy(self):
137
138
139
140
141
        """Must be called when this ``GLRGBVector`` is no longer needed.
        Removes some property listeners from the :class:`.RGBVectorOpts`
        instance, calls the OpenGL version-specific ``destroy``
        function, and calls the :meth:`.GLVector.destroy` method.
        """
Paul McCarthy's avatar
Paul McCarthy committed
142
        self.opts.removeListener('interpolation', self.name)
143
        self.opts.removeListener('unitLength',    self.name)
144
        fslgl.glrgbvector_funcs.destroy(self)
Paul McCarthy's avatar
Paul McCarthy committed
145
146
147
148
        glvector.GLVector.destroy(self)


    def refreshImageTexture(self):
149
        """Overrides :meth:`.GLVector.refreshImageTexture`. Calls the base
150
        class implementation.
151
        """
Paul McCarthy's avatar
Paul McCarthy committed
152
        opts = self.opts
Paul McCarthy's avatar
Paul McCarthy committed
153
154

        if opts.interpolation == 'none': interp = gl.GL_NEAREST
Paul McCarthy's avatar
Paul McCarthy committed
155
156
        else:                            interp = gl.GL_LINEAR

157
        glvector.GLVector.refreshImageTexture(self, interp)
Paul McCarthy's avatar
Paul McCarthy committed
158

159

160
    def registerAuxImage(self, which, image, onReady=None):
161
162
163
        """Overrides :meth:`.GLVector.refreshAuxTexture`. Calls the base
        class implementation.
        """
Paul McCarthy's avatar
Paul McCarthy committed
164
        opts = self.opts
165
166

        if opts.interpolation == 'none': interp = gl.GL_NEAREST
Paul McCarthy's avatar
Paul McCarthy committed
167
168
        else:                            interp = gl.GL_LINEAR

169
170
        glvector.GLVector.registerAuxImage(
            self, which, image, onReady, interp=interp)
171
172


Paul McCarthy's avatar
Paul McCarthy committed
173
    def __interpChanged(self, *a):
174
175
176
        """Called when the :attr:`.RGBVectorOpts.interpolation` property
        changes. Updates the :class:`.ImageTexture` interpolation.
        """
Paul McCarthy's avatar
Paul McCarthy committed
177
        opts = self.opts
178
179

        if opts.interpolation == 'none': interp = gl.GL_NEAREST
Paul McCarthy's avatar
Paul McCarthy committed
180
181
        else:                            interp = gl.GL_LINEAR

182
183
184
185
        self.imageTexture   .set(interp=interp)
        self.modulateTexture.set(interp=interp)
        self.clipTexture    .set(interp=interp)
        self.colourTexture  .set(interp=interp)
Paul McCarthy's avatar
Paul McCarthy committed
186
        self.asyncUpdateShaderState(alwaysNotify=True)
Paul McCarthy's avatar
Paul McCarthy committed
187

188

189
190
191
192
193
194
    def __unitLengthChanged(self, *a):
        """Called when :attr:`.RGBVectorOpts.unitLength` changes. Refreshes
        the texture data.
        """
        self.imageTexture.refresh()

Paul McCarthy's avatar
Paul McCarthy committed
195
196

    def compileShaders(self):
197
198
199
        """Overrides :meth:`.GLVector.compileShaders`. Calls the OpenGL
        version-specific ``compileShaders`` function.
        """
Paul McCarthy's avatar
Paul McCarthy committed
200
        fslgl.glrgbvector_funcs.compileShaders(self)
Paul McCarthy's avatar
Paul McCarthy committed
201

Paul McCarthy's avatar
Paul McCarthy committed
202
203

    def updateShaderState(self):
204
205
        """Overrides :meth:`.GLVector.compileShaders`. Calls the OpenGL
        version-specific ``updateShaderState`` function.
Paul McCarthy's avatar
Paul McCarthy committed
206
        """
207
        return fslgl.glrgbvector_funcs.updateShaderState(self)
Paul McCarthy's avatar
Paul McCarthy committed
208
209


210
    def preDraw(self, xform=None, bbox=None):
211
212
213
        """Overrides :meth:`.GLVector.preDraw`. Calls the base class
        implementation, and the OpenGL version-specific ``preDraw`` function.
        """
214
215
        glvector.GLVector.preDraw(self, xform, bbox)
        fslgl.glrgbvector_funcs.preDraw(self, xform, bbox)
Paul McCarthy's avatar
Paul McCarthy committed
216
217


Paul McCarthy's avatar
Paul McCarthy committed
218
    def draw2D(self, *args, **kwargs):
Paul McCarthy's avatar
Paul McCarthy committed
219
220
221
        """Overrides :meth:`.GLVector.draw2D`. Calls the OpenGL
        version-specific ``draw2D`` function.
        """
222
223
224
225
226
        with glroutines.enabled((gl.GL_CULL_FACE)):
            gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL)
            gl.glCullFace(gl.GL_BACK)
            gl.glFrontFace(self.frontFace())
            fslgl.glrgbvector_funcs.draw2D(self, *args, **kwargs)
Paul McCarthy's avatar
Paul McCarthy committed
227
228


Paul McCarthy's avatar
Paul McCarthy committed
229
    def draw3D(self, *args, **kwargs):
Paul McCarthy's avatar
Paul McCarthy committed
230
231
        """Overrides :meth:`.GLVector.draw3D`. Calls the OpenGL
        version-specific ``draw3D`` function.
Paul McCarthy's avatar
Paul McCarthy committed
232
        """
Paul McCarthy's avatar
Paul McCarthy committed
233
        fslgl.glrgbvector_funcs.draw3D(self, *args, **kwargs)
Paul McCarthy's avatar
Paul McCarthy committed
234

Paul McCarthy's avatar
Paul McCarthy committed
235

Paul McCarthy's avatar
Paul McCarthy committed
236
    def drawAll(self, *args, **kwargs):
237
238
        """Overrides :meth:`.GLVector.drawAll`. Calls the OpenGL
        version-specific ``drawAll`` function.
Paul McCarthy's avatar
Paul McCarthy committed
239
        """
Paul McCarthy's avatar
Paul McCarthy committed
240
        fslgl.glrgbvector_funcs.drawAll(self, *args, **kwargs)
Paul McCarthy's avatar
Paul McCarthy committed
241

Paul McCarthy's avatar
Paul McCarthy committed
242

243
    def postDraw(self, xform=None, bbox=None):
244
245
246
        """Overrides :meth:`.GLVector.postDraw`. Calls the base class
        implementation, and the OpenGL version-specific ``postDraw``
        function.
Paul McCarthy's avatar
Paul McCarthy committed
247
        """
248
249
        glvector.GLVector.postDraw(self, xform, bbox)
        fslgl.glrgbvector_funcs.postDraw(self, xform, bbox)