コード例 #1
0
def generateSphereImageStack(width,height,slices,center=vec3(0.5),scale=vec3(0.45),innerval=0.75,shellval=1.0):
    '''Create a sphere image on an otherwise blank image stack made with generateImageStack().'''
    dim=vec3(width,height,slices)
    center=center*dim
    images=generateImageStack(width,height,slices)

    for i,im in enumerate(images):
        imax=0.0

        for n,m in matIterate(im.img):
            p=(vec3(m,n,i)-center)/(scale*dim)
            val=0
            plen=p.len() # plen is twice the distance from image stack center to p

            if plen<0.9: # inner part of circle
                val=innerval
            elif plen<1.0: # circle edge
                val=shellval
            else:
                continue

            im.img.setAt(val,n,m)
            imax=max(imax,val)

        im.imgmin=0.0
        im.imgmax=imax

    return images
コード例 #2
0
ファイル: ImageAlgorithms.py プロジェクト: lijielife/Eidolon
def generateImageStack(width,
                       height,
                       slices,
                       timesteps=1,
                       pos=vec3(),
                       rot=rotator(),
                       spacing=vec3(1),
                       name='img'):
    '''Create a blank image stack with each timestep ordered bottom-up with integer timesteps.'''
    assert width > 0
    assert height > 0
    assert slices > 0
    assert spacing.x() > 0
    assert spacing.y() > 0
    assert timesteps > 0

    images = []
    positions = [
        pos + (rot * vec3(0, 0,
                          spacing.z() * s)) for s in range(slices)
    ]
    for t, s in trange(timesteps, slices):
        siname = '%s_%i_%i' % (name, s, t)
        si = SharedImage(siname, positions[s], rot, (width, height),
                         (spacing.x(), spacing.y()), t)
        si.allocateImg(siname + 'Img')
        si.img.fill(0)
        images.append(si)

    return images
コード例 #3
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
        def _generatecut(task):
            for rep, figs in repfigspairs:
                tslen = len(rep.getTimestepList())
                planept = planetrans.getTranslation()
                planerot = planetrans.getRotation()
                planenorm = planerot * vec3(0, 0, 1)

                assert tslen == len(figs)
                task.setMaxProgress(tslen)

                for i, tsrep in enumerate(rep.enumSubreprs()):
                    task.setProgress(i + 1)

                    snodes, sinds, scols = MeshAlgorithms.generateMeshPlanecut(
                        tsrep.dataset,
                        'slicemesh%i' % i,
                        planept,
                        planenorm,
                        self.linewidth,
                        nodecolors=tsrep.nodecolors)
                    vb = None
                    ib = None

                    if snodes:
                        # transform the isolines to orthographic camera space
                        snodes.sub(planept, 0, 0, snodes.n(), 1)
                        snodes.mul(planerot.inverse())
                        snodes.mul(vec3(1, 1), 0, 0, snodes.n(), 1)

                        vb = renderer.MatrixVertexBuffer(snodes, scols)
                        ib = renderer.MatrixIndexBuffer(sinds)

                    self.mgr.callThreadSafe(figs[i].fillData, vb, ib)

            self._repaintDelay()
コード例 #4
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
 def fillLineFig(self):
     '''Fills the figure representing the drawn line so that it is visible on screen while drawing.'''
     if self.drawingLine:
         orthot = self._planeToWorldTransform().inverse()
         vbuf = PyVertexBuffer([(orthot * self.lineStart) * vec3(1, 1),
                                (orthot * self.lineEnd) * vec3(1, 1)],
                               [vec3(0, 0, 1)] * 2, [self.drawcolor] * 2)
         ibuf = PyIndexBuffer([(0, 1)])
         self.lineFig.fillData(vbuf, ibuf)
コード例 #5
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
    def mouseDrag(self, e, dx, dy):
        h = self.getSelectedHandle()
        if h:
            h.mouseDrag(e, vec3(dx, dy))
        elif e.buttons() == Qt.LeftButton:
            self.scroll += vec3(dx, -dy, 0)
        elif e.buttons() == Qt.RightButton:
            self.zoom = max(0.01, self.zoom - dy * 0.01)

        self._repaintDelay()
コード例 #6
0
ファイル: ImageAlgorithms.py プロジェクト: lijielife/Eidolon
def calculateReprIsoplaneMesh(rep, planetrans, stackpos, slicewidth):
    '''
    Calculates the mesh for image representation object `rep' at plane `planetrans' and at the stack position `stackpos' 
    (which is meaningful only for ImageSeriesRepr types). This will determine where `rep' is intersected by `planetrans' 
    and returns the triple (nodes,indices,xis) representing a mesh for that intersecting geometry. 2D images viewed at
    oblique angles produces slices which are `slicewidth' wide. Returns a (nodes,indices,xis) triple where the nodes are
    in world space coordinates.
    '''
    assert isinstance(rep, (ImageSeriesRepr, ImageVolumeRepr))
    nodes, indices, xis = defaultImageQuad  # default plane values

    # get a plane point and norm from the transform
    planept = planetrans.getTranslation()
    planenorm = planetrans.getRotation() * vec3(0, 0, 1)

    # calculate the plane mesh by transforming the default quad by the repr's defined transform,
    if isinstance(rep, ImageSeriesRepr):
        trans = rep.getDefinedTransform(stackpos)
        nodes = [trans * v for v in nodes]
        reppt = trans.getTranslation()
        repnorm = trans.getRotation() * vec3(0, 0, 1)

        # if this is being viewed at an oblique angle, determine where the image plane intersects the view plane and represent that as a 2D textured line
        if not SceneUtils.equalPlanes(reppt, repnorm, planept, planenorm):
            et = MathDef.ElemType.Quad1NL
            heights = [n.planeDist(planept, planenorm) for n in nodes
                       ]  # determine distance from each node to the view plane
            xipair = first(
                SceneUtils.calculateQuadIsoline(heights, et, 0)
            )  # calculate the isoline of intersection between the image plane and view plane

            if xipair:  # if an isoline is found, calculate an isoline quad with a width of slicewidth
                xi1, xi2 = xipair
                pt1 = et.applyBasis(nodes, *xi1)
                pt2 = et.applyBasis(nodes, *xi2)
                nodes = SceneUtils.generateQuadFromLine(
                    pt1, pt2, planenorm, slicewidth)
                xis = [xi1, xi1, xi2, xi2]
            else:
                nodes = [
                ]  # otherwise given no nodes, meaning nothing to see here

    # calculate the plane mesh by slicing through the volume at the plane defined by `planetrans'
    elif isinstance(rep, ImageVolumeRepr):
        inters = rep.getPlaneIntersects(
            planept, planenorm,
            True)  # get the (node,xi) pairs for the intersecting polygon
        if len(inters):
            nodes, xis = list(
                zip(*inters))  # divide nodes and xis into separate lists
            indices = [(0, i + 1, i + 2)
                       for i in range(len(inters) - 2)]  # triangle fan indices

    return nodes, indices, xis
