Source code for fsleyes.gl.gl21.gllinevector_funcs

#
# gllinevector_funcs.py - OpenGL 2.1 functions used by the GLLineVector class.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides functions which are used by the :class:`.GLLineVector`
class to render :class:`.Image` overlays as line vector images in an OpenGL 2.1
compatible manner.


This module uses functions in the :mod:`.gl21.glvector_funcs` module, which
contains logic used for rendering both ``GLRGBVector`` and ``GLLineVector``
instances.


The voxel coordinates for every vector are passed directly to a vertex shader
program which calculates the position of the corresponding line vertices.


The ``glvector`` fragment shader (the same as that used by the
:class:`.GLRGBVector` class) is used to colour each line according to the
orientation of the underlying vector.
"""


from __future__ import division


import logging

import numpy               as np
import OpenGL.GL           as gl

import fsl.data.constants   as constants
import fsl.transform.affine as affine
from . import                  glvector_funcs


log = logging.getLogger(__name__)


[docs]def init(self): """Compiles and configures the vertex/fragment shaders used to render the ``GLLineVector`` via calls to :func:`compileShaders` and :func:`updateShaderState`. """ self.shader = None name = self.name compileShaders( self) updateShaderState(self) def update(*a, **kwa): updateShaderState(self) self.notify() # GLVector.addListener adds a listener # for the transform property, so we # overwrite it here - we need to update # the display<->voxel transformation # matrices whenever the transform # changes. self.opts.addListener('orientFlip', name, update, weak=False) self.opts.addListener('directed', name, update, weak=False) self.opts.addListener('lengthScale', name, update, weak=False) self.opts.addListener('transform', name, update, overwrite=True, weak=False)
[docs]def destroy(self): """Deletes the vertex/fragment shaders. """ self.opts.removeListener('orientFlip', self.name) self.opts.removeListener('directed', self.name) self.opts.removeListener('lengthScale', self.name) self.opts.removeListener('transform', self.name) self.shader.destroy()
[docs]def compileShaders(self): """Compiles the vertex/fragment shaders via the :func:`.gl21.glvector_funcs.compileShaders` function. """ self.shader = glvector_funcs.compileShaders(self, 'gllinevector')
[docs]def updateShaderState(self): """Updates all variables used by the vertex/fragment shaders. The fragment shader is configured by the :func:`.gl21.glvector_funcs.updateFragmentShaderState` function. """ shader = self.shader shader.load() changed = glvector_funcs.updateShaderState(self) image = self.vectorImage opts = self.opts # see comments in gl21/glvector_funcs.py if self.vectorImage.niftiDataType == constants.NIFTI_DT_RGB24: vvxMat = affine.scaleOffsetXform(2, -1) else: vvxMat = self.imageTexture.voxValXform directed = opts.directed lengthScale = opts.lengthScale / 100.0 imageDims = image.pixdim[:3] d2vMat = opts.getTransform('display', 'voxel') v2dMat = opts.getTransform('voxel', 'display') xFlip = opts.orientFlip # If the unitLength option is on, the vector # data will have already been scaled to have # length 1 (see GLLineVector.__init__). But # we draw vectors in two parts, from the voxel # centre. So we have to half the vector lengths. if opts.unitLength: lengthScale /= 2 # We also scale the vector data by the # minimum voxel length, so that each # vector has unit length relative to # the voxel dimensions. fac = (image.pixdim[:3] / min(image.pixdim[:3])) lengthScale /= fac changed |= shader.set('vectorTexture', 4) changed |= shader.set('displayToVoxMat', d2vMat) changed |= shader.set('voxToDisplayMat', v2dMat) changed |= shader.set('voxValXform', vvxMat) changed |= shader.set('imageDims', imageDims) changed |= shader.set('directed', directed) changed |= shader.set('lengthScale', lengthScale) changed |= shader.set('xFlip', xFlip) shader.unload() return changed
[docs]def preDraw(self, xform=None, bbox=None): """Prepares the GL state for drawing. This amounts to loading the vertex/fragment shader programs. """ self.shader.load()
[docs]def draw2D(self, zpos, axes, xform=None, bbox=None): """Draws the line vectors at a plane at the specified Z location. Voxel coordinates are passed to the vertex shader, which calculates the corresponding line vertex locations. """ opts = self.opts shader = self.shader v2dMat = opts.getTransform('voxel', 'display') voxels = self.generateVoxelCoordinates2D(zpos, axes, bbox=bbox) voxels = np.repeat(voxels, 2, 0) indices = np.arange(voxels.shape[0], dtype=np.uint32) if xform is None: xform = v2dMat else: xform = affine.concat(xform, v2dMat) shader.set( 'voxToDisplayMat', xform) shader.setAtt('vertexID', indices) shader.setAtt('voxel', voxels) shader.loadAtts() gl.glLineWidth(opts.lineWidth) gl.glDrawArrays(gl.GL_LINES, 0, voxels.size // 3) shader.unloadAtts()
[docs]def draw3D(self, xform=None, bbox=None): """Draws the line vectors in 3D space. """ pass
[docs]def drawAll(self, axes, zposes, xforms): """Draws the line vectors at every slice specified by the Z locations. """ for zpos, xform in zip(zposes, xforms): self.draw2D(zpos, axes, xform)
[docs]def postDraw(self, xform=None, bbox=None): """Clears the GL state after drawing. """ self.shader.unload()