Пример #1
0
def draw3D(self, xform=None, bbox=None):
    """Draws the image in 3D on the canvas.

    :arg self:    The :class:`.GLVolume` object which is managing the image
                  to be drawn.

    :arg xform:   A 4*4 transformation matrix to be applied to the vertex
                  data.

    :arg bbox:    An optional bounding box.
    """

    opts = self.opts
    canvas = self.canvas
    copts = canvas.opts
    tex = self.renderTexture1
    proj = self.canvas.projectionMatrix
    vertices, voxCoords, texCoords = self.generateVertices3D(bbox)
    rayStep, texform = opts.calculateRayCastSettings(xform, proj)

    rayStep = affine.transformNormal(
        rayStep, self.imageTexture.texCoordXform(self.overlay.shape))
    texform = affine.concat(
        texform, self.imageTexture.invTexCoordXform(self.overlay.shape))

    # If lighting is enabled, we specify the light
    # position in image texture coordinates, to make
    # life easier for the shader
    if copts.light:
        lxform = opts.getTransform('display', 'texture')
        lightPos = affine.transform(canvas.lightPos, lxform)
    else:
        lightPos = [0, 0, 0]

    if xform is not None:
        vertices = affine.transform(vertices, xform)

    self.shader.set('lighting', copts.light)
    self.shader.set('tex2ScreenXform', texform)
    self.shader.set('rayStep', rayStep)
    self.shader.set('lightPos', lightPos)
    self.shader.setAtt('vertex', vertices)
    self.shader.setAtt('texCoord', texCoords)

    self.shader.loadAtts()

    tex.bindAsRenderTarget()
    gl.glDrawArrays(gl.GL_TRIANGLES, 0, 36)
    tex.unbindAsRenderTarget()

    self.shader.unloadAtts()
    self.shader.unload()
Пример #2
0
def test_transformNormal(seed):

    normals = -100 + 200 * np.random.random((50, 3))

    def tn(n, xform):

        xform = npla.inv(xform[:3, :3]).T
        return np.dot(xform, n)

    for n in normals:

        scales = -10 + np.random.random(3) * 10
        offsets = -100 + np.random.random(3) * 200
        rotations = -np.pi + np.random.random(3) * 2 * np.pi
        origin = -100 + np.random.random(3) * 200

        xform = affine.compose(scales, offsets, rotations, origin)

        expected = tn(n, xform)
        result = affine.transformNormal(n, xform)

        assert np.all(np.isclose(expected, result))
Пример #3
0
def draw3D(self, xform=None, bbox=None):
    """Draws the image in 3D on the canvas.

    :arg self:    The :class:`.GLVolume` object which is managing the image
                  to be drawn.

    :arg xform:   A 4*4 transformation matrix to be applied to the vertex
                  data.

    :arg bbox:    An optional bounding box.
    """

    opts = self.opts
    tex = self.renderTexture1
    proj = self.canvas.projectionMatrix
    vertices, voxCoords, texCoords = self.generateVertices3D(bbox)
    rayStep, texform = opts.calculateRayCastSettings(xform, proj)

    rayStep = affine.transformNormal(
        rayStep, self.imageTexture.texCoordXform(self.overlay.shape))
    texform = affine.concat(
        texform, self.imageTexture.invTexCoordXform(self.overlay.shape))

    if xform is not None:
        vertices = affine.transform(vertices, xform)

    self.shader.set('tex2ScreenXform', texform)
    self.shader.set('rayStep', rayStep)
    self.shader.setAtt('vertex', vertices)
    self.shader.setAtt('texCoord', texCoords)

    self.shader.loadAtts()

    tex.bindAsRenderTarget()
    gl.glDrawArrays(gl.GL_TRIANGLES, 0, 36)
    tex.unbindAsRenderTarget()

    self.shader.unloadAtts()
    self.shader.unload()
Пример #4
0
    def get3DClipPlane(self, planeIdx):
        """A convenience method which calculates a point-vector description
        of the specified clipping plane. ``planeIdx`` is an index into the
        :attr:`clipPosition`, :attr:`clipAzimuth`, and
        :attr:`clipInclination`, properties.

        Returns the clip plane at the given ``planeIdx`` as an origin and
        normal vector, in the display coordinate system..
        """

        pos     = self.clipPosition[   planeIdx]
        azimuth = self.clipAzimuth[    planeIdx]
        incline = self.clipInclination[planeIdx]

        b       = self.bounds
        pos     = pos             / 100.0
        azimuth = azimuth * np.pi / 180.0
        incline = incline * np.pi / 180.0

        xmid = b.xlo + 0.5 * b.xlen
        ymid = b.ylo + 0.5 * b.ylen
        zmid = b.zlo + 0.5 * b.zlen

        centre = [xmid, ymid, zmid]
        normal = [0, 0, -1]

        rot1     = affine.axisAnglesToRotMat(incline, 0, 0)
        rot2     = affine.axisAnglesToRotMat(0, 0, azimuth)
        rotation = affine.concat(rot2, rot1)

        normal = affine.transformNormal(normal, rotation)
        normal = affine.normalise(normal)

        offset = (pos - 0.5) * max((b.xlen, b.ylen, b.zlen))
        origin = centre + normal * offset

        return origin, normal