コード例 #7
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
 def fillContourFig(self):
     '''Fills the figure representing the drawn contour with lines so that it is visible on screen while drawing.'''
     if self.drawingContour:
         orthot = self._planeToWorldTransform().inverse()
         contour = self.contour[::10] if len(
             self.contour
         ) > 200 else self.contour  # simplify contour if a lot of points are present
         nodes = [(orthot * n) * vec3(1, 1)
                  for n in contour]  # convert contour to 2D coordinates
         num = len(nodes)
         vbuf = PyVertexBuffer(nodes, [vec3(0, 0, 1)] * num,
                               [self.drawcolor] * num)
         ibuf = PyIndexBuffer([(i, i + 1) for i in range(num - 1)])
         self.contourFig.fillData(vbuf, ibuf)
コード例 #8
0
def extendImage(obj,name,mx,my,mz,fillVal=0,numProcs=0,task=None):
    '''
    Extends the image `obj' by the given margins (mx,my,mz) in each dimension. The extended regions are filled with the
    value `fillVall'. The `numProcs' value is used in determining how many processes to use (0 for all). The returned
    object has name `name' and represents the same spatial data as the original plus the added margins.
    '''
    if mx==0 and my==0:
        images=[i.clone() for i in obj.images]
    else:
        obj.setShared(True)
        result=extendImageRange(len(obj.images),numProcs,task,obj.images,mx,my,fillVal,partitionArgs=(obj.images,))
        checkResultMap(result)
        images=sumResultMap(result)
        
    dz=obj.getVolumeTransform().getRotation()*(obj.getVoxelSize()*vec3(0,0,1))
    if mz>0:
        for stack in obj.getVolumeStacks():
            for i in range(1,mz+1):
                img=images[stack[0]].clone()
                img.img.fill(fillVal)
                img.position-=dz*i
                img.calculateDimensions()
                images.append(img)
                
                img=images[stack[-1]].clone()
                img.img.fill(fillVal)
                img.position+=dz*i
                img.calculateDimensions()
                images.append(img)
            
    return obj.plugin.createSceneObject(name,images,obj.source,obj.isTimeDependent)
コード例 #9
0
def generateTestImageStack(width,height,slices,timesteps=1,pos=vec3(),rot=rotator(),spacing=vec3(1)):
    '''Create a test pattern on an otherwise blank image stack made with generateImageStack().'''
    images=generateImageStack(width,height,slices,timesteps,pos,rot,spacing)
    
    for ind,im in enumerate(images):
            t=int(float(ind)/slices)
            i=ind%slices
            imax=0.0

            for n,m in trange(im.img.n(),im.img.m()):
                val=0.0
                
                if n==t and m==t:
                    val=4.0
                elif n==(i+1) or m==(i+1):
                    val=0.5
                elif n<20 and m<20:
                    val=3.0
                elif m<15 and i<15:
                    val=2.0
                elif n<10 and i<10:
                    val=1.0

                im.img.setAt(val,n,m)
                imax=max(imax,val)

            im.imgmin=0.0
            im.imgmax=imax

    return images
コード例 #10
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
    def addPoint(self, name, text, col):
        '''
        Add a positionable point called `name' with description `text' and color `col'. This will produce a quadruple
        (handle,label,button,edit) containing the PointHandle2D, QLable, QPushButton, and QLineEdit used to represent
        this point, storing it in self.pointMap keyed to name as well as returning it.
        '''
        handle = SceneComponents.PointHandle2D(self, vec3(), col)
        label = QtWidgets.QLabel(text)
        button = QtWidgets.QPushButton('Set Plane')
        edit = QtWidgets.QLineEdit()

        self.gridLayout.addWidget(label, len(self.pointMap), 0)
        self.gridLayout.addWidget(button, len(self.pointMap), 1)
        self.gridLayout.addWidget(edit, len(self.pointMap), 2)

        label.setStyleSheet('background-color: %s;' %
                            str(toQtColor(col).name()))
        edit.setReadOnly(True)

        def _setfunc():
            handle.setNode(0, self.getWorldPosition(0.5, 0.5, False))
            self._repaintDelay()

        button.clicked.connect(_setfunc)

        handle.setVisible(True)
        handle.setVisible3D(False)

        self.addHandle(handle)
        self.pointMap[name] = (handle, label, button, edit)
        return (handle, label, button, edit)
コード例 #11
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
 def getBBTransform(self):
     '''
     Returns the boundbox transform which adjusts the figures to fit inside the selected viewing area based on the 
     scene bound box, scroll, and zoom parameters.
     '''
     bbw, bbh, bbd = self.sceneBB.getDimensions()
     bscale = self.getBoxFitScale(bbw, bbh)
     return transform(self.scroll - self.sceneBB.center * bscale,
                      vec3(self.zoom * bscale, self.zoom * bscale))
コード例 #12
0
ファイル: SceneObject.py プロジェクト: liu3xing3long/Eidolon
 def setParam(self,name,value):
     assert self.isInScene(), 'Cannot set parameters until representation is added to the scene'
     for fig in self.figs:
         if name=='width':
             fig.setDimension(value,fig.getHeight())
         elif name=='height':
             fig.setDimension(fig.getWidth(),value)
         elif name=='glyphscale':
             fig.setGlyphScale(vec3(*toIterable(value)))
         elif name=='glyphname':
             fig.setGlyphName(value)
コード例 #13
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
    def mousePress(self, e):
        # check for handle selection
        if e.buttons() == Qt.LeftButton:
            self.selectHandleScreenCoord(e.x(), e.y())
        else:
            self.deselectHandles()

            # reset view
            if e.buttons() == Qt.MiddleButton:
                self.scroll = vec3()
                self.zoom = 1.0
                self._repaintDelay()
コード例 #14
0
def loadImageFile(filename,imgobj,pos=vec3(),rot=rotator(),spacing=(1.0,1.0)):
    '''Returns a SharedImage object containing the image data from `imgobj' which must have a fillRealMatrix(mat) method.'''
    #imgobj=imgobj or Image.loadImageFile(filename)

    w=imgobj.getWidth()
    h=imgobj.getHeight()

    si=SharedImage(filename,pos,rot,(w,h),spacing)
    si.allocateImg(os.path.split(filename)[1])
    imgobj.fillRealMatrix(si.img)
    si.readMinMaxValues()

    return si
コード例 #15
0
ファイル: SceneObject.py プロジェクト: liu3xing3long/Eidolon
    def __init__(self,parent,reprtype,reprcount,refine,reprdata,parentdataset=None,drawInternal=False,externalOnly=False,matname='Default',aabb=None,**kwargs):
        SceneObjectRepr.__init__(self,parent,reprtype,reprcount,matname)
        self.refine=refine
        self.drawInternal=drawInternal
        self.externalOnly=externalOnly
        self.figs=[] #list of figures, one for each index set

        self.kwargs=dict(kwargs)

        self.position=vec3()
        self.scale=vec3(1,1,1)
        self.rotation=(0.0,0.0,0.0)

        self.datafuncs={} # map from names to functional expressions for tranforming data to material information
        self.datafuncs['valfunc']=MeshAlgorithms.ValueFunc._Average # data-to-unitvalue function
        self.datafuncs['alphafunc']=MeshAlgorithms.UnitFunc._One # unitvalue-to-unitvalue alpha function

        self.dataset,self.origindices=reprdata
        self.parentdataset=parentdataset
        self.nodes=self.dataset.getNodes()
        self.lines=self.dataset.getIndexSet(self.dataset.getName()+MatrixType.lines[1])
        self.tris=self.dataset.getIndexSet(self.dataset.getName()+MatrixType.tris[1])
        self.nodeprops=self.dataset.getIndexSet(self.dataset.getName()+MatrixType.props[1])
        self.extinds=self.dataset.getIndexSet(self.dataset.getName()+MatrixType.extinds[1])

        self.datafield=None
        #self.minfield=None
        #self.maxfield=None
        self.selminfield=None
        self.selmaxfield=None

        self.aabb=aabb

        self.bufferGen=ModifierBufferGenerator()

        self.nodecolors=ColorMatrix('Colors',self.nodes.n())
        self.nodecolors.fill(color(1.0,1.0,1.0,1.0))
コード例 #16
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
    def setPlaneIndicator(self, obj, planetrans):
        '''Set the plane indicator in the 3D view for object `obj' to be at transform `planetrans'.'''
        if isinstance(obj.parent, ImageSceneObject) and obj.parent.is2D:
            self.indicatorPlane.setVisible(False)
            self.indicatorTrans = transform()
        else:
            bb = obj.getAABB()
            trans = transform(planetrans.getTranslation(),
                              planetrans.getScale() * vec3(bb.radius * 1.5),
                              planetrans.getRotation())

            # needed to prevent update loops with _repaintDelay and _repaint3DDelay
            if trans != self.indicatorTrans or self.indicatorPlane.isVisible(
            ) != self.indicatorVisible:
                self.indicatorTrans = trans
                self.indicatorPlane.setTransform(trans)
                self.indicatorPlane.setVisible(self.indicatorVisible)
                self._repaint3DDelay()
コード例 #17
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
    def selectHandleScreenCoord(self, x, y):
        '''Select a handle based on the screen coordinate (`x',`y'). Returns the handle is selected, None otherwise.'''
        selected = None
        pos = vec3(x, y)
        for h in self.handles:
            if h.checkSelected(
                    pos):  # call this to set each handle's selected property
                selected = selected or h

        # if a handle was selected, call the notice method and set all other handles to be inactive
        if selected:
            self.handleSelected(selected)
            for h in self.handles:
                h.setActive(h == selected)

            self._repaintDelay()

        return selected
コード例 #18
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
    def drawMouseDrag(self, e, dx, dy):
        '''
        Add points to the contour when the mouse is dragged with position delta (dx,dy). This will interpolate over
        large distances to (mostly) ensure a uniform distribution of points along the drawn contour. Returns True
        if the contour was drawn, False otherwise.
        '''
        if not self.drawingContour:
            return False

        p = self.getWorldPosition(e.x(), e.y())
        dlen = int(vec3(dx, dy).len())

        if dlen:  # if the mouse was moved relatively fast, add uniformly-spaced points along the drag path
            self.contour += [
                Utils.lerp(i / dlen, self.contour[-1], p)
                for i in Utils.frange(dlen)
            ]
        else:
            self.contour.append(p)

        return True
コード例 #19
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
    def _imagePlaneMesh(self, rep, planetrans, stackpos, centerInView=False):
        '''
        Returns the (nodes,indices,xis) mesh for the isoplane mesh defining the slice of of image representation `rep' 
        at stack position `stackpos' or plane position `planetrans'. The nodes are in screen ortho coordinate space, 
        adjusted to be within the viewing bound box if `centerInView' is True. 
        '''
        # get the mesh for the plane figure in world space coordinates
        nodes, indices, xis = ImageAlgorithms.calculateReprIsoplaneMesh(
            rep, planetrans, stackpos, self.slicewidth)

        # transform nodes into camera space coordinates
        invtrans = planetrans.inverse()
        invtrans.setScale(vec3(1, 1))
        nodes = [invtrans * v for v in nodes]

        # transform the nodes to fit within the current viewing bound box
        if centerInView:
            bbt = self.getBBTransform()
            nodes = [bbt * n for n in nodes]

        return nodes, indices, xis
コード例 #20
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
    def getImagePosition(self, x, y):
        pos = self.getScreenOrthoPosition(x, y)
        rep = self.mgr.findObject(self.sourceName)

        imgstackpos = self.getImageStackPosition()
        trans = rep.getDefinedTransform(imgstackpos)
        invtrans = self.viewplane.inverse()
        invtrans.setScale(vec3(1, 1))

        screennodes, indices, xis = self._imagePlaneMesh(
            rep, self.viewplane, imgstackpos, True)

        for tri in indices:
            nodes = [screennodes[i] for i in tri]
            trixis = [xis[i] for i in tri]
            nodes += [nodes[0] + nodes[0].planeNorm(nodes[1], nodes[2])]

            xi = pointSearchLinTet(pos, *nodes)

            if xi.isInUnitCube() and sum(xi) <= 1.0:
                imgxi = ElemType.Tri1NL.applyBasis(trixis, xi.x(), xi.y(), 0)
                return imgxi * trans.getScale().abs(), imgstackpos

        return None, imgstackpos
コード例 #21
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
 def getOrthoPosition(self, pos):
     '''
     Returns the orthographic camera coordinate of the world vector `pos'. In orthographic coordinates, the screen
     center is (0,0) and bottom-right is (1,1).
     '''
     return (self._planeToWorldTransform() / pos) * vec3(1, 1)
コード例 #22
0
# Hounsfield value range
Hounsfield=Utils.enum(
    ('air',-1000),
    ('water',0),
    ('muscle',10,40),
    ('blood',40),
    ('bone',1000),
    ('min',-1000),
    ('max',4000), # 4095?
    doc='Known Hounsfield unit values for various tissues, 1 or 2 values for a minimum and optional maximum range'
)


# coordinates, indices, and xi values for a 2-triangle square on the XY plane centered on the origin
defaultImageQuad=(
    (vec3(-0.5,0.5), vec3(0.5,0.5), vec3(-0.5,-0.5), vec3(0.5,-0.5)), # vertices
    ((0, 2, 1), (1, 2, 3)), # triangle indices
    (vec3(0,0), vec3(1,0), vec3(0,1), vec3(1,1)) # xi values
)


def hounsfieldToUnit(h):
    '''Converts a Hounsfield unit value `h' to a unit value, 0 if `h' is the minimal Hounsfield value to 1 if maximal.'''
    return clamp(lerpXi(h,Hounsfield.min,Hounsfield.max),0.0,1.0)


@timing
def checkNan(obj):
    '''Assert that all values in the image object `obj' are not NaN.'''
    for i,img in enumerate(obj.images):
        for n,m in matIterate(img.img):
