Пример #1
0
class Dockit(DirectObject):
    ''' Main class of the Dockit! game '''
    # VdW radius scale constant
    radius_scale = 1.5
    # Step to move ligand
    move_step = 50
    # Step to rotate ligand
    rotate_step = 90
    #Instructions
    instructions = ['Dock it!','',
                    'X: a/d','Y: w/s','Z: q/e',
                    'Heading: r/f','Pitch: t/g','Roll: h/y',
                    'Cartoon: c','Control: x','Center: z',
                    'Help: v','Exit: escape']
    
    def __init__(self, width, height, pdb_file1, pdb_file2, full_atom=True, debug=False):
        ''' Build Dockit! '''
        self.multisamples = base.win.getFbProperties().getMultisamples()
        self.cartoon = False
        self.structures = False
        self.help = False
        self.text_on_screen = []
        self.width = width
        self.height = height
        
        # Keyboard events
        self.accept('c', self.toggle_cartoon)
        self.accept('x', self.toggle_control_structures)
        self.accept('z', self.center_camera)
        self.accept('v', self.help_on_screen)
        self.accept('escape', sys.exit)
        self.keyMap = {"a":0, "d":0, "w":0, "s":0, "q":0, "e":0, 
                       "r":0, "f":0, "g":0, "t":0, "h":0, "y":0}
        
        # Load PDB structures
        receptor = PDBReader.read_pdb_from_file(pdb_file1)
        ligand = PDBReader.read_pdb_from_file(pdb_file2)
        receptor.move_to_origin()
        ligand.move_to_origin()
        
        # Background
        base.setBackgroundColor(0.7, 0.7, 0.7, 1.0)
        
        # Load Color maps
        color_map_cpk = CPK()
        color_map_bfactor = BFactor(min_value=-14.0, max_value=8.0, middle_value=0.0)
        
        # Load 3D model
        self.receptor_node = render.attachNewNode("Receptor")
        if full_atom:
            self.load_protein_full_atom(receptor, self.receptor_node, color_map_bfactor)
        else:
            self.load_protein_residue(receptor, self.receptor_node)
        self.receptor_node.reparentTo(render)
        
        self.ligand_node = render.attachNewNode("Ligand")
        if full_atom:
            self.load_protein_full_atom(ligand, self.ligand_node, color_map_cpk)
        else:
            self.load_protein_residue(ligand, self.ligand_node)
        self.ligand_node.reparentTo(render)
        
        # Ambient lights
        self.alight = AmbientLight('alight')
        self.alight.setColor(LVecBase4f(0.162679, 0.162679, 0.169059, 1.0))
        self.alnp = render.attachNewNode(self.alight)
        render.setLight(self.alnp)
        
        # Center receptor and ligand
        self.center_proteins()
        
        # Center camera on complexes
        self.center = loader.loadModel("models/atom_sphere")
        self.center.setColor(0.0, 0.53, 0.0, 1.0)
        self.center_camera()
        
        # DirectionalLight
        self.dlight = DirectionalLight('dlight')
        self.dlight.setColor(LVecBase4f(0.797448, 0.660287, 0.743222, 1.0))
        self.dlight.setShadowCaster(True, 512, 512)
        render.setShaderAuto()
        self.dlnp = render.attachNewNode(self.dlight)
        self.dlnp.setPos(0,-50,0)
        render.setLight(self.dlnp)
        self.dlnp.lookAt(self.center)
        l1 = loader.loadModel("models/Dirlight")
        l1.setColor(1.0, 1.0, 0.0, 1.0)
        l1.setPos(0, -50, 0)
        l1.setScale(1)
        self.lights = [l1]
        
        # Post processing        
        render.setAntialias(AntialiasAttrib.MAuto)
        
        # Show control structures if required
        if self.structures:
            for light in self.lights:
                light.reparentTo(render)
            self.center.reparentTo(render)
            
        # Movement functions
        taskMgr.add(self.ligand_movement, 'ligand_movement') 

        # Key mapping
        self.key_bindings()
        
        # Show frame rate
        if debug:
            base.setFrameRateMeter(True)
    
        # Create two windows from cameras pointing to each molecule
        wx = base.win.getProperties().getXOrigin()
        wy = base.win.getProperties().getYOrigin() 
        
        # Ligand window
        wp = WindowProperties()
        wp.setSize(300,300)
        wp.setOrigin(self.width + wx + 10, wy - 50)
        wp.setTitle('From Ligand')
        self.ligand_view = base.openWindow(props=wp)
        self.cam_ligand = base.camList[1]
        self.cam_ligand.setPos(self.center.getX(), self.center.getY(), self.center.getZ())
        self.cam_ligand.lookAt(self.receptor_node)
        
        # Receptor window
        wp = WindowProperties()
        wp.setSize(300,300)
        wp.setOrigin(self.width + wx + 10, wy + 300)
        wp.setTitle('From Receptor')
        self.receptor_view = base.openWindow(props=wp)
        self.cam_receptor = base.camList[2]
        self.cam_receptor.setPos(self.center.getX(), self.center.getY(), self.center.getZ())
        self.cam_receptor.lookAt(self.ligand_node)

        
    def key_bindings(self):
        ''' Define key bindings '''
        base.accept('a', self.setKey, ["a",1])
        base.accept('a-up', self.setKey, ["a",0])
        base.accept('d', self.setKey, ["d",1])
        base.accept('d-up', self.setKey, ["d",0])
        base.accept('w', self.setKey, ["w",1])
        base.accept('w-up', self.setKey, ["w",0])
        base.accept('s', self.setKey, ["s",1])
        base.accept('s-up', self.setKey, ["s",0])
        base.accept('q', self.setKey, ["q",1])
        base.accept('q-up', self.setKey, ["q",0])
        base.accept('e', self.setKey, ["e",1])
        base.accept('e-up', self.setKey, ["e",0])
        base.accept('f', self.setKey, ["f",1])
        base.accept('f-up', self.setKey, ["f",0])
        base.accept('r', self.setKey, ["r",1])
        base.accept('r-up', self.setKey, ["r",0])
        base.accept('g', self.setKey, ["g",1])
        base.accept('g-up', self.setKey, ["g",0])
        base.accept('t', self.setKey, ["t",1])
        base.accept('t-up', self.setKey, ["t",0])
        base.accept('h', self.setKey, ["h",1])
        base.accept('h-up', self.setKey, ["h",0])
        base.accept('y', self.setKey, ["y",1])
        base.accept('y-up', self.setKey, ["y",0])
        
        
    def ligand_movement(self, task):
        ''' Compute ligand movement depending on key bindings '''
        if (self.keyMap["a"]!=0):
            self.ligand_node.setX(self.ligand_node.getX()-Dockit.move_step * globalClock.getDt())
        if (self.keyMap["d"]!=0):
            self.ligand_node.setX(self.ligand_node.getX()+Dockit.move_step * globalClock.getDt())
        if (self.keyMap["q"]!=0):
            self.ligand_node.setY(self.ligand_node.getY()-Dockit.move_step * globalClock.getDt())
        if (self.keyMap["e"]!=0):
            self.ligand_node.setY(self.ligand_node.getY()+Dockit.move_step * globalClock.getDt())
        if (self.keyMap["w"]!=0):
            self.ligand_node.setZ(self.ligand_node.getZ()-Dockit.move_step * globalClock.getDt())
        if (self.keyMap["s"]!=0):
            self.ligand_node.setZ(self.ligand_node.getZ()+Dockit.move_step * globalClock.getDt())
        if (self.keyMap["r"]!=0):
            self.ligand_node.setH(self.ligand_node.getH()-Dockit.rotate_step * globalClock.getDt())
        if (self.keyMap["f"]!=0):
            self.ligand_node.setH(self.ligand_node.getH()+Dockit.rotate_step * globalClock.getDt())
        if (self.keyMap["g"]!=0):
            self.ligand_node.setP(self.ligand_node.getP()-Dockit.rotate_step * globalClock.getDt())
        if (self.keyMap["t"]!=0):
            self.ligand_node.setP(self.ligand_node.getP()+Dockit.rotate_step * globalClock.getDt())
        if (self.keyMap["h"]!=0):
            self.ligand_node.setR(self.ligand_node.getR()-Dockit.rotate_step * globalClock.getDt())
        if (self.keyMap["y"]!=0):
            self.ligand_node.setR(self.ligand_node.getR()+Dockit.rotate_step * globalClock.getDt())
        return task.cont


    def setKey(self, key, value):
        ''' Record the state of the arrow keys '''
        self.keyMap[key] = value 
        
        
    def load_protein_full_atom(self, protein, node, color_map):
        ''' Display a given protein using spheres models for each atom '''
        atoms = protein.get_atoms()
        self.atom_objects = []
        for atom in atoms:
            a = loader.loadModel("models/atom_sphere")
            a.setPos(atom.get_x(), atom.get_y(), atom.get_z())
            a.setScale(atom.get_radius() / Dockit.radius_scale)
            self.apply_color(a, atom, color_map)
            a.reparentTo(node)
        node.flattenStrong()
        
    
    def load_protein_residue(self, protein, node):
        ''' Display a given protein using spheres models for each atom '''
        residues = protein.get_residues()
        self.residues_objects = []
        for residue in residues:
            r = loader.loadModel("models/atom_sphere")
            x,y,z = residue.get_center_coordinates()
            r.setPos(x, y, z)
            r.setScale(4.0 / Dockit.radius_scale)
            r.setColor(LVecBase4f(1.0, 0.59, 0.0, 1.0))
            r.setColorScale(LVecBase4f(1, 1, 1, 1))
            r.reparentTo(node)
        node.flattenStrong()
        
        
    def apply_color(self, a3d, atom, color_map):
        ''' Apply a color map to the element '''
        if isinstance(color_map, BFactor):
            color = color_map.get_color_by_bfactor(atom.get_b_factor())
        else:
            color = color_map.get_color_by_element(atom.get_element())
        red, green, blue, alpha = color.get_rgba() 
        a3d.setColor(LVecBase4f(red, green, blue, alpha))
        a3d.setColorScale(LVecBase4f(1, 1, 1, 1))
    
    
    def toggle_cartoon(self):
        ''' Use Cartoon ink filter '''
        self.cartoon = not self.cartoon
        if self.cartoon:
            tempnode = NodePath(PandaNode("temp node"))
            tempnode.setAttrib(LightRampAttrib.makeSingleThreshold(0.4, 0.6))
            tempnode.setShaderAuto()
            base.cam.node().setInitialState(tempnode.getState())
            self.separation = 1.3 # Pixels
            self.filters = CommonFilters(base.win, base.cam)
            self.filters.setCartoonInk(separation=self.separation)
            # Currently using MAuto antialias, uncomment to use different
            #render.setAntialias(AntialiasAttrib.MBetter)
            #self.filters.finalQuad.setAntialias(AntialiasAttrib.MBetter)
        else:
            self.filters.cleanup()
            base.cam.node().setInitialState(self.alight.getState())
    
    
    def toggle_control_structures(self):
        ''' Show control structures as lights and center nodes and bounds if required '''
        self.structures = not self.structures
        for light in self.lights:
            if not self.structures:
                light.detachNode()
            else:
                light.reparentTo(render)
        
        if self.structures:
            self.receptor_node.showBounds()
            self.ligand_node.showBounds()
            self.center.reparentTo(render)
            print "Number of multisamples available: %d" % self.multisamples
        else:
            self.receptor_node.hideBounds()
            self.ligand_node.hideBounds()
            self.center.detachNode()
    
    
    def center_proteins(self):
        ''' Move receptor and ligand to a centered position depending on its size '''
        # Center the receptor
        receptor_radius = self.receptor_node.getBounds().getRadius()
        receptor_center = self.receptor_node.getBounds().getCenter()
        self.receptor_node.setPos(0,receptor_radius,0)
        
        # Center the ligand
        ligand_radius = self.ligand_node.getBounds().getRadius()
        self.ligand_node.setPos(receptor_center[0]-receptor_radius-ligand_radius, 
                                receptor_center[1], 
                                receptor_center[2])
        self.ligand_node.lookAt(self.receptor_node)
     
    
    def center_camera(self):
        ''' Center camera on scene '''
        xc, yc, zc = (self.receptor_node.getBounds().getCenter() + self.ligand_node.getBounds().getCenter()) / 2.0
        self.center.setPos(xc, yc, zc)
        ligand_radius = self.ligand_node.getBounds().getRadius()
        receptor_radius = self.receptor_node.getBounds().getRadius()
        scene_center = self.center.getPos()
        base.cam.setPos(scene_center[0], -10-scene_center[1]-2*ligand_radius-2*receptor_radius, scene_center[2])
        base.cam.lookAt(self.center)  
        
        
    def help_on_screen(self):
        ''' Show commands on screen '''
        self.help = not self.help
        if self.help:
            i = 0
            for instruction in Dockit.instructions:
                self.text_on_screen.append(self.__gen_label_text(instruction,i))
                i += 1
        else:
            for text in self.text_on_screen:
                text.destroy()
            del self.text_on_screen[:]
    
    
    def __gen_label_text(self, text, i):
        ''' Auxiliar function to display text line by line '''
        return OnscreenText(text = text, pos = (-1.6, .9-.06*i), fg=(1,1,1,1),
                       align = TextNode.ALeft, scale=0.06, mayChange = 0)