Пример #5
0
def updateShaderState(self):
    """Sets all variables required by the vertex and fragment programs. """

    if not self.ready():
        return

    opts = self.opts
    shader = self.shader

    # enable the vertex and fragment programs
    shader.load()

    # The voxValXform transformation turns
    # an image texture value into a raw
    # voxel value. The colourMapXform
    # transformation turns a raw voxel value
    # into a value between 0 and 1, suitable
    # for looking up an appropriate colour
    # in the 1D colour map texture.
    voxValXform = affine.concat(self.colourTexture.getCoordinateTransform(),
                                self.imageTexture.voxValXform)
    voxValXform = [voxValXform[0, 0], voxValXform[0, 3], 0, 0]

    # And the clipping range, normalised
    # to the image texture value range
    invClip = 1 if opts.invertClipping else -1
    useNegCmap = 1 if opts.useNegativeCmap else 0
    imageIsClip = 1 if opts.clipImage is None else -1

    # modalpha not applied in 3D
    modAlpha = 1 if opts.modulateAlpha else -1
    imageIsMod = 1 if opts.modulateImage is None else -1
    modXform = self.getModulateValueXform()

    imgXform = self.imageTexture.invVoxValXform
    if opts.clipImage is None: clipXform = imgXform
    else: clipXform = self.clipTexture.invVoxValXform

    clipLo = opts.clippingRange[0] * clipXform[0, 0] + clipXform[0, 3]
    clipHi = opts.clippingRange[1] * clipXform[0, 0] + clipXform[0, 3]
    texZero = 0.0 * imgXform[0, 0] + imgXform[0, 3]

    clipping = [clipLo, clipHi, invClip, imageIsClip]
    modulate = [modXform[0, 0], modXform[0, 3], modAlpha, imageIsMod]
    negCmap = [useNegCmap, texZero, modAlpha, 0]

    # disable clip image/modalpha for 3D
    if self.threedee:
        clipping[3] = 1
        modulate[2] = -1
        modulate[3] = 1

    changed = False
    changed |= shader.setFragParam('voxValXform', voxValXform)
    changed |= shader.setFragParam('clipping', clipping)
    changed |= shader.setFragParam('modulate', modulate)
    changed |= shader.setFragParam('negCmap', negCmap)

    if self.threedee:
        clipPlanes = np.zeros((5, 4), dtype=np.float32)
        d2tmat = opts.getTransform('display', 'texture')

        for i in range(opts.numClipPlanes):
            origin, normal = self.get3DClipPlane(i)
            origin = affine.transform(origin, d2tmat)
            normal = affine.transformNormal(normal, d2tmat)
            clipPlanes[i, :] = glroutines.planeEquation2(origin, normal)

        changed |= shader.setFragParam('clipPlanes', clipPlanes)

    self.shader.unload()

    return changed
Пример #6
0
def draw3D(self, xform=None, bbox=None):
    """Draws the image in 3D on the canvas.

    :arg self:    The :class:`.GLVolume` object which is managing the image
                  to be drawn.

    :arg xform:   A 4*4 transformation matrix to be applied to the vertex
                  data.

    :arg bbox:    An optional bounding box.
    """
    opts = self.opts
    canvas = self.canvas
    display = self.display
    shader = self.shader
    shape = self.image.shape
    proj = canvas.projectionMatrix
    src = self.renderTexture1
    dest = self.renderTexture2
    w, h = src.shape

    vertices, voxCoords, texCoords = self.generateVertices3D(bbox)
    rayStep, texform = opts.calculateRayCastSettings(xform, proj)

    rayStep = affine.transformNormal(rayStep,
                                     self.imageTexture.texCoordXform(shape))
    texform = affine.concat(texform, self.imageTexture.invTexCoordXform(shape))

    if xform is not None:
        vertices = affine.transform(vertices, xform)

    vertices = np.array(vertices, dtype=np.float32).ravel('C')

    outerLoop = opts.getNumOuterSteps()
    screenSize = [1.0 / w, 1.0 / h, 0, 0]
    rayStep = list(rayStep) + [0]
    texform = texform[2, :]
    settings = [(1 - opts.blendFactor)**2, 0, 0, display.alpha / 100.0]

    gl.glVertexPointer(3, gl.GL_FLOAT, 0, vertices)

    shader.setAtt('texCoord', texCoords)
    shader.setFragParam('rayStep', rayStep)
    shader.setFragParam('screenSize', screenSize)
    shader.setFragParam('tex2ScreenXform', texform)

    # Disable blending - we want each
    # loop to replace the contents of
    # the texture, not blend into it!
    with glroutines.enabled((gl.GL_VERTEX_ARRAY)), \
         glroutines.disabled((gl.GL_BLEND)):

        for i in range(outerLoop):

            settings = list(settings)
            dtex = src.depthTexture
            settings[1] = i * opts.numInnerSteps

            if i == outerLoop - 1: settings[2] = 1
            else: settings[2] = -1

            shader.setFragParam('settings', settings)

            dest.bindAsRenderTarget()
            src.bindTexture(gl.GL_TEXTURE5)
            dtex.bindTexture(gl.GL_TEXTURE6)

            gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
            gl.glDrawArrays(gl.GL_TRIANGLES, 0, 36)

            src.unbindTexture()
            dtex.unbindTexture()
            dest.unbindAsRenderTarget()

            dest, src = src, dest

    shader.unloadAtts()
    shader.unload()

    self.renderTexture1 = src
    self.renderTexture2 = dest