コード例 #23
0
def initDefaultAssets(mgr):
    '''Initializes scene colors and lights, loads GPU scripts, and creates materials.'''

    # set the scene's default camera to a single camera, ambient light, and background color to match the UI color scheme
    mgr.setSingleFreeCamera()
    mgr.setAmbientLight(color(0.5, 0.5, 0.5))
    mgr.setBackgroundColor(color(0.3515625, 0.3515625, 0.3515625))

    if mgr.conf.get(platformID, ConfVars.camerazlock).lower() == 'false':
        mgr.setCameraZLocked(False)

    # create a default directional light that follows the camera
    cl = mgr.createLight(LightType._cdir, 'Camera Light')
    cl.setColor(color(0.25, 0.25, 0.25))

    res = mgr.conf.get(platformID, ConfVars.resdir)

    mgr.scene.addResourceDir(res)
    mgr.scene.initializeResources()

    # load shaders/fragment programs
    for s in mgr.conf.get('Shaders', ConfVars.shaders).split(','):
        spec = mgr.conf.get('Shaders', s).split(',')
        ptype = PT_FRAGMENT if spec[0] == 'fragment' else PT_VERTEX
        profiles = spec[1] if len(spec) > 1 else None
        mgr.loadGPUScriptFile(res + s,
                              ptype,
                              profiles=profiles,
                              ignoreError=True)

    # create spectrums

    s2 = mgr.createSpectrum('BW')
    s2.setSpectrumData([color(0, 0, 0), color()])

    s3 = mgr.createSpectrum('BWAlpha')
    s3.setSpectrumData([color(0, 0, 0), color()], [0, 1],
                       [vec3(0, 0), vec3(1, 1)])

    s1 = mgr.createSpectrum('Rainbow')
    s1.setSpectrumData([
        color(0.0, 0.0, 1.0),
        color(0.0, 1.0, 1.0),
        color(0.0, 1.0, 0.0),
        color(1.0, 1.0, 0.0),
        color(1.0, 0.0, 0.0)
    ])

    s4 = mgr.createSpectrum('RedBlue')
    s4.setSpectrumData([color(1.0, 0.0, 0.0), color(0.0, 0.0, 1.0)])

    s5 = mgr.createSpectrum('BlackGreenWhite')
    s5.setSpectrumData(
        [color(0.0, 0.0, 0.0),
         color(0, 1.0, 0),
         color(1.0, 1.0, 1.0)])

    s6 = mgr.createSpectrum('BlueWhiteRed')
    s6.setSpectrumData(
        [color(0.0, 0.0, 1.0),
         color(1.0, 1.0, 1.0),
         color(1.0, 0.0, 0.0)])

    s7 = mgr.createSpectrum('EMSpectrum')
    s7.setSpectrumData([
        color(0.6, 0.0, 0.6),
        color(0.0, 1.0, 1.0),
        color(0.0, 1.0, 0.0),
        color(1.0, 1.0, 0.0),
        color(1.0, 0.0, 0.0),
        color(0.3, 0.0, 0.0)
    ])

    ctspec = [
        color(0, 0, 0),
        color(1, 0, 0),
        color(0.85, 0.85, 0.5),
        color(1, 1, 1)
    ]
    ctspecpos = [
        hounsfieldToUnit(-150),
        hounsfieldToUnit(200),
        hounsfieldToUnit(400),
        hounsfieldToUnit(550)
    ]
    ctalpha = [
        vec3(hounsfieldToUnit(-425), 0),
        vec3(hounsfieldToUnit(300), 0.25),
        vec3(hounsfieldToUnit(550), 1.0),
        vec3(hounsfieldToUnit(1050), 1.0),
        vec3(hounsfieldToUnit(2000), 0.0)
    ]

    s8 = mgr.createSpectrum('CardiacCT')
    s8.setSpectrumData(ctspec, ctspecpos, ctalpha)

    s9 = mgr.createSpectrum('CardiacCT2D')
    s9.setSpectrumData(
        ctspec, ctspecpos,
        [vec3(hounsfieldToUnit(-150), 0),
         vec3(hounsfieldToUnit(-100), 1.0)])

    s10 = mgr.createSpectrum('Thermal')
    s10.setSpectrumData([
        color(0.105, 0.047, 0.253),
        color(0.291, 0.046, 0.419),
        color(0.472, 0.111, 0.428),
        color(0.646, 0.174, 0.378),
        color(0.807, 0.263, 0.279),
        color(0.930, 0.411, 0.145),
        color(0.985, 0.601, 0.024),
        color(0.971, 0.813, 0.228),
        color(0.988, 0.998, 0.645)
    ])

    # create materials

    m = mgr.createMaterial('Default')

    m = mgr.createMaterial('BlueWhiteRed')
    m.copySpectrumFrom(s6)

    m = mgr.createMaterial('Rainbow')
    m.copySpectrumFrom(s1)

    # prime color materials
    m = mgr.createMaterial('Red')
    m.setDiffuse(color(1, 0, 0))
    m = mgr.createMaterial('Green')
    m.setDiffuse(color(0, 1, 0))
    m = mgr.createMaterial('Blue')
    m.setDiffuse(color(0, 0, 1))
    m = mgr.createMaterial('Yellow')
    m.setDiffuse(color(1, 1, 0))
    m = mgr.createMaterial('Magenta')
    m.setDiffuse(color(1, 0, 1))
    m = mgr.createMaterial('Cyan')
    m.setDiffuse(color(0, 1, 1))

    m = mgr.createMaterial('BaseImage')
    m.useVertexColor(False)
    m.setGPUProgram('BaseImage', PT_FRAGMENT)
    m.copySpectrumFrom(s3)

    m = mgr.createMaterial('BaseImage2D')
    m.useVertexColor(False)
    m.setGPUProgram('BaseImage', PT_FRAGMENT)
    m.copySpectrumFrom(s2)

    m = mgr.createMaterial('BoundBoxes')
    m.useLighting(False)

    m = mgr.createMaterial('CtrlNode')
    m.useVertexColor(False)
    m.setPointSizeAbs(5.0)
    m.useLighting(False)
    m.setDiffuse(color(0.75, 0, 0))

    m = mgr.createMaterial('Handle')
    m.useLighting(False)
    m.useDepthCheck(False)

    m = mgr.createMaterial('Contour')
    m.useLighting(False)
コード例 #24
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
    def calculateViewPlane(self, reptrans, planename=None, imgxi=0):
        '''
        Return the transform object representing the plane in space named by `planename' or by `reptrans' alone if
        this isn't provided. If `planename' names a representation object then its transform is used to define the
        plane, specifically if its a ImageSceneObjectRepr then getDefinedTransform() is called to get this transform.
        If `planename' is one of the standard plane names (XY, YZ, or XZ) then the transform is defined to represent
        this plane at image xi value `imgxi' in the direction normal to the plane (ie. this is Z axis xi value for
        plane XY). The `reptrans' transform represents the transformation from xi space to world of the object the
        plane bisects, thus if the object is a volume this is the transformation from texture coordinates to world
        coordinates. This used to transform the standard plane definitions to world coordinates, and to define the
        resulting transform if `planename' names neither a representation object nor a standard plane. The return
        value is a transform in world space with a (1,1,1) scale component.
        '''
        planerep = self.mgr.findObject(planename)

        if planerep != None:
            if isinstance(planerep, ImageSceneObjectRepr):
                planetrans = planerep.getDefinedTransform()
                planetrans.setScale(vec3(1))
            else:
                planept = planerep.getPosition(True)
                planerot = rotator(*planerep.getRotation(True))
                planetrans = transform(
                    planept - (planerot * vec3(0, 0, self.planeShift)),
                    vec3(1), planerot)
        elif self.isStdPlaneName(planename):
            xi = clamp(imgxi, epsilon * 100, 1.0 - epsilon * 100)

            # choose xi values in the volume representing the plane's center, normal, and right-hand direction
            if planename == 'XY':
                xipos = vec3(0.5, 0.5, xi)
                xinorm = xipos + vec3(0, 0, 1)
                xiright = xipos + vec3(1, 0, 0)
            elif planename == 'YZ':
                xipos = vec3(xi, 0.5, 0.5)
                xinorm = xipos + vec3(1, 0, 0)
                xiright = xipos + vec3(0, 1, 0)
            else:  # XZ
                xipos = vec3(0.5, xi, 0.5)
                xinorm = xipos + vec3(0, 1, 0)
                xiright = xipos + vec3(1, 0, 0)

            # calculate a world position and rotation by applying the transform to the xi values
            planept = reptrans * xipos
            planerot = rotator((reptrans * xiright) - planept,
                               (reptrans * xinorm) - planept, vec3(1, 0, 0),
                               vec3(0, 0, 1))
            planetrans = transform(planept, vec3(1), planerot)
        else:
            planetrans = transform(reptrans.getTranslation(), vec3(1),
                                   reptrans.getRotation())

        return planetrans
コード例 #25
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
    def __init__(self,
                 mgr,
                 camera,
                 indicatorCol=color(1, 1, 1, 0.25),
                 parent=None):
        Base2DWidget.__init__(self, parent)
        self.mgr = mgr
        self.camera = camera
        self.camera.setOrtho(True)
        self.camera.setSecondaryCamera(True)

        self.camera.setPosition(vec3(0, 0, 0))
        self.camera.setLookAt(vec3(0, 0, -1))
        self.camera.setNearClip(epsilon * 100)
        self.camera.setFarClip(100.0)  # ensure the skybox is clipped out

        self.sourceName = None
        self.planeName = None

        self.scroll = vec3()
        self.zoom = 1.0
        self.viewplane = transform(
        )  # the plane in world space corresponding to the current 2D view

        self.objFigMap = {
        }  # maps representation names to (planetrans,figs) pairs

        self.handles = []  # list of handles in this view

        self.slicewidth = 0.2  # width in world units of 2D slices
        self.linewidth = 1.0  # width in world units of mesh lines
        self.planeShift = 0.00005  # amount to move slice position down by in world units so that slice planes don't cut out the image where we want to see it

        self.sceneBB = BoundBox(BaseCamera2DWidget.defaultQuad[0])

        self.indicatorCol = indicatorCol
        self.indicatorMaterial = self.mgr.scene.createMaterial(
            'IndicatorPlane')
        self.indicatorMaterial.setDiffuse(color(1, 1, 1, 1))
        self.indicatorMaterial.useLighting(False)

        self.indicatorTrans = transform()
        self.indicatorPlane = self.createFigure('PlaneIndicator%i' % id(self),
                                                FT_TRILIST, False)
        self.indicatorPlane.setMaterial(self.indicatorMaterial)
        self.indicatorPlane.setTransparent(True)
        self.indicatorPlane.setVisible(False)

        self.indicatorVisible = True

        # construct a quad with a cylinder rim for the indicator plane
        q = BaseCamera2DWidget.defaultQuad[0]
        mq = (q[0] + q[1]) * 0.5
        cnodes, cinds = SceneUtils.generateCylinder(
            [mq, q[1], q[3], q[2], q[0], mq], [0.0025] * 6, 1, 4, False)
        nodes = list(BaseCamera2DWidget.defaultQuad[0]) + cnodes
        inds = list(BaseCamera2DWidget.defaultQuad[1]) + cinds
        self.indicatorPlane.fillData(
            PyVertexBuffer(nodes, [vec3(0, 0, 1)] * len(nodes),
                           [indicatorCol] * len(nodes)), PyIndexBuffer(inds),
            False, True)

        delayedMethodWeak(
            self, '_repaintDelay'
        )  #delay method for repainting allows safe calling multiple times and from task threads

        mgr.addEventHandler(EventType._widgetPreDraw, self._repaintDelay)