Пример #7
0
def updateShaderState(self):
    """Updates the parameters used by the shader programs, reflecting the
    current display properties.
    """

    if not self.ready():
        return

    opts = self.opts
    display = self.display
    shader = self.shader

    imageIsClip = opts.clipImage is None
    imageIsMod = opts.modulateImage is None

    # The clipping options are in the voxel value
    # range, but the shader needs them to be in image
    # texture value range (0.0 - 1.0). So let's scale
    # them.
    imgXform = self.imageTexture.invVoxValXform
    if imageIsClip: clipXform = imgXform
    else: clipXform = self.clipTexture.invVoxValXform

    modAlpha = opts.modulateAlpha
    modXform = self.getModulateValueXform()
    modScale = modXform[0, 0]
    modOffset = modXform[0, 3]

    clipLow = opts.clippingRange[0] * clipXform[0, 0] + clipXform[0, 3]
    clipHigh = opts.clippingRange[1] * clipXform[0, 0] + clipXform[0, 3]
    texZero = 0.0 * imgXform[0, 0] + imgXform[0, 3]
    imageShape = self.image.shape[:3]
    texShape = self.imageTexture.shape[:3]

    if len(texShape) == 2:
        texShape = list(texShape) + [1]

    if imageIsClip: clipImageShape = imageShape
    else: clipImageShape = opts.clipImage.shape[:3]
    if imageIsMod: modImageShape = imageShape
    else: modImageShape = opts.modulateImage.shape[:3]

    # Create a single transformation matrix
    # which transforms from image texture values
    # to voxel values, and scales said voxel
    # values to colour map texture coordinates.
    img2CmapXform = affine.concat(self.colourTexture.getCoordinateTransform(),
                                  self.imageTexture.voxValXform)

    shader.load()

    # disable clipimage/modalpha in 3D
    if self.threedee:
        imageIsClip = True
        imageIsMod = True
        modAlpha = False

    changed = False

    changed |= shader.set('useSpline', opts.interpolation == 'spline')
    changed |= shader.set('imageShape', imageShape)
    changed |= shader.set('texShape', texShape)
    changed |= shader.set('clipLow', clipLow)
    changed |= shader.set('clipHigh', clipHigh)
    changed |= shader.set('modScale', modScale)
    changed |= shader.set('modOffset', modOffset)
    changed |= shader.set('texZero', texZero)
    changed |= shader.set('invertClip', opts.invertClipping)
    changed |= shader.set('useNegCmap', opts.useNegativeCmap)
    changed |= shader.set('imageIsClip', imageIsClip)
    changed |= shader.set('imageIsMod', imageIsMod)
    changed |= shader.set('img2CmapXform', img2CmapXform)
    changed |= shader.set('clipImageShape', clipImageShape)
    changed |= shader.set('modImageShape', modImageShape)
    changed |= shader.set('modulateAlpha', modAlpha)
    changed |= shader.set('imageTexture', 0)
    changed |= shader.set('colourTexture', 1)
    changed |= shader.set('negColourTexture', 2)
    changed |= shader.set('clipTexture', 3)
    changed |= shader.set('modulateTexture', 4)

    if self.threedee:
        blendFactor = (1 - opts.blendFactor)**2
        clipPlanes = np.zeros((opts.numClipPlanes, 4), dtype=np.float32)
        d2tmat = opts.getTransform('display', 'texture')

        if opts.clipMode == 'intersection': clipMode = 1
        elif opts.clipMode == 'union': clipMode = 2
        elif opts.clipMode == 'complement': clipMode = 3
        else: clipMode = 0

        for i in range(opts.numClipPlanes):
            origin, normal = self.get3DClipPlane(i)
            origin = affine.transform(origin, d2tmat)
            normal = affine.transformNormal(normal, d2tmat)
            clipPlanes[i, :] = glroutines.planeEquation2(origin, normal)

        changed |= shader.set('numClipPlanes', opts.numClipPlanes)
        changed |= shader.set('clipMode', clipMode)
        changed |= shader.set('clipPlanes', clipPlanes, opts.numClipPlanes)
        changed |= shader.set('blendFactor', blendFactor)
        changed |= shader.set('stepLength', 1.0 / opts.getNumSteps())
        changed |= shader.set('alpha', display.alpha / 100.0)

    shader.unload()

    return changed