コード例 #26
0
ファイル: Camera2DView.py プロジェクト: liu3xing3long/Eidolon
class BaseCamera2DWidget(Base2DWidget):
    '''
    This is the base class for all 2D drawing widgets using a camera from the renderer. It handles the update cycle of
    the camera rendering to a stream which is then fed into the image object for the widget. It provides createFigure()
    which will create Figure objects only visible to the internal camera. This class has no UI components and relies on
    inheriting subtypes calling modifyDrawWidget() to perform the correct association between the widget to draw into
    and the fillImage() method which updates the camera and copies its data over.
    
    The UI notion of this widget is to view a single primary image representation object in 2D, which can be dragged and
    zoomed. An image stack value allows scrolling in the through-plane direction if supported, this represents moving in
    the through-plane direction of an image volume or between planes of an image stack. Secondary images and meshes can 
    be rendered over top of this image, these are sliced at the plane in 3D space which 2D view is currently "viewing".

    The following methods return default values and must be overridden in a subtype for this class to function:
    getImageStackPosition(), getImageStackMax(), getSecondaryNames().
    '''

    defaultQuad = (
        (vec3(-0.5, 0.5), vec3(0.5, 0.5), vec3(-0.5,
                                               -0.5), vec3(0.5,
                                                           -0.5)),  # vertices
        ((0, 2, 1), (1, 2, 3)),  # triangle indices
        (vec3(0, 0), vec3(1, 0), vec3(0, 1), vec3(1, 1))  # xi values
    )

    standardPlanes = ('XY', 'XZ', 'YZ')

    def __init__(self,
                 mgr,
                 camera,
                 indicatorCol=color(1, 1, 1, 0.25),
                 parent=None):
        Base2DWidget.__init__(self, parent)
        self.mgr = mgr
        self.camera = camera
        self.camera.setOrtho(True)
        self.camera.setSecondaryCamera(True)

        self.camera.setPosition(vec3(0, 0, 0))
        self.camera.setLookAt(vec3(0, 0, -1))
        self.camera.setNearClip(epsilon * 100)
        self.camera.setFarClip(100.0)  # ensure the skybox is clipped out

        self.sourceName = None
        self.planeName = None

        self.scroll = vec3()
        self.zoom = 1.0
        self.viewplane = transform(
        )  # the plane in world space corresponding to the current 2D view

        self.objFigMap = {
        }  # maps representation names to (planetrans,figs) pairs

        self.handles = []  # list of handles in this view

        self.slicewidth = 0.2  # width in world units of 2D slices
        self.linewidth = 1.0  # width in world units of mesh lines
        self.planeShift = 0.00005  # amount to move slice position down by in world units so that slice planes don't cut out the image where we want to see it

        self.sceneBB = BoundBox(BaseCamera2DWidget.defaultQuad[0])

        self.indicatorCol = indicatorCol
        self.indicatorMaterial = self.mgr.scene.createMaterial(
            'IndicatorPlane')
        self.indicatorMaterial.setDiffuse(color(1, 1, 1, 1))
        self.indicatorMaterial.useLighting(False)

        self.indicatorTrans = transform()
        self.indicatorPlane = self.createFigure('PlaneIndicator%i' % id(self),
                                                FT_TRILIST, False)
        self.indicatorPlane.setMaterial(self.indicatorMaterial)
        self.indicatorPlane.setTransparent(True)
        self.indicatorPlane.setVisible(False)

        self.indicatorVisible = True

        # construct a quad with a cylinder rim for the indicator plane
        q = BaseCamera2DWidget.defaultQuad[0]
        mq = (q[0] + q[1]) * 0.5
        cnodes, cinds = SceneUtils.generateCylinder(
            [mq, q[1], q[3], q[2], q[0], mq], [0.0025] * 6, 1, 4, False)
        nodes = list(BaseCamera2DWidget.defaultQuad[0]) + cnodes
        inds = list(BaseCamera2DWidget.defaultQuad[1]) + cinds
        self.indicatorPlane.fillData(
            PyVertexBuffer(nodes, [vec3(0, 0, 1)] * len(nodes),
                           [indicatorCol] * len(nodes)), PyIndexBuffer(inds),
            False, True)

        delayedMethodWeak(
            self, '_repaintDelay'
        )  #delay method for repainting allows safe calling multiple times and from task threads

        mgr.addEventHandler(EventType._widgetPreDraw, self._repaintDelay)

    def _repaintDelay(self):
        self.mgr.callThreadSafe(self.repaint)

    @delayedcall(
        0.5
    )  # only need/want 1 delay thread for repainting the 3D scene, don't want to do this often
    def _repaint3DDelay(self):
        self.mgr.callThreadSafe(self.mgr.repaint)

    def getImageStackPosition(self):
        '''Get the index in the image stack of the source object. This must be overridden to set the stack position.'''
        return 0

    def getImageStackMax(self):
        '''Get the maximum stack index. This must be overridden to define the max value as something other than 0.'''
        return 0

    def getSecondaryNames(self):
        '''Get the names of secondary viewable objects. This must be overridden to return something other than [].'''
        return []

    def getObjectNames(self):
        return [self.sourceName] + list(self.getSecondaryNames())

    def getImageXiPosition(self):
        '''Get the xi value on the unit interval representing Z position within the stack the current view represents.'''
        maxv = self.getImageStackMax()
        return 0 if maxv == 0 else self.getImageStackPosition() / float(maxv)

    def mousePress(self, e):
        # check for handle selection
        if e.buttons() == Qt.LeftButton:
            self.selectHandleScreenCoord(e.x(), e.y())
        else:
            self.deselectHandles()

            # reset view
            if e.buttons() == Qt.MiddleButton:
                self.scroll = vec3()
                self.zoom = 1.0
                self._repaintDelay()

    def mouseDrag(self, e, dx, dy):
        h = self.getSelectedHandle()
        if h:
            h.mouseDrag(e, vec3(dx, dy))
        elif e.buttons() == Qt.LeftButton:
            self.scroll += vec3(dx, -dy, 0)
        elif e.buttons() == Qt.RightButton:
            self.zoom = max(0.01, self.zoom - dy * 0.01)

        self._repaintDelay()

    def mouseRelease(self, e):
        self.deselectHandles()

    def parentClosed(self, e):
        self.removeHandles()
        self.mgr.removeEventHandler(self._repaintDelay)
        self.mgr.removeCamera(self.camera)
        self.camera = None
        self.indicatorPlane.setVisible(False)

    def fillImage(self, img):
        assert isMainThread()
        w, h = self.getDrawDims()
        if w > 0 and h > 0:
            self.camera.setAspectRatio(float(w) / h)
            self.camera.renderToStream(img.bits(), w, h, renderer.TF_ARGB32)

    def isStdPlaneName(self, name):
        '''Returns True if `name' is the name of a standard plane (ie. XY, YZ, XZ).'''
        return name in BaseCamera2DWidget.standardPlanes

    def calculateViewPlane(self, reptrans, planename=None, imgxi=0):
        '''
        Return the transform object representing the plane in space named by `planename' or by `reptrans' alone if
        this isn't provided. If `planename' names a representation object then its transform is used to define the
        plane, specifically if its a ImageSceneObjectRepr then getDefinedTransform() is called to get this transform.
        If `planename' is one of the standard plane names (XY, YZ, or XZ) then the transform is defined to represent
        this plane at image xi value `imgxi' in the direction normal to the plane (ie. this is Z axis xi value for
        plane XY). The `reptrans' transform represents the transformation from xi space to world of the object the
        plane bisects, thus if the object is a volume this is the transformation from texture coordinates to world
        coordinates. This used to transform the standard plane definitions to world coordinates, and to define the
        resulting transform if `planename' names neither a representation object nor a standard plane. The return
        value is a transform in world space with a (1,1,1) scale component.
        '''
        planerep = self.mgr.findObject(planename)

        if planerep != None:
            if isinstance(planerep, ImageSceneObjectRepr):
                planetrans = planerep.getDefinedTransform()
                planetrans.setScale(vec3(1))
            else:
                planept = planerep.getPosition(True)
                planerot = rotator(*planerep.getRotation(True))
                planetrans = transform(
                    planept - (planerot * vec3(0, 0, self.planeShift)),
                    vec3(1), planerot)
        elif self.isStdPlaneName(planename):
            xi = clamp(imgxi, epsilon * 100, 1.0 - epsilon * 100)

            # choose xi values in the volume representing the plane's center, normal, and right-hand direction
            if planename == 'XY':
                xipos = vec3(0.5, 0.5, xi)
                xinorm = xipos + vec3(0, 0, 1)
                xiright = xipos + vec3(1, 0, 0)
            elif planename == 'YZ':
                xipos = vec3(xi, 0.5, 0.5)
                xinorm = xipos + vec3(1, 0, 0)
                xiright = xipos + vec3(0, 1, 0)
            else:  # XZ
                xipos = vec3(0.5, xi, 0.5)
                xinorm = xipos + vec3(0, 1, 0)
                xiright = xipos + vec3(1, 0, 0)

            # calculate a world position and rotation by applying the transform to the xi values
            planept = reptrans * xipos
            planerot = rotator((reptrans * xiright) - planept,
                               (reptrans * xinorm) - planept, vec3(1, 0, 0),
                               vec3(0, 0, 1))
            planetrans = transform(planept, vec3(1), planerot)
        else:
            planetrans = transform(reptrans.getTranslation(), vec3(1),
                                   reptrans.getRotation())

        return planetrans

    def createFigure(self, name, ftype=FT_TRILIST, is2DOnly=True):
        '''
        Helper method for creating a figure with name `name' and type `ftype'. If `is2DOnly' is True then the returned
        figure is visible to this widget's camera only, otherwise it's default behaviour is unchanged.
        '''
        assert isMainThread()
        fig = self.mgr.scene.createFigure(name, '', ftype)
        fig.fillData(PyVertexBuffer([]), PyIndexBuffer([]))
        fig.setVisible(True)

        if is2DOnly:  # visible to this widget's 2D camera only
            fig.setCameraVisibility(None, False)
            fig.setCameraVisibility(self.camera, True)

        return fig

    def createMaterial(self, name, useLighting=False, useVertexColor=True):
        '''Helper method for creating a blank material which bypasses the UI.'''
        mat = self.mgr.scene.createMaterial(name)
        mat.useLighting(useLighting)
        mat.useVertexColor(useVertexColor)
        return mat

    def createTexture(self, name, width, height, format):
        '''Helper method for creating a texture which bypasses the UI.'''
        return self.mgr.scene.createTexture(name, width, height, 0, format)

    def addHandle(self, handle):
        '''Add the handle to the view and to self.handles.'''
        handle.addToScene(self.mgr, self.mgr.scene)
        self.handles.append(handle)
        self._repaintDelay()

    def removeHandle(self, index):
        '''Remove the handle at position `index' in self.handles to the view.'''
        h = self.handles.pop(index)
        h.removeFromScene(self.mgr, self.mgr.scene)
        self._repaintDelay()

    def removeHandles(self):
        '''Remove all handles from the view.'''
        while len(self.handles) > 0:
            self.removeHandle(0)

        self.handles = []
        self._repaintDelay()

    def getHandle(self, index):
        '''Return handle at position `index' in the list of handles.'''
        return self.handles[index]

    def getSelectedHandle(self):
        '''Returns the selected handle, None otherwise.'''
        return first(h for h in self.handles if h.isSelected())

    def selectHandleScreenCoord(self, x, y):
        '''Select a handle based on the screen coordinate (`x',`y'). Returns the handle is selected, None otherwise.'''
        selected = None
        pos = vec3(x, y)
        for h in self.handles:
            if h.checkSelected(
                    pos):  # call this to set each handle's selected property
                selected = selected or h

        # if a handle was selected, call the notice method and set all other handles to be inactive
        if selected:
            self.handleSelected(selected)
            for h in self.handles:
                h.setActive(h == selected)

            self._repaintDelay()

        return selected

    def deselectHandles(self):
        '''Deselect all handles.'''
        for h in self.handles:
            h.setSelected(False)

    def handleSelected(self, handle):
        '''
        Called when the given handle object is selected by mouse click. If this is not overridden, handles are never
        activated but the view will otherwise function correctly. This method should be overridden to make a selected
        handle active.
        '''
        pass

    def getObjFigures(self, name, numfigs=1, ftype=FT_TRILIST):
        '''Get the Figure objects for the object `name', or create `numfigs' objects of type `ftype' if none found.'''
        if name not in self.objFigMap:
            self.objFigMap[name] = (transform(), [])

        trans, figs = self.objFigMap[name]
        lfigs = len(figs)
        if lfigs < numfigs:
            for i in range(numfigs - lfigs):
                figs.append(
                    self.createFigure('%s_2DFig%i' % (name, i + lfigs), ftype))

        for i, f in enumerate(figs):
            f.setVisible(i < numfigs)

        return trans, figs

    def retainObjFigures(self, names):
        '''Keep only the figures for those objects named in the iterable `names.'''
        notfound = set(self.objFigMap.keys()).difference(set(names))
        for nf in notfound:
            figs = self.objFigMap.pop(nf)[1]
            for f in figs:
                f.setVisible(False)

    def setObjPlane(self, name, planetrans):
        '''Set the view plane for the object named by `name' in self.objFigMap, retaining the figure list.'''
        self.objFigMap[name] = (planetrans, self.objFigMap[name][1])

    def setFigsVisible(self, name, vis):
        '''Set the visibility of the figures to `vis' for object named by `name'.'''
        if name in self.objFigMap:
            for fig in self.objFigMap[name][1]:
                fig.setVisible(vis)

    def setPlaneIndicator(self, obj, planetrans):
        '''Set the plane indicator in the 3D view for object `obj' to be at transform `planetrans'.'''
        if isinstance(obj.parent, ImageSceneObject) and obj.parent.is2D:
            self.indicatorPlane.setVisible(False)
            self.indicatorTrans = transform()
        else:
            bb = obj.getAABB()
            trans = transform(planetrans.getTranslation(),
                              planetrans.getScale() * vec3(bb.radius * 1.5),
                              planetrans.getRotation())

            # needed to prevent update loops with _repaintDelay and _repaint3DDelay
            if trans != self.indicatorTrans or self.indicatorPlane.isVisible(
            ) != self.indicatorVisible:
                self.indicatorTrans = trans
                self.indicatorPlane.setTransform(trans)
                self.indicatorPlane.setVisible(self.indicatorVisible)
                self._repaint3DDelay()

    def setIndicatorVisible(self, visible):
        '''Set whether the 3D plane indicator is visible or not.'''
        self.indicatorVisible = visible
        self._repaintDelay()

    def getBBTransform(self):
        '''
        Returns the boundbox transform which adjusts the figures to fit inside the selected viewing area based on the 
        scene bound box, scroll, and zoom parameters.
        '''
        bbw, bbh, bbd = self.sceneBB.getDimensions()
        bscale = self.getBoxFitScale(bbw, bbh)
        return transform(self.scroll - self.sceneBB.center * bscale,
                         vec3(self.zoom * bscale, self.zoom * bscale))

    def _planeToWorldTransform(self):
        '''Returns the transform from plane-relative coordinates to world coordinates.'''
        return self.viewplane * self.getBBTransform().inverse()

    def getWorldPosition(self, x, y, isAbsolute=True):
        '''
        Returns the world position of the screen coordinate (x,y). If `isAbsolute', (x,y) is an absolute pixel coordinate,
        otherwise it is a screen proportionate coordinate (ie. (0,0) is top-left corner of screen and (1,1) is bottom-right).
        '''
        return self._planeToWorldTransform() * self.getScreenOrthoPosition(
            x, y, isAbsolute)

    def getScreenPosition(self, pos):
        '''Returns the screen coordinate of the world vector `pos'.'''
        return self.camera.getScreenPosition(self.getOrthoPosition(pos))

    def getOrthoPosition(self, pos):
        '''
        Returns the orthographic camera coordinate of the world vector `pos'. In orthographic coordinates, the screen
        center is (0,0) and bottom-right is (1,1).
        '''
        return (self._planeToWorldTransform() / pos) * vec3(1, 1)

    def getScreenOrthoPosition(self, x, y, isAbsolute=True):
        '''
        Returns the screen orthographic coordinate from the screen coordinate (x,y).If `isAbsolute', (x,y) is an absolute 
        coordinate, otherwise it is a screen proportionate coordinate (ie. (0,0) is top-left corner, (1,1) is bottom-right).
        '''
        return self.camera.getWorldPosition(
            x, y, isAbsolute
        )  # because the camera doesn't move from the origin this converts to ortho coordinates

    def getImagePosition(self, x, y):
        pos = self.getScreenOrthoPosition(x, y)
        rep = self.mgr.findObject(self.sourceName)

        imgstackpos = self.getImageStackPosition()
        trans = rep.getDefinedTransform(imgstackpos)
        invtrans = self.viewplane.inverse()
        invtrans.setScale(vec3(1, 1))

        screennodes, indices, xis = self._imagePlaneMesh(
            rep, self.viewplane, imgstackpos, True)

        for tri in indices:
            nodes = [screennodes[i] for i in tri]
            trixis = [xis[i] for i in tri]
            nodes += [nodes[0] + nodes[0].planeNorm(nodes[1], nodes[2])]

            xi = pointSearchLinTet(pos, *nodes)

            if xi.isInUnitCube() and sum(xi) <= 1.0:
                imgxi = ElemType.Tri1NL.applyBasis(trixis, xi.x(), xi.y(), 0)
                return imgxi * trans.getScale().abs(), imgstackpos

        return None, imgstackpos

    def setFigTransforms(self):
        '''Set the transforms for all figures to fit them in the viewing area and translate/scale as inputed by user.'''
        bbt = self.getBBTransform()
        for _, figs in self.objFigMap.values():
            for fig in figs:
                fig.setTransform(bbt)

    def _imagePlaneMesh(self, rep, planetrans, stackpos, centerInView=False):
        '''
        Returns the (nodes,indices,xis) mesh for the isoplane mesh defining the slice of of image representation `rep' 
        at stack position `stackpos' or plane position `planetrans'. The nodes are in screen ortho coordinate space, 
        adjusted to be within the viewing bound box if `centerInView' is True. 
        '''
        # get the mesh for the plane figure in world space coordinates
        nodes, indices, xis = ImageAlgorithms.calculateReprIsoplaneMesh(
            rep, planetrans, stackpos, self.slicewidth)

        # transform nodes into camera space coordinates
        invtrans = planetrans.inverse()
        invtrans.setScale(vec3(1, 1))
        nodes = [invtrans * v for v in nodes]

        # transform the nodes to fit within the current viewing bound box
        if centerInView:
            bbt = self.getBBTransform()
            nodes = [bbt * n for n in nodes]

        return nodes, indices, xis

    def _updatePlaneFig(self, fig, rep, planetrans, stackpos=0):
        '''
        Updates `fig' to contain mesh data for isoplane cut through `rep' at plane `planetrans' if `rep' is a volume,
        otherwise at image stack position `stackpos'. Returns the bound box of the mesh.
        '''
        assert rep != None, 'Cannot find representation for 2D view'

        nodes, indices, xis = self._imagePlaneMesh(
            rep, planetrans, stackpos)  # mesh in screen ortho coordinates
        vb = PyVertexBuffer(nodes, [vec3.Z()] * len(nodes), None, xis)
        ib = PyIndexBuffer(indices)
        fig.fillData(vb, ib)

        return BoundBox(nodes)

    @delayedcall(0.15)
    def _updateMeshPlanecutFigs(self, repfigspairs, planetrans):
        '''Updates the figures containing mesh slice data for each secondary mesh object.'''
        @Utils.taskroutine('Generating Mesh Planecut')
        @Utils.timing
        def _generatecut(task):
            for rep, figs in repfigspairs:
                tslen = len(rep.getTimestepList())
                planept = planetrans.getTranslation()
                planerot = planetrans.getRotation()
                planenorm = planerot * vec3(0, 0, 1)

                assert tslen == len(figs)
                task.setMaxProgress(tslen)

                for i, tsrep in enumerate(rep.enumSubreprs()):
                    task.setProgress(i + 1)

                    snodes, sinds, scols = MeshAlgorithms.generateMeshPlanecut(
                        tsrep.dataset,
                        'slicemesh%i' % i,
                        planept,
                        planenorm,
                        self.linewidth,
                        nodecolors=tsrep.nodecolors)
                    vb = None
                    ib = None

                    if snodes:
                        # transform the isolines to orthographic camera space
                        snodes.sub(planept, 0, 0, snodes.n(), 1)
                        snodes.mul(planerot.inverse())
                        snodes.mul(vec3(1, 1), 0, 0, snodes.n(), 1)

                        vb = renderer.MatrixVertexBuffer(snodes, scols)
                        ib = renderer.MatrixIndexBuffer(sinds)

                    self.mgr.callThreadSafe(figs[i].fillData, vb, ib)

            self._repaintDelay()

        return self.mgr.runTasks(_generatecut())

    def updateView(self):
        '''
        Update the visible data for the current view's position. This will update the quad for the main image, secondary
        images, and refill the isoline meshes for the secondary meshes. The plane in world space the 2D view currently
        shows will be set to self.viewplane. Handles will also be updated as necessary, and all figures will be transformed
        to fit into the current viewing position. If this method is overridden, the override should call this one to
        perform these operations after updating subtype-specific state, ie. as the last statement in the method.
        '''
        assert isMainThread()
        rep = self.mgr.findObject(
            self.sourceName
        )  # get the main object, this is None if it's been deleted since the last update
        if rep == None:
            return

        imgstackpos = self.getImageStackPosition()

        self.retainObjFigures([
            r.getName() for r in self.mgr.enumSceneObjectReprs()
        ])  # remove reprs that don't exist anymore

        # calculate the plane in space this view is located at
        self.viewplane = self.calculateViewPlane(
            rep.getDefinedTransform(imgstackpos), self.planeName,
            self.getImageXiPosition())

        self.setPlaneIndicator(rep, self.viewplane)

        # update handle visibility/activity
        for h in self.handles:
            h.setPlaneVisible(self.viewplane)
            h.updateHandle()

        _, mainfig = self.getObjFigures(
            self.sourceName)  # get the main object's figure

        # update the main figure's plane figure and set it's material to the representation's currently used one
        self.sceneBB = self._updatePlaneFig(mainfig[0], rep, self.viewplane,
                                            imgstackpos)
        mainfig[0].setOverlay(False)
        mat = rep.getCurrentTimestepMaterial(imgstackpos)
        if mat:
            mainfig[0].setMaterial(mat)

        repfigspairs = []

        # update each secondary representation, either image or mesh types
        for s in self.getSecondaryNames():
            s = s.split('<', 1)[0].strip()
            srep = self.mgr.findObject(
                s
            )  # split the label by <, assuming there's no < in the repr or object names
            assert srep, 'Cannot find %r' % s

            # image repr, 1 plane for volumes since they slice through at the view plane, 1 plane per slice of image series since they're 2D
            if isinstance(srep, ImageSceneObjectRepr):
                numslices = srep.getNumStackSlices() if isinstance(
                    srep, ImageSeriesRepr) else 1
                _, sfig = self.getObjFigures(s, numslices)

                for sslice in range(numslices):
                    self.sceneBB += self._updatePlaneFig(
                        sfig[sslice], srep, self.viewplane, sslice)
                    sfig[sslice].setMaterial(
                        srep.getCurrentTimestepMaterial(sslice))
                    sfig[sslice].setOverlay(True)
            else:
                # mesh repr, find the closest timestep to "now"
                timesteps = srep.getTimestepList()
                tsrepr = srep.getTimestepRepr()
                matname = tsrepr.getMaterialName()
                nearestTS, _ = minmaxIndices(
                    abs(self.mgr.timestep - v) for v in timesteps)

                #strans,sfigs=self.getObjFigures(s,len(timesteps),FT_LINELIST) # get the view plane and figures for this repr
                strans, sfigs = self.getObjFigures(s, len(
                    timesteps))  # get the view plane and figures for this repr

                self.setObjPlane(s,
                                 self.viewplane)  # set the repr's view plane

                # calculate the repr's isolines where it intersects the viewing plane if the current view plane
                # differs from the one the previous isolines were calculated for, this ensure the calculation is
                # only done if the view plane has moved.
                if isinstance(tsrepr, MeshSceneObjectRepr) and (
                        tsrepr.tris != None
                        or tsrepr.lines != None) and self.viewplane != strans:
                    repfigspairs.append((srep, sfigs))

                # set figure properties, making only the current timestep's figure visible
                for i, f in enumerate(sfigs):
                    f.setOverlay(True)
                    f.setMaterial(matname)
                    f.setVisible(
                        i == nearestTS
                    )  # True only for the figure at the current timestep

        if repfigspairs:
            self._updateMeshPlanecutFigs(repfigspairs, self.viewplane)

        self.setFigTransforms()