Beispiel #1
0
    def make_DnaStrandOrSegment_for_marker(self, controlling_marker): # review: wholechain arg needed? @@@
        """
        The given DnaMarker is either newly made to control a wholechain,
        or old but newly controlling it; but it has no DnaStrandOrSegment.

        Make and return a new DnaStrand or DnaSegment
        (ask marker what class to use)
        inside self (review: inside some Group inside self?),
        perhaps making use of info in controlling_marker
        to help decide how to initialize some of its attributes.

        (Assume calling code will later move all chunks
        and markers from marker's wholechain into the new DnaStrandOrSegment,
        and will store references to it as needed
        into controlling_marker and/or its wholechain,
        so don't do those things here.)
        """
        assert not self.killed(), \
               "self must not be killed in %r.make_DnaStrandOrSegment_for_marker" % self
        class1 = controlling_marker.DnaStrandOrSegment_class()
        name = gensym(class1.__name__.split('.')[-1], self.assy)
            ###STUB -- should use class constant prefix
            # todo: sensible name? (if we split a seg, is name related to old seg; if so how?)
        assy = controlling_marker.assy # it's a Jig so it has one
        obj = class1(name, assy, None) # note: these args are for Group.__init__
        self.addchild(obj) # note: this asserts self.assy is not None
            # (but it can't assert not self.killed(), see its comment for why)
        return obj
 def _createStructure(self):        
     """
     Build a graphene sheet from the parameters in the Property Manager.
     """
     
     # self.name needed for done message
     if self.create_name_from_prefix:
         # create a new name
         name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
         self._gensym_data_for_reusing_name = (self.prefix, name)
     else:
         # use externally created name
         self._gensym_data_for_reusing_name = None
             # (can't reuse name in this case -- not sure what prefix it was
             #  made with)
         name = self.name
         
         
     params = self._gatherParameters()
     
     #
     self.win.assy.part.ensure_toplevel_group()
     
     structGenerator = GrapheneGenerator()
     
     struct = structGenerator.make(self.win.assy,                                      
                                   name, 
                                   params, 
                                   editCommand = self)
     
    
     self.win.assy.part.topnode.addmember(struct)
     self.win.win_update()
     return struct
Beispiel #3
0
    def _createStructure(self):
        """
        Build a graphene sheet from the parameters in the Property Manager.
        """

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix,
                                      self.win.assy)  # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
            # (can't reuse name in this case -- not sure what prefix it was
            #  made with)
            name = self.name

        params = self._gatherParameters()

        #
        self.win.assy.part.ensure_toplevel_group()

        structGenerator = GrapheneGenerator()

        struct = structGenerator.make(self.win.assy,
                                      name,
                                      params,
                                      editCommand=self)

        self.win.assy.part.topnode.addmember(struct)
        self.win.win_update()
        return struct
Beispiel #4
0
    def make_Atom_and_bondpoints(self, elem, pos, atomtype=None, Chunk_class=None):
        """
        Create one unbonded atom, of element elem
        and (if supplied) the given atomtype
        (otherwise the default atomtype for elem),
        at position pos, in its own new chunk,
        with enough bondpoints to have no valence error.

        @param Chunk_class: constructor for the returned atom's new chunk
                            (self.assy.Chunk by default)

        @return: one newly created Atom object, already placed into a new
                 chunk which has been added to the model using addnode
        """
        # bruce 041215 moved this from chunk.py to chem.py, and split part of it
        # into the new atom method make_bondpoints_when_no_bonds, to help fix bug 131.
        # bruce 050510 added atomtype option
        # bruce 080520 added Chunk_class option
        # bruce 090112 renamed oneUnbonded function and turned it into this method
        assy = self.assy
        if Chunk_class is None:
            Chunk_class = assy.Chunk
        chunk = Chunk_class(assy, "bug")  # name is reset below!
        atom = Atom(elem.symbol, pos, chunk)
        # bruce 041124 revised name of new chunk, was gensym('Chunk.');
        # no need for gensym since atom key makes the name unique, e.g. C1.
        atom.set_atomtype_but_dont_revise_singlets(atomtype)
        # ok to pass None, type name, or type object; this verifies no change in elem
        # note, atomtype might well already be the value we're setting;
        # if it is, this should do nothing
        ## chunk.name = "Chunk-%s" % str(atom)
        chunk.name = gensym("Chunk", assy)  # bruce 080407 per Mark NFR desire
        atom.make_bondpoints_when_no_bonds()  # notices atomtype
        assy.addnode(chunk)  # REVIEW: same as self.addnode?
        return atom
Beispiel #5
0
    def make_DnaStrandOrSegment_for_marker(
            self, controlling_marker):  # review: wholechain arg needed? @@@
        """
        The given DnaMarker is either newly made to control a wholechain,
        or old but newly controlling it; but it has no DnaStrandOrSegment.

        Make and return a new DnaStrand or DnaSegment
        (ask marker what class to use)
        inside self (review: inside some Group inside self?),
        perhaps making use of info in controlling_marker
        to help decide how to initialize some of its attributes.

        (Assume calling code will later move all chunks
        and markers from marker's wholechain into the new DnaStrandOrSegment,
        and will store references to it as needed
        into controlling_marker and/or its wholechain,
        so don't do those things here.)
        """
        assert not self.killed(), \
               "self must not be killed in %r.make_DnaStrandOrSegment_for_marker" % self
        class1 = controlling_marker.DnaStrandOrSegment_class()
        name = gensym(class1.__name__.split('.')[-1], self.assy)
        ###STUB -- should use class constant prefix
        # todo: sensible name? (if we split a seg, is name related to old seg; if so how?)
        assy = controlling_marker.assy  # it's a Jig so it has one
        obj = class1(name, assy,
                     None)  # note: these args are for Group.__init__
        self.addchild(obj)  # note: this asserts self.assy is not None
        # (but it can't assert not self.killed(), see its comment for why)
        return obj
Beispiel #6
0
    def __init__(self, assy, name, scale, pov, zoomFactor, wxyz):
        """
        @param pov: the inverse of the "center of view" in model coordinates
        @type pov: position vector (Numeric.array of 3 ints or floats, as made
                   by V(x,y,z))

        @param wxyz: orientation of view
        @type wxyz: a Quaternion (class VQT.Q), or a sequence of 4 floats
                    which can be passed to that class to make one, e.g.
                    Q(W, x, y, z) is the quaternion with axis vector x,y,z
                    and sin(theta/2) = W
        """
        self.const_pixmap = imagename_to_pixmap("modeltree/NamedView.png")
        if not name:
            name = gensym("%s" % self.sym, assy)
        Node.__init__(self, assy, name)
        self.scale = scale
        assert type(pov) is type(V(1, 0, 0))
        self.pov = V(pov[0], pov[1], pov[2])
        self.zoomFactor = zoomFactor
        self.quat = Q(wxyz)
            #bruce 050518/080303 comment: wxyz is passed as an array of 4 floats
            # (in same order as in mmp file's csys record), when parsing
            # csys mmp records, or with wxyz a quat in other places.
        return
Beispiel #7
0
    def __init__(self, assy, name, scale, pov, zoomFactor, wxyz):
        """
        @param pov: the inverse of the "center of view" in model coordinates
        @type pov: position vector (Numeric.array of 3 ints or floats, as made
                   by V(x,y,z))

        @param wxyz: orientation of view
        @type wxyz: a Quaternion (class VQT.Q), or a sequence of 4 floats
                    which can be passed to that class to make one, e.g.
                    Q(W, x, y, z) is the quaternion with axis vector x,y,z
                    and sin(theta/2) = W
        """
        self.const_pixmap = imagename_to_pixmap("modeltree/NamedView.png")
        if not name:
            name = gensym("%s" % self.sym, assy)
        Node.__init__(self, assy, name)
        self.scale = scale
        assert type(pov) is type(V(1, 0, 0))
        self.pov = V(pov[0], pov[1], pov[2])
        self.zoomFactor = zoomFactor
        self.quat = Q(wxyz)
            #bruce 050518/080303 comment: wxyz is passed as an array of 4 floats
            # (in same order as in mmp file's csys record), when parsing
            # csys mmp records, or with wxyz a quat in other places.
        return
Beispiel #8
0
 def __init__(self, assy, name, text=''):
     self.const_pixmap = imagename_to_pixmap("modeltree/comment.png")
     if not name:
         name = gensym("%s" % self.sym, assy)
     Node.__init__(self, assy, name)
     self.lines = [] # this makes set_text changed() test legal (result of test doesn't matter)
     self.set_text(text)
     return
Beispiel #9
0
 def __init__(self, assy, name, text=''):
     self.const_pixmap = imagename_to_pixmap("modeltree/comment.png")
     if not name:
         name = gensym("%s" % self.sym, assy)
     Node.__init__(self, assy, name)
     self.lines = [] # this makes set_text changed() test legal (result of test doesn't matter)
     self.set_text(text)
     return
Beispiel #10
0
def _make_DnaGroup_for_homeless_objects_in_Part(part):
    # not needed, done in addnode: part.ensure_toplevel_group()
    assy = part.assy  #k
    name = gensym("DnaGroup", assy)  #bruce 080407 remove "fallback", pass assy
    dad = None
    dnaGroup = DnaGroup(name, assy, dad)  # same args as for Group.__init__
    part.addnode(dnaGroup)
    if debug_flags.DEBUG_DNA_UPDATER:
        print "dna_updater: made new dnaGroup %r" % dnaGroup, \
              "(bug or unfixed mmp file)"
    return dnaGroup
Beispiel #11
0
def _make_DnaGroup_for_homeless_objects_in_Part(part):
    # not needed, done in addnode: part.ensure_toplevel_group()
    assy = part.assy #k
    name = gensym("DnaGroup", assy) #bruce 080407 remove "fallback", pass assy
    dad = None
    dnaGroup = DnaGroup(name, assy, dad) # same args as for Group.__init__
    part.addnode(dnaGroup)
    if debug_flags.DEBUG_DNA_UPDATER:
        print "dna_updater: made new dnaGroup %r" % dnaGroup, \
              "(bug or unfixed mmp file)"
    return dnaGroup
def mark_atoms(atoms):
    assert atoms # a list
    assy = atoms[0].molecule.assy
    for atom in atoms:
        assert atom.molecule.assy is assy # all in same assy
    jig = VeryVisibleAtomMarker(assy, atoms)
    jig.name = gensym("Marked Atoms ", assy)
    assy.place_new_jig(jig)
    # redraw, etc
    assy.win.glpane.gl_update() # this works now to redraw
    #e more updates?
    return
Beispiel #13
0
def mark_atoms(atoms):
    assert atoms # a list
    assy = atoms[0].molecule.assy
    for atom in atoms:
        assert atom.molecule.assy is assy # all in same assy
    jig = VeryVisibleAtomMarker(assy, atoms)
    jig.name = gensym("Marked Atoms ", assy)
    assy.place_new_jig(jig)
    # redraw, etc
    assy.win.glpane.gl_update() # this works now to redraw
    #e more updates?
    return
Beispiel #14
0
    def makeChunkFromSelectedAtoms(self):
        """
        Create a new chunk from the selected atoms.
        """

        # ninad 070411 moved the original method out of 'merge' method to
        # facilitate implementation of 'Create New Chunk
        # from selected atoms' feature

        cmd = greenmsg("Create New Chunk: ")
        if not self.selatoms:
            msg1 = "Create New Chunk: "
            msg2 = redmsg("Select some atoms first to create a new chunk")
            env.history.message(msg1 + msg2)
            return

        # ninad070411 : Following checks if the selected molecules
        # belong to more than one chunk. If they don't (i.e. if they are a part of
        # a sinle chunk, it returns from the method with proper histry msg

        molList = []
        for atm in self.selatoms.values():
            if not len(molList) > 1:
                mol = atm.molecule
                if mol not in molList:
                    molList.append(mol)

        if len(molList) < 2:
            msg1 = "Create New Chunk: "
            msg2 = redmsg(
                "Not created as the selected atoms are part of the \
            same chunk."
            )
            env.history.message(msg1 + msg2)
            return

        # bruce 060329 new feature: work on atoms too (put all selected atoms into a new chunk)
        self.ensure_toplevel_group()  # avoid bug for part containing just one chunk, all atoms selected
        numol = Chunk(self.assy, gensym("Chunk", self.assy))
        natoms = len(self.selatoms)
        for a in self.selatoms.values():
            # leave the moved atoms picked, so still visible
            a.hopmol(numol)
        self.addmol(numol)
        # e should we add it in the same groups (and just after the chunks) which these atoms used to belong to?
        # could use similar scheme to placing jigs...
        msg = fix_plurals(
            "made chunk from %d atom(s)" % natoms
        )  # len(numol.atoms) would count bondpoints, this doesn't
        msg = msg.replace("chunk", numol.name)
        env.history.message(cmd + msg)
        self.w.win_update()
Beispiel #15
0
    def makeChunkFromSelectedAtoms(self):
        """
        Create a new chunk from the selected atoms.
        """

        #ninad 070411 moved the original method out of 'merge' method to
        #facilitate implementation of 'Create New Chunk
        #from selected atoms' feature

        cmd = greenmsg("Create New Chunk: ")
        if not self.selatoms:
            msg1 = "Create New Chunk: "
            msg2 = redmsg('Select some atoms first to create a new chunk')
            env.history.message(msg1 + msg2)
            return

        #ninad070411 : Following checks if the selected molecules
        #belong to more than one chunk. If they don't (i.e. if they are a part of
        # a sinle chunk, it returns from the method with proper histry msg

        molList = []
        for atm in self.selatoms.values():
            if not len(molList) > 1:
                mol = atm.molecule
                if mol not in molList:
                    molList.append(mol)

        if len(molList) < 2:
            msg1 = "Create New Chunk: "
            msg2 = redmsg('Not created as the selected atoms are part of the \
            same chunk.')
            env.history.message(msg1 + msg2)
            return

        #bruce 060329 new feature: work on atoms too (put all selected atoms into a new chunk)
        self.ensure_toplevel_group(
        )  # avoid bug for part containing just one chunk, all atoms selected
        numol = Chunk(self.assy, gensym("Chunk", self.assy))
        natoms = len(self.selatoms)
        for a in self.selatoms.values():
            # leave the moved atoms picked, so still visible
            a.hopmol(numol)
        self.addmol(numol)
        #e should we add it in the same groups (and just after the chunks) which these atoms used to belong to?
        # could use similar scheme to placing jigs...
        msg = fix_plurals(
            "made chunk from %d atom(s)" %
            natoms)  # len(numol.atoms) would count bondpoints, this doesn't
        msg = msg.replace('chunk', numol.name)
        env.history.message(cmd + msg)
        self.w.win_update()
    def _modifyStructure(self, params):
        """
        Modify the structure based on the parameters specified. 
        Overrides EditCommand._modifystructure. This method removes the old 
        structure and creates a new one using self._createStructure. This 
        was needed for the structures like this (Dna, Nanotube etc) . .
        See more comments in the method.
        """    
        if not pref_nt_segment_resize_by_recreating_nanotube():
            self._modifyStructure_NEW_SEGMENT_RESIZE(params)
            return

        assert self.struct
        # parameters have changed, update existing structure
        self._revertNumber()

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
                # (can't reuse name in this case -- not sure what prefix it was
                #  made with)
            name = self.name

        #@NOTE: Unlike editcommands such as Plane_EditCommand, this 
        #editCommand actually removes the structure and creates a new one 
        #when its modified. -- Ninad 2007-10-24

        self._removeStructure()

        self.previousParams = params

        self.struct = self._createStructure()
        # Now append the new structure in self._segmentList (this list of 
        # segments will be provided to the previous command 
        # (BuildDna_EditCommand)
        # TODO: Should self._createStructure does the job of appending the 
        # structure to the list of segments? This fixes bug 2599 
        # (see also BuildDna_PropertyManager.Ok 

        if self._parentNanotubeGroup is not None:
            #Should this be an assertion? (assert self._parentNanotubeGroup is not 
            #None. For now lets just print a warning if parentNanotubeGroup is None 
            self._parentNanotubeGroup.addSegment(self.struct)
        return  
Beispiel #17
0
    def _build_struct(self, previewing=False):
        """Private method. Called internally to build the structure
        by calling the (generator-specific) method build_struct
        (if needed) and processing its return value.
        """
        params = self.gather_parameters()

        if self.struct is None:
            # no old structure, we are making a new structure
            # (fall through)
            pass
        elif not same_vals(params, self.previousParams):
            # parameters have changed, update existing structure
            self._revert_number()
            # (fall through, using old name)
            pass
        else:
            # old structure, parameters same as previous, do nothing
            return

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix,
                                      self.win.assy)  # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
            # (can't reuse name in this case -- not sure what prefix it was
            #  made with)
            name = self.name

        if previewing:
            env.history.message(self.cmd + "Previewing " + name)
        else:
            env.history.message(self.cmd + "Creating " + name)
        self.remove_struct()
        self.previousParams = params
        self.struct = self.build_struct(name, params, -self.win.glpane.pov)
        self.win.assy.addnode(self.struct)
        # Do this if you want it centered on the previous center.
        # self.win.glpane.setViewFitToWindow(fast = True)
        # Do this if you want it centered on the origin.
        self.win.glpane.setViewRecenter(fast=True)
        self.win.win_update()  # includes mt_update

        return
Beispiel #18
0
    def _createStructure(self):
        """
        Build a peptide from the parameters in the Property Manager.
        """

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix,
                                      self.win.assy)  # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
            # (can't reuse name in this case -- not sure what prefix it was
            #  made with)
            name = self.name

        self.secondary, self.phi, self.psi, aa_type = self._gatherParameters()

        #
        self.win.assy.part.ensure_toplevel_group()
        """
        struct = self.structGenerator.make(self.win.assy,
                                           name,
                                           params,
                                           -self.win.glpane.pov)
        """
        from geometry.VQT import V
        pos1 = V(self.mouseClickPoints[0][0], \
                 self.mouseClickPoints[0][1], \
                 self.mouseClickPoints[0][2])
        pos2 = V(self.mouseClickPoints[1][0], \
                 self.mouseClickPoints[1][1], \
                 self.mouseClickPoints[1][2])
        struct = self.structGenerator.make_aligned(self.win.assy,
                                                   name,
                                                   aa_type,
                                                   self.phi,
                                                   self.psi,
                                                   pos1,
                                                   pos2,
                                                   fake_chain=False,
                                                   secondary=self.secondary)

        self.win.assy.part.topnode.addmember(struct)
        self.win.win_update()
        return struct
Beispiel #19
0
 def _createStructure(self):        
     """
     Build a peptide from the parameters in the Property Manager.
     """
     
     # self.name needed for done message
     if self.create_name_from_prefix:
         # create a new name
         name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
         self._gensym_data_for_reusing_name = (self.prefix, name)
     else:
         # use externally created name
         self._gensym_data_for_reusing_name = None
             # (can't reuse name in this case -- not sure what prefix it was
             #  made with)
         name = self.name
         
     self.secondary, self.phi, self.psi, aa_type = self._gatherParameters()
     
     #
     self.win.assy.part.ensure_toplevel_group()
     """
     struct = self.structGenerator.make(self.win.assy,
                                        name, 
                                        params, 
                                        -self.win.glpane.pov)
     """                                   
     from geometry.VQT import V
     pos1 = V(self.mouseClickPoints[0][0], \
              self.mouseClickPoints[0][1], \
              self.mouseClickPoints[0][2])
     pos2 = V(self.mouseClickPoints[1][0], \
              self.mouseClickPoints[1][1], \
              self.mouseClickPoints[1][2])
     struct = self.structGenerator.make_aligned(self.win.assy, 
                                                name, 
                                                aa_type, 
                                                self.phi, 
                                                self.psi, 
                                                pos1, 
                                                pos2, 
                                                fake_chain = False, 
                                                secondary = self.secondary)
     
     self.win.assy.part.topnode.addmember(struct)
     self.win.win_update()
     return struct
    def _build_struct(self, previewing = False):
        """Private method. Called internally to build the structure
        by calling the (generator-specific) method build_struct
        (if needed) and processing its return value.
        """
        params = self.gather_parameters()

        if self.struct is None:
            # no old structure, we are making a new structure
            # (fall through)
            pass
        elif not same_vals( params, self.previousParams):
            # parameters have changed, update existing structure
            self._revert_number()
            # (fall through, using old name)
            pass
        else:
            # old structure, parameters same as previous, do nothing
            return

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
                # (can't reuse name in this case -- not sure what prefix it was
                #  made with)
            name = self.name
        
        if previewing:
            env.history.message(self.cmd + "Previewing " + name)
        else:
            env.history.message(self.cmd + "Creating " + name)
        self.remove_struct()
        self.previousParams = params
        self.struct = self.build_struct(name, params, - self.win.glpane.pov)
        self.win.assy.addnode(self.struct)
        # Do this if you want it centered on the previous center.
        # self.win.glpane.setViewFitToWindow(fast = True)
        # Do this if you want it centered on the origin.
        self.win.glpane.setViewRecenter(fast = True)
        self.win.win_update() # includes mt_update

        return
    def _createStructure(self):
        """
        creates and returns the structure (in this case a L{Group} object that
        contains the DNA strand and axis chunks.
        @return : group containing that contains the DNA strand and axis chunks.
        @rtype: L{Group}
        @note: This needs to return a DNA object once that model is implemented
        """

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix,
                                      self.win.assy)  # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
            # (can't reuse name in this case -- not sure what prefix it was
            #  made with)
            name = self.name

        # Create the model tree group node.
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # DNa group will be placed), if the topnode is not a group, make it a
        # a 'Group' (applicable to Clipboard parts).See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()

        dnaGroup = DnaGroup(self.name,
                            self.win.assy,
                            self.win.assy.part.topnode,
                            editCommand=self)
        try:

            self.win.assy.place_new_geometry(dnaGroup)

            return dnaGroup

        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            dnaGroup.kill()
            raise PluginBug(
                "Internal error while trying to create DNA duplex.")
    def makeChunkFromAtomList(self,
                              atomList,
                              name = None,
                              group = None,
                              color = None):
        """
        Creates a new chunk from the given atom list.

        @param atomList: List of atoms from which to create the chunk.
        @type  atomList: list

        @param name: Name of new chunk. If None, we'll assign one.
        @type  name: str

        @param group: The group to add the new chunk to. If None, the new chunk
                      is added to the bottom of the model tree.
        @type  group: L{Group}

        @param color: Color of new chunk. If None, no chunk color is assigned
                      (chunk atoms will be drawn in their element colors).
        @type  color: tuple

        @return: The new chunk.
        @rtype:  L{Chunk}

        """
        assert atomList

        if name:
            newChunk = Chunk(self.assy, name)
        else:
            newChunk = Chunk(self.assy, gensym("Chunk", self.assy))

        for a in atomList:
            a.hopmol(newChunk)

        if group is not None:
            group.addchild(newChunk) #bruce 080318 addmember -> addchild
        else:
            self.addnode(newChunk)

        newChunk.setcolor(color, repaint_in_MT = False)

        return newChunk
    def _createStructure(self):
        """
        creates and returns the structure (in this case a L{Group} object that
        contains the DNA strand and axis chunks.
        @return : group containing that contains the DNA strand and axis chunks.
        @rtype: L{Group}
        @note: This needs to return a DNA object once that model is implemented
        """

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
                # (can't reuse name in this case -- not sure what prefix it was
                #  made with)
            name = self.name


        # Create the model tree group node.
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # DNa group will be placed), if the topnode is not a group, make it a
        # a 'Group' (applicable to Clipboard parts).See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()

        dnaGroup = DnaGroup(self.name,
                            self.win.assy,
                            self.win.assy.part.topnode,
                            editCommand = self)
        try:

            self.win.assy.place_new_geometry(dnaGroup)

            return dnaGroup

        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            dnaGroup.kill()
            raise PluginBug("Internal error while trying to create DNA duplex.")
Beispiel #24
0
    def makeChunkFromAtomList(self,
                              atomList,
                              name=None,
                              group=None,
                              color=None):
        """
        Creates a new chunk from the given atom list.
        
        @param atomList: List of atoms from which to create the chunk.
        @type  atomList: list
        
        @param name: Name of new chunk. If None, we'll assign one.
        @type  name: str
        
        @param group: The group to add the new chunk to. If None, the new chunk
                      is added to the bottom of the model tree.
        @type  group: L{Group}
        
        @param color: Color of new chunk. If None, no chunk color is assigned
                      (chunk atoms will be drawn in their element colors).
        @type  color: tuple
        
        @return: The new chunk.
        @rtype:  L{Chunk}
        
        """
        assert atomList

        if name:
            newChunk = Chunk(self.assy, name)
        else:
            newChunk = Chunk(self.assy, gensym("Chunk", self.assy))

        for a in atomList:
            a.hopmol(newChunk)

        if group is not None:
            group.addchild(newChunk)  #bruce 080318 addmember -> addchild
        else:
            self.addnode(newChunk)

        newChunk.setcolor(color, repaint_in_MT=False)

        return newChunk
    def _modifyStructure(self, params):
        """
        Modify the structure based on the parameters specified. 
        Overrides EditCommand._modifystructure. This method removes the old 
        structure and creates a new one using self._createStructure. This 
        was needed for the structures like this (Cnt, Nanotube etc) . .
        See more comments in the method.
        @see: a note in self._createSegment() about use of ntSegment.setProps 
        """
        assert self.struct
        # parameters have changed, update existing structure
        self._revertNumber()

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix,
                                      self.win.assy)  # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
            # (can't reuse name in this case -- not sure what prefix it was
            #  made with)
            name = self.name

        #@NOTE: Unlike editcommands such as Plane_EditCommand, this
        #editCommand actually removes the structure and creates a new one
        #when its modified. We don't yet know if the CNT object model
        # will solve this problem. (i.e. reusing the object and just modifying
        #its attributes.  Till that time, we'll continue to use
        #what the old GeneratorBaseClass use to do ..i.e. remove the item and
        # create a new one  -- Ninad 2007-10-24

        self._removeStructure()

        self.previousParams = params

        self.struct = self._createStructure()
        return
    def _modifyStructure(self, params):
        """
        Modify the structure based on the parameters specified. 
        Overrides EditCommand._modifystructure. This method removes the old 
        structure and creates a new one using self._createStructure. This 
        was needed for the structures like this (Dna, Nanotube etc) . .
        See more comments in the method.
        @see: a note in self._createSegment() about use of dnaSegment.setProps 
        """    
        assert self.struct
        # parameters have changed, update existing structure
        self._revertNumber()

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
                # (can't reuse name in this case -- not sure what prefix it was
                #  made with)
            name = self.name

        #@NOTE: Unlike editcommands such as Plane_EditCommand, this 
        #editCommand actually removes the structure and creates a new one 
        #when its modified. We don't yet know if the DNA object model 
        # will solve this problem. (i.e. reusing the object and just modifying
        #its attributes.  Till that time, we'll continue to use 
        #what the old GeneratorBaseClass use to do ..i.e. remove the item and 
        # create a new one  -- Ninad 2007-10-24

        self._removeStructure()

        self.previousParams = params

        self.struct = self._createStructure()
        return 
Beispiel #27
0
    def make_Atom_and_bondpoints(self,
                                 elem,
                                 pos,
                                 atomtype = None,
                                 Chunk_class = None ):
        """
        Create one unbonded atom, of element elem
        and (if supplied) the given atomtype
        (otherwise the default atomtype for elem),
        at position pos, in its own new chunk,
        with enough bondpoints to have no valence error.

        @param Chunk_class: constructor for the returned atom's new chunk
                            (self.assy.Chunk by default)

        @return: one newly created Atom object, already placed into a new
                 chunk which has been added to the model using addnode
        """
        #bruce 041215 moved this from chunk.py to chem.py, and split part of it
        # into the new atom method make_bondpoints_when_no_bonds, to help fix bug 131.
        #bruce 050510 added atomtype option
        #bruce 080520 added Chunk_class option
        #bruce 090112 renamed oneUnbonded function and turned it into this method
        assy = self.assy
        if Chunk_class is None:
            Chunk_class = assy.Chunk
        chunk = Chunk_class(assy, 'bug') # name is reset below!
        atom = Atom(elem.symbol, pos, chunk)
        # bruce 041124 revised name of new chunk, was gensym('Chunk.');
        # no need for gensym since atom key makes the name unique, e.g. C1.
        atom.set_atomtype_but_dont_revise_singlets(atomtype)
            # ok to pass None, type name, or type object; this verifies no change in elem
            # note, atomtype might well already be the value we're setting;
            # if it is, this should do nothing
        ## chunk.name = "Chunk-%s" % str(atom)
        chunk.name = gensym("Chunk", assy) #bruce 080407 per Mark NFR desire
        atom.make_bondpoints_when_no_bonds() # notices atomtype
        assy.addnode(chunk) # REVIEW: same as self.addnode?
        return atom
 def _makeChunkFromAtomList(self, atomList):
     """
     Creates a new chunk from the given atom list.
     
     @param atomList: List of atoms from which to create the chunk.
     @type  atomList: list
     
     @return: The new chunk.
     @rtype:  L{Chunk}
     
     @deprecated: use ops_rechunk.makeChunkFromAtomsList() instead.
     
     """
     if not atomList:
         print "bug in creating chunks from the given atom list"
         return
     
     newChunk = Chunk(self.win.assy, gensym("Chunk", self.win.assy))
     for a in atomList:            
         # leave the moved atoms picked, so still visible
         a.hopmol(newChunk)
     return newChunk   
Beispiel #29
0
    def buildChunk(self, assy):
        """
        Build Chunk for the cookies. First, combine bonds from
        all layers together, which may fuse some half bonds to full bonds.
        """
        from model.chunk import Chunk
        from model.chem import Atom
        from utilities.constants import gensym

        numLayers = len(self.bondLayers)
        if numLayers:
            allBonds = {}
            allCarbons = {}

            # Copy the bonds, carbons and hedron from the first layer
            for ii in range(numLayers):
                if self.bondLayers.has_key(ii):
                    for bKey, bValue in self.bondLayers[ii].items():
                        allBonds[bKey] = bValue

                    del self.bondLayers[ii]
                    break

            for carbons in self.carbonPosDict.values():
                for cKey, cValue in carbons.items():
                    allCarbons[cKey] = cValue

            for hedrons in self.hedroPosDict.values():
                for hKey, hValue in hedrons.items():
                    allCarbons[hKey] = hValue

            for bonds in self.bondLayers.values():
                for bKey, bValues in bonds.items():
                    if bKey in allBonds:
                        existValues = allBonds[bKey]
                        for bValue in bValues:
                            if type(bValue) == type((1, 1)):
                                if bValue[1]:
                                    ctValue = (bValue[0], 0)
                                else:
                                    ctValue = (bValue[0], 1)
                                if ctValue in existValues:
                                    idex = existValues.index(ctValue)
                                    existValues[idex] = bValue[0]
                                else:
                                    existValues += [bValue]
                            else:
                                existValues += [bValue]
                        allBonds[bKey] = existValues
                    else:
                        allBonds[bKey] = bValues

            # print "allbonds: ", allBonds
            # print "allCarbons: ", allCarbons

            carbonAtoms = {}
            mol = Chunk(assy, gensym("Crystal", assy))
            for bKey, bBonds in allBonds.items():
                keyHedron = True
                if len(bBonds):
                    for bond in bBonds:
                        if keyHedron:
                            if type(bBonds[0]) == type(1) or (not bBonds[0][1]):
                                if not bKey in carbonAtoms:
                                    keyAtom = Atom("C", allCarbons[bKey], mol)
                                    carbonAtoms[bKey] = keyAtom
                                else:
                                    keyAtom = carbonAtoms[bKey]
                                keyHedron = False

                        if keyHedron:
                            if type(bond) != type((1, 1)):
                                raise ValueError, (bKey, bond, bBonds)
                            else:
                                xp = (allCarbons[bKey] + allCarbons[bond[0]]) / 2.0
                                keyAtom = Atom("X", xp, mol)

                        if type(bond) == type(1) or bond[1]:
                            if type(bond) == type(1):
                                bvKey = bond
                            else:
                                bvKey = bond[0]
                            if not bvKey in carbonAtoms:
                                bondAtom = Atom("C", allCarbons[bvKey], mol)
                                carbonAtoms[bvKey] = bondAtom
                            else:
                                bondAtom = carbonAtoms[bvKey]
                        else:
                            xp = (allCarbons[bKey] + allCarbons[bond[0]]) / 2.0
                            bondAtom = Atom("X", xp, mol)

                        bond_atoms(keyAtom, bondAtom)

            if len(mol.atoms) > 0:
                # bruce 050222 comment: much of this is not needed, since mol.pick() does it.
                # Note: this method is similar to one in BuildCrystal_Command.py.
                assy.addmol(mol)
                assy.unpickall_in_GLPane()
                # was unpickparts; not sure _in_GLPane is best (or that
                # this is needed at all) [bruce 060721]
                mol.pick()
                assy.mt.mt_update()

        return  # from buildChunk
    def _createStructure(self):
        """
        Creates and returns the structure (in this case a L{Group} object that
        contains the nanotube chunk.
        @return : group containing the nanotube chunk.
        @rtype: L{Group}
        @note: This needs to return a CNT object once that model is implemented
        """

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
                # (can't reuse name in this case -- not sure what prefix it was
                #  made with)
            name = self.name

        # Create the model tree group node.
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # Nanotube group will be placed), if the topnode is not a group, make it
        # a 'Group' (applicable to Clipboard parts). See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()

        ntSegment = NanotubeSegment(self.name,
                                    self.win.assy,
                                    self.win.assy.part.topnode,
                                    editCommand = self)
        try:
            # Make the nanotube. <ntGroup> will contain one chunk:
            #  - Axis (Segment)
            # No error checking here; do all error checking in _gatherParameters().
            nanotube = self._gatherParameters()
            position = V(0.0, 0.0, 0.0)
            self.nanotube  =  nanotube  # needed for done msg #@
            ntChunk = nanotube.build(self.name, self.win.assy, position)

            ntSegment.addchild(ntChunk)

            #set some properties such as ntRise and its two endpoints.
            #This information will be stored on the NanotubeSegment object so that
            #it can be retrieved while editing this object.

            #WARNING 2008-03-05: Since self._modifyStructure calls
            #self._createStructure() If in the near future, we actually permit
            #modifying a
            #structure (such as a nanotube) without actually recreating the
            #entire structure, then the following properties must be set in
            #self._modifyStructure as well. Needs more thought.
            #props =(nanotube.getChirality(),
            #        nanotube.getType(),
            #        nanotube.getEndings(),
            #        nanotube.getEndPoints())

            ntSegment.setProps(nanotube.getParameters())

            return ntSegment

        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            self._segmentList.remove(ntSegment)
            ntSegment.kill_with_contents()
            raise PluginBug("Internal error while trying to create Nanotube.")
Beispiel #31
0
    def _modifyStructure(self, params):
        """
        Modify the structure based on the parameters specified. 
        Overrides EditCommand._modifystructure. This method removes the old 
        structure and creates a new one using self._createStructure. This 
        was needed for the structures like this (Dna, Nanotube etc) . .
        See more comments in the method.
        """        


        assert self.struct
        # parameters have changed, update existing structure
        self._revertNumber()


        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.assy) # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
                # (can't reuse name in this case -- not sure what prefix it was
                #  made with)
            name = self.name

        self.dna = B_Dna_PAM3_SingleStrand()

        numberOfBases, \
                     dnaForm, \
                     dnaModel, \
                     basesPerTurn, \
                     duplexRise, \
                     color_junk = params
        #see a note about color_junk in DnaSegment_EditCommand._modifyStructure()


        numberOfBasesToAddOrRemove =  self._determine_numberOfBases_to_change()

        if numberOfBasesToAddOrRemove != 0: 
            resizeEndStrandAtom, resizeEndAxisAtom = \
                               self.get_strand_and_axis_endAtoms_at_resize_end()

            if resizeEndAxisAtom:
                dnaSegment = resizeEndAxisAtom.molecule.parent_node_of_class(
                    self.assy.DnaSegment)

                resizeEnd_final_position = self._get_resizeEnd_final_position(
                    resizeEndAxisAtom, 
                    abs(numberOfBasesToAddOrRemove),
                    duplexRise )

                self.dna.modify(dnaSegment, 
                                resizeEndAxisAtom,
                                numberOfBasesToAddOrRemove, 
                                basesPerTurn, 
                                duplexRise,
                                resizeEndAxisAtom.posn(),
                                resizeEnd_final_position,
                                resizeEndStrandAtom = resizeEndStrandAtom
                            )                        

        return  
Beispiel #32
0
    def buildChunk(self, assy):
        """
        Build Chunk for the cookies. First, combine bonds from
        all layers together, which may fuse some half bonds to full bonds.
        """
        from model.chunk import Chunk
        from model.chem import Atom
        from utilities.constants import gensym

        numLayers = len(self.bondLayers)
        if numLayers:
            allBonds = {}
            allCarbons = {}

            #Copy the bonds, carbons and hedron from the first layer
            for ii in range(numLayers):
                if self.bondLayers.has_key(ii):
                    for bKey, bValue in self.bondLayers[ii].items():
                        allBonds[bKey] = bValue

                    del self.bondLayers[ii]
                    break

            for carbons in self.carbonPosDict.values():
                for cKey, cValue in carbons.items():
                    allCarbons[cKey] = cValue

            for hedrons in self.hedroPosDict.values():        
                for hKey, hValue in hedrons.items():
                    allCarbons[hKey] = hValue

            for bonds in self.bondLayers.values():
                for bKey, bValues in bonds.items():
                    if bKey in allBonds:
                        existValues = allBonds[bKey]
                        for bValue in bValues:
                            if type(bValue) == type((1, 1)):
                                if bValue[1]: 
                                    ctValue = (bValue[0], 0)
                                else: 
                                    ctValue = (bValue[0], 1)
                                if ctValue in existValues:
                                    idex = existValues.index(ctValue)
                                    existValues[idex] = bValue[0]
                                else:
                                    existValues += [bValue]
                            else: 
                                existValues += [bValue]
                        allBonds[bKey] = existValues
                    else: allBonds[bKey] = bValues

            #print "allbonds: ", allBonds
            #print "allCarbons: ", allCarbons

            carbonAtoms = {}
            mol = Chunk(assy, gensym("Crystal", assy))
            for bKey, bBonds in allBonds.items():
                keyHedron = True
                if len(bBonds):
                    for bond in bBonds:
                        if keyHedron:
                            if type(bBonds[0]) == type(1) or (not bBonds[0][1]):
                                if not bKey in carbonAtoms:
                                    keyAtom = Atom("C", allCarbons[bKey], mol) 
                                    carbonAtoms[bKey] = keyAtom
                                else:
                                    keyAtom = carbonAtoms[bKey]
                                keyHedron = False

                        if keyHedron:    
                            if type(bond) != type((1, 1)):
                                raise ValueError, (bKey, bond, bBonds)
                            else:
                                xp = (allCarbons[bKey] + allCarbons[bond[0]])/2.0
                                keyAtom = Atom("X", xp, mol)         

                        if type(bond) == type(1) or bond[1]:
                            if type(bond) == type(1):
                                bvKey = bond
                            else: 
                                bvKey = bond[0]
                            if not bvKey in carbonAtoms:
                                bondAtom = Atom("C", allCarbons[bvKey], mol) 
                                carbonAtoms[bvKey] = bondAtom
                            else: 
                                bondAtom = carbonAtoms[bvKey]
                        else:
                            xp = (allCarbons[bKey] + allCarbons[bond[0]])/2.0
                            bondAtom = Atom("X", xp, mol)     

                        bond_atoms(keyAtom, bondAtom)

            if len(mol.atoms) > 0:
                #bruce 050222 comment: much of this is not needed, since mol.pick() does it.
                # Note: this method is similar to one in BuildCrystal_Command.py.
                assy.addmol(mol)
                assy.unpickall_in_GLPane() 
                    # was unpickparts; not sure _in_GLPane is best (or that
                    # this is needed at all) [bruce 060721]
                mol.pick()
                assy.mt.mt_update()

        return # from buildChunk
    def _modifyStructure(self, params):
        """
        Modify the structure based on the parameters specified.
        Overrides EditCommand._modifystructure. This method removes the old
        structure and creates a new one using self._createStructure. This
        was needed for the structures like this (Dna, Nanotube etc) . .
        See more comments in the method.
        """

        #It could happen that the self.struct is killed before this method
        #is called. For example: Enter Edit Dna strand, select the strand, hit
        #delete and then hit Done to exit strand edit. Whenever you hit Done,
        #modify structure gets called (if old params don't match new ones)
        #so it needs to return safely if the structure was not valid due
        #to some previous operation
        if not self.hasValidStructure():
            return

        # parameters have changed, update existing structure
        self._revertNumber()


        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.assy) # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
                # (can't reuse name in this case -- not sure what prefix it was
                #  made with)
            name = self.name

        self.dna = B_Dna_PAM3_SingleStrand_Generator()

        numberOfBases, \
                     dnaForm, \
                     dnaModel, \
                     color_junk, \
                     name_junk = params
        #see a note about color_junk in DnaSegment_EditCommand._modifyStructure()

        numberOfBasesToAddOrRemove =  self._determine_numberOfBases_to_change()

        if numberOfBasesToAddOrRemove != 0:
            resizeEndStrandAtom, resizeEndAxisAtom = \
                               self.get_strand_and_axis_endAtoms_at_resize_end()

            if resizeEndAxisAtom:
                dnaSegment = resizeEndAxisAtom.molecule.parent_node_of_class(
                    self.assy.DnaSegment)

                if dnaSegment:
                    #A DnaStrand can have multiple DNA Segments with different
                    #basesPerTurn and duplexRise so make sure that while
                    #resizing the strand, use the dna segment of the
                    #resizeEndAxisAtom. Fixes bug 2922 - Ninad 2008-08-04
                    basesPerTurn = dnaSegment.getBasesPerTurn()
                    duplexRise = dnaSegment.getDuplexRise()

                    resizeEnd_final_position = self._get_resizeEnd_final_position(
                        resizeEndAxisAtom,
                        abs(numberOfBasesToAddOrRemove),
                        duplexRise )

                    self.dna.modify(dnaSegment,
                                    resizeEndAxisAtom,
                                    numberOfBasesToAddOrRemove,
                                    basesPerTurn,
                                    duplexRise,
                                    resizeEndAxisAtom.posn(),
                                    resizeEnd_final_position,
                                    resizeEndStrandAtom = resizeEndStrandAtom )

        return
Beispiel #34
0
    def cm_group(self): # bruce 050126 adding comments and changing behavior; 050420 permitting exactly one subtree
        """
        put the selected subtrees (one or more than one) into a new Group (and update)
        """
        ##e I wonder if option/alt/middleButton should be like a "force" or "power" flag
        # for cmenus; in this case, it would let this work even for a single element,
        # making a 1-item group. That idea can wait. [bruce 050126]
        #bruce 050420 making this work inside clipboard items too
        # TEST if assy.part updated in time ####@@@@ -- no, change to selgroup!
        self.deselect_partly_picked_whole_nodes()
        sg = self.assy.current_selgroup()
        node = sg.hindmost() # smallest nodetree containing all picked nodes 
        if not node:
            env.history.message("nothing selected to Group") # should never happen
            return
        if node.picked:
            #bruce 050420: permit this case whenever possible (formation of 1-item group);
            # cmenu constructor should disable or leave out the menu command when desired.
            if node != sg:
                assert node.dad # in fact, it'll be part of the same sg subtree (perhaps equal to sg)
                node = node.dad
                assert not node.picked
                # fall through -- general case below can handle this.
            else:
                # the picked item is the topnode of a selection group.
                # If it's the main part, we could make a new group inside it
                # containing all its children (0 or more). This can't happen yet
                # so I'll be lazy and save it for later.
                assert node != self.assy.tree
                # Otherwise it's a clipboard item. Let the Part take care of it
                # since it needs to patch up its topnode, choose the right name,
                # preserve its view attributes, etc.
                assert node.part.topnode == node
                newtop = node.part.create_new_toplevel_group()
                env.history.message("made new group %s" % newtop.name) ###k see if this looks ok with autogenerated name
                self.mt_update()
                return
        # (above 'if' might change node and then fall through to here)
        # node is an unpicked Group inside (or equal to) sg;
        # more than one of its children (or exactly one if we fell through from the node.picked case above)
        # are either picked or contain something picked (but maybe none of them are directly picked).
        # We'll make a new Group inside node, just before the first child containing
        # anything picked, and move all picked subtrees into it (preserving their order;
        # but losing their structure in terms of unpicked groups that contain some of them).
        ###e what do we do with the picked state of things we move? worry about the invariant! ####@@@@

        # make a new Group (inside node, same assy)
        ###e future: require all assys the same, or, do this once per topnode or assy-node.
        # for now: this will have bugs when done across topnodes!
        # so the caller doesn't let that happen, for now. [050126]
        new = Group(gensym("Group", node.assy), node.assy, node) # was self.assy
        assert not new.picked

        # put it where we want it -- before the first node member-tree with anything picked in it
        for m in node.members:
            if m.haspicked():
                assert m != new
                ## node.delmember(new) #e (addsibling ought to do this for us...) [now it does]
                m.addsibling(new, before = True)
                break # (this always happens, since something was picked under node)
        node.apply2picked(lambda(x): x.moveto(new))
            # this will have skipped new before moving anything picked into it!
            # even so, I'd feel better if it unpicked them before moving them...
            # but I guess it doesn't. for now, just see if it works this way... seems to work.
            # ... later [050316], it evidently does unpick them, or maybe delmember does.
        msg = fix_plurals("grouped %d item(s) into " % len(new.members)) + "%s" % new.name
        env.history.message( msg)

        # now, should we pick the new group so that glpane picked state has not changed?
        # or not, and then make sure to redraw as well? hmm...
        # - possibility 1: try picking the group, then see if anyone complains.
        # Caveat: future changes might cause glpane redraw to occur anyway, defeating the speed-purpose of this...
        # and as a UI feature I'm not sure what's better.
        # - possibility 2: don't pick it, do update glpane. This is consistent with Ungroup (for now)
        # and most other commands, so I'll do it.
        #
        # BTW, the prior code didn't pick the group
        # and orginally didn't unpick the members but now does, so it had a bug (failure to update
        # glpane to show new picked state), whose bug number I forget, which this should fix.
        # [bruce 050316]
        ## new.pick() # this will emit an undesirable history message... fix that?
        self.win.glpane.gl_update() #k needed? (e.g. for selection change? not sure.)
        self.mt_update()
        return
    def _createSegment(self):
        """
        Creates and returns the structure (in this case a L{Group} object that 
        contains the DNA strand and axis chunks. 
        @return : group containing that contains the DNA strand and axis chunks.
        @rtype: L{Group}  
        @note: This needs to return a DNA object once that model is implemented        
        """

        params = self._gatherParameters()

        # No error checking in build_struct, do all your error
        # checking in gather_parameters
        numberOfBases, \
                     dnaForm, \
                     dnaModel, \
                     basesPerTurn, \
                     duplexRise, \
                     endPoint1, \
                     endPoint2 = params

        if numberOfBases < 2:
            #Don't create a duplex with only one or 0 bases! 
            #Reset a few variables. This should be done by calling a separate 
            #method on command (there is similar code in self.createStructures)
            #as well. 
            self.mouseClickPoints = []
            self.graphicsMode.resetVariables()

            msg  = redmsg("Cannot preview/insert a DNA duplex with less than 2 base pairs.")
            self.propMgr.updateMessage(msg)

            self.dna = None # Fixes bug 2530. Mark 2007-09-02
            return None
        else: 
            msg = "Specify two points in the 3D Graphics Area to define the "\
                "endpoints of the DNA duplex"
            self.propMgr.updateMessage(msg)


        #If user enters the number of basepairs and hits preview i.e. endPoint1
        #and endPoint2 are not entered by the user and thus have default value 
        #of V(0, 0, 0), then enter the endPoint1 as V(0, 0, 0) and compute
        #endPoint2 using the duplex length. 
        #Do not use '==' equality check on vectors! its a bug. Use same_vals 
        # or Veq instead. 
        if Veq(endPoint1 , endPoint2) and Veq(endPoint1, V(0, 0, 0)):
            endPoint2 = endPoint1 + \
                      self.win.glpane.right * \
                      getDuplexLength('B-DNA', numberOfBases)

        if dnaForm == 'B-DNA':
            if dnaModel == 'PAM3':
                dna = B_Dna_PAM3()
            elif dnaModel == 'PAM5':
                dna = B_Dna_PAM5()
            else:
                print "bug: unknown dnaModel type: ", dnaModel
        else:
            raise PluginBug("Unsupported DNA Form: " + dnaForm)

        self.dna  =  dna  # needed for done msg

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
                # (can't reuse name in this case -- not sure what prefix it was
                #  made with)
            name = self.name


        # Create the model tree group node. 
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # DNa group will be placed), if the topnode is not a group, make it a
        # a 'Group' (applicable to Clipboard parts).See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()

        if self._parentDnaGroup is None:
            print_compact_stack("bug: Parent DnaGroup in DnaDuplex_EditCommand"\
                                "is None. This means the previous command "\
                                "was not 'BuildDna_EditCommand' Ignoring for now")
            if self._fallbackDnaGroup is None:
                self._createFallbackDnaGroup()

            dnaGroup = self._fallbackDnaGroup
        else:
            dnaGroup = self._parentDnaGroup


        dnaSegment = DnaSegment(self.name, 
                                self.win.assy,
                                dnaGroup,
                                editCommand = self  )
        try:
            # Make the DNA duplex. <dnaGroup> will contain three chunks:
            #  - Strand1
            #  - Strand2
            #  - Axis
            dna.make(dnaSegment, 
                     numberOfBases, 
                     basesPerTurn, 
                     duplexRise,
                     endPoint1,
                     endPoint2)

            #set some properties such as duplexRise and number of bases per turn
            #This information will be stored on the DnaSegment object so that
            #it can be retrieved while editing this object. 
            #This works with or without dna_updater. Now the question is 
            #should these props be assigned to the DnaSegment in 
            #dnaDuplex.make() itself ? This needs to be answered while modifying
            #make() method to fit in the dna data model. --Ninad 2008-03-05

            #WARNING 2008-03-05: Since self._modifyStructure calls 
            #self._createStructure() (which in turn calls self._createSegment() 
            #in this case) If in the near future, we actually permit modifying a
            #structure (such as dna) without actually recreating the whole 
            #structre, then the following properties must be set in 
            #self._modifyStructure as well. Needs more thought.
            props = (duplexRise, basesPerTurn)

            dnaSegment.setProps(props)

            return dnaSegment


        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            dnaSegment.kill_with_contents()
            raise PluginBug("Internal error while trying to create DNA duplex.")
Beispiel #36
0
    def modifySeparate(self, new_old_callback=None):
        """
        For each Chunk (named N) containing any selected atoms,
        move the selected atoms out of N (but without breaking any bonds)
        into a new Chunk which we name N-frag. If N is now empty, remove it.
        
        @param new_old_callback: If provided, then each time we create a new
            (and nonempty) fragment N-frag, call new_old_callback with the
            2 args N-frag and N (that is, with the new and old molecules).
        @type  new_old_callback: function
        
        @warning: we pass the old mol N to that callback, even if it has no 
                  atoms and we deleted it from this assembly.
        """
        # bruce 040929 wrote or revised docstring, added new_old_callback feature
        # for use from Extrude.
        # Note that this is called both from a tool button and for internal uses.
        # bruce 041222 removed side effect on selection mode, after discussion
        # with Mark and Josh. Also added some status messages.
        # Questions: is it good to refrain from merging all moved atoms into one
        # new mol? If not, then if N becomes empty, should we rename N-frag to N?

        cmd = greenmsg("Separate: ")

        if not self.selatoms:  # optimization, and different status msg
            msg = redmsg("No atoms selected")
            env.history.message(cmd + msg)
            return
        if 1:
            #bruce 060313 mitigate bug 1627, or "fix it by doing something we'd rather not always have to do" --
            # create (if necessary) a new toplevel group right now (before addmol does), avoiding a traceback
            # when all atoms in a clipboard item part consisting of a single chunk are selected for this op,
            # and the old part.topnode (that chunk) disappears from loss of atoms before we add the newly made chunk
            # containing those same atoms.
            # The only things wrong with this fix are:
            # - It's inefficient (so is the main algorithm, and it'd be easy to rewrite it to be faster, as explained below).
            # - The user ends up with a new Group even if one would theoretically not have been needed.
            #   But that's better than a traceback and disabled session, so for A7 this fix is fine.
            # - The same problem might arise in other situations (though I don't know of any), so ideally we'd
            #   have a more general fix.
            # - It's nonmodular for this function to have to know anything about Parts.
            ##e btw, a simpler way to do part of the following is "part = self". should revise this when time to test it. [bruce 060329]
            someatom = self.selatoms.values(
            )[0]  # if atoms in multiple parts could be selected, we'd need this for all their mols
            part = someatom.molecule.part
            part.ensure_toplevel_group()
            # this is all a kluge; a better way would be to rewrite the main algorithm to find the mols
            # with selected atoms, only make numol for those, and add it (addmol) before transferring all the atoms to it.
            pass
        numolist = []
        for mol in self.molecules[:]:  # new mols are added during the loop!
            numol = Chunk(self.assy, gensym(mol.name + "-frag",
                                            self.assy))  # (in modifySeparate)
            for a in mol.atoms.values():
                if a.picked:
                    # leave the moved atoms picked, so still visible
                    a.hopmol(numol)
            if numol.atoms:
                numol.setDisplayStyle(
                    mol.display)  # Fixed bug 391.  Mark 050710
                numol.setcolor(mol.color, repaint_in_MT=False)
                #bruce 070425, fix Extrude bug 2331 (also good for Separate in general), "nice to have" for A9
                self.addmol(
                    numol
                )  ###e move it to just after the one it was made from? or, end of same group??
                numolist += [numol]
                if new_old_callback:
                    new_old_callback(numol, mol)  # new feature 040929
        msg = fix_plurals("Created %d new chunk(s)" % len(numolist))
        env.history.message(cmd + msg)
        self.w.win_update()  #e do this in callers instead?
Beispiel #37
0
    def makeStrandChunkFromBrokenStrand(self, x1, x2):  # by Mark
        """
        Makes a new strand chunk using the two singlets just created by
        busting the original strand, which is now broken. If the original
        strand was a ring, no new chunk is created.
        
        The new strand chunk, which includes the atoms between the 3' end of
        the original strand and the new 5' end (i.e. the break point), is 
        added to the same DNA group as the original strand and assigned a 
        different color.
        
        @param x1: The first of two singlets created by busting a strand
                   backbone bond. It is either the 3' or 5' open bond singlet,
                   but we don't know yet.
        @type  x1: L{Atom}
        
        @param x2: The second of two singlets created by busting a backbone
                   backbone bond. It is either the 3' or 5' open bond singlet,
                   but we don't know yet.
        @type  x2: L{Atom}
        
        @return: The new strand chunk. Returns B{None} if no new strand chunk
                 is created, as is the case of a ring.
        @rtype:  L{Chunk}
        """
        minimize = debug_pref(
            "Adjust broken strand bondpoints using minimizer?",
            #bruce 080415 revised text (to not use the developer-
            # jargon-only term "singlet"), changed prefs_key,
            # and removed non_debug = True, for .rc2 release,
            # since the repositioning bug this worked around
            # is now fixed.
            Choice_boolean_False,
            prefs_key=True,
        )

        _five_prime_atom = None
        _three_prime_atom = None

        for singlet in (x1, x2):
            adjustSinglet(singlet, minimize=minimize)
            open_bond = singlet.bonds[0]
            if open_bond.isFivePrimeOpenBond():
                _five_prime_atom = open_bond.other(singlet)
            else:
                _three_prime_atom = open_bond.other(singlet)

        # Make sure we have exactly one 3' and one 5' singlet.
        # If not, there is probably a direction error on the open bond(s)
        # that x1 and/or x2 are members of.
        if not _five_prime_atom:
            print_compact_stack("No 5' bondpoint.")
            return None
        if not _three_prime_atom:
            print_compact_stack("No 3' bondpoint.")
            return None

        atomList = self.o.assy.getConnectedAtoms([_five_prime_atom])

        if _three_prime_atom in atomList:
            # The strand was a closed loop strand, so we're done.
            return None  # Since no new chunk was created.

        # See self.ensure_toplevel_group() docstring for explanation.
        self.ensure_toplevel_group()
        _group_five_prime_was_in = _five_prime_atom.molecule.dad
        if env.prefs[assignColorToBrokenDnaStrands_prefs_key]:
            _new_strand_color = getNextStrandColor(
                _five_prime_atom.molecule.color)
        else:
            _new_strand_color = _five_prime_atom.molecule.color
        return self.makeChunkFromAtomList(
            atomList,
            group=_group_five_prime_was_in,
            name=gensym("Strand"),
            # doesn't need "DnaStrand" or self.assy,
            # since not normally seen by users
            # [bruce 080407 comment]
            color=_new_strand_color)
Beispiel #38
0
    def cm_group(
        self
    ):  # bruce 050126 adding comments and changing behavior; 050420 permitting exactly one subtree
        """
        put the selected subtrees (one or more than one) into a new Group (and update)
        """
        ##e I wonder if option/alt/middleButton should be like a "force" or "power" flag
        # for cmenus; in this case, it would let this work even for a single element,
        # making a 1-item group. That idea can wait. [bruce 050126]
        #bruce 050420 making this work inside clipboard items too
        # TEST if assy.part updated in time ####@@@@ -- no, change to selgroup!
        self.deselect_partly_picked_whole_nodes()
        sg = self.assy.current_selgroup()
        node = sg.hindmost()  # smallest nodetree containing all picked nodes
        if not node:
            env.history.message(
                "nothing selected to Group")  # should never happen
            return
        if node.picked:
            #bruce 050420: permit this case whenever possible (formation of 1-item group);
            # cmenu constructor should disable or leave out the menu command when desired.
            if node != sg:
                assert node.dad  # in fact, it'll be part of the same sg subtree (perhaps equal to sg)
                node = node.dad
                assert not node.picked
                # fall through -- general case below can handle this.
            else:
                # the picked item is the topnode of a selection group.
                # If it's the main part, we could make a new group inside it
                # containing all its children (0 or more). This can't happen yet
                # so I'll be lazy and save it for later.
                assert node != self.assy.tree
                # Otherwise it's a clipboard item. Let the Part take care of it
                # since it needs to patch up its topnode, choose the right name,
                # preserve its view attributes, etc.
                assert node.part.topnode == node
                newtop = node.part.create_new_toplevel_group()
                env.history.message(
                    "made new group %s" % newtop.name
                )  ###k see if this looks ok with autogenerated name
                self.mt_update()
                return
        # (above 'if' might change node and then fall through to here)
        # node is an unpicked Group inside (or equal to) sg;
        # more than one of its children (or exactly one if we fell through from the node.picked case above)
        # are either picked or contain something picked (but maybe none of them are directly picked).
        # We'll make a new Group inside node, just before the first child containing
        # anything picked, and move all picked subtrees into it (preserving their order;
        # but losing their structure in terms of unpicked groups that contain some of them).
        ###e what do we do with the picked state of things we move? worry about the invariant! ####@@@@

        # make a new Group (inside node, same assy)
        ###e future: require all assys the same, or, do this once per topnode or assy-node.
        # for now: this will have bugs when done across topnodes!
        # so the caller doesn't let that happen, for now. [050126]
        new = Group(gensym("Group", node.assy), node.assy,
                    node)  # was self.assy
        assert not new.picked

        # put it where we want it -- before the first node member-tree with anything picked in it
        for m in node.members:
            if m.haspicked():
                assert m != new
                ## node.delmember(new) #e (addsibling ought to do this for us...) [now it does]
                m.addsibling(new, before=True)
                break  # (this always happens, since something was picked under node)
        node.apply2picked(lambda (x): x.moveto(new))
        # this will have skipped new before moving anything picked into it!
        # even so, I'd feel better if it unpicked them before moving them...
        # but I guess it doesn't. for now, just see if it works this way... seems to work.
        # ... later [050316], it evidently does unpick them, or maybe delmember does.
        msg = fix_plurals(
            "grouped %d item(s) into " % len(new.members)) + "%s" % new.name
        env.history.message(msg)

        # now, should we pick the new group so that glpane picked state has not changed?
        # or not, and then make sure to redraw as well? hmm...
        # - possibility 1: try picking the group, then see if anyone complains.
        # Caveat: future changes might cause glpane redraw to occur anyway, defeating the speed-purpose of this...
        # and as a UI feature I'm not sure what's better.
        # - possibility 2: don't pick it, do update glpane. This is consistent with Ungroup (for now)
        # and most other commands, so I'll do it.
        #
        # BTW, the prior code didn't pick the group
        # and orginally didn't unpick the members but now does, so it had a bug (failure to update
        # glpane to show new picked state), whose bug number I forget, which this should fix.
        # [bruce 050316]
        ## new.pick() # this will emit an undesirable history message... fix that?
        self.win.glpane.gl_update(
        )  #k needed? (e.g. for selection change? not sure.)
        self.mt_update()
        return
    def _createStructure(self):
        """
        Creates and returns the structure (in this case a L{NanotubeSegment} 
        object. 
        @return : Nanotube segment that include the nanotube chunk.
        @rtype: L{NanotubeSegment}        
        """
        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.win.assy) # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
                # (can't reuse name in this case -- not sure what prefix it was
                #  made with)
            name = self.name


        # Create the model tree group node. 
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # DNa group will be placed), if the topnode is not a group, make it a
        # a 'Group' (applicable to Clipboard parts).See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()
        ntSegment = NanotubeSegment(self.name, 
                                    self.win.assy,
                                    self.win.assy.part.topnode,
                                    editCommand = self  )
        try:
            # Make the NanotubeSegment.

            n, m, type, endings, endPoint1, endPoint2 = self._gatherParameters()

            from cnt.model.Nanotube import Nanotube
            self.nanotube = Nanotube()
            nanotube  =  self.nanotube
            nanotube.setChirality(n, m)
            nanotube.setType(type)
            nanotube.setEndings(endings)
            nanotube.setEndPoints(endPoint1, endPoint2)
            position = V(0.0, 0.0, 0.0)
            ntChunk = nanotube.build(self.name, self.win.assy, position)

            nanotube.computeEndPointsFromChunk(ntChunk)

            ntSegment.addchild(ntChunk)

            #set some properties such as nanotubeRise
            #This information will be stored on the NanotubeSegment object so that
            #it can be retrieved while editing this object. 
            #Should these props be assigned to the NanotubeSegment in 
            #Nanotube.build() itself? This needs to be answered while modifying
            #build() method to fit in the dna data model. --Ninad 2008-03-05

            #WARNING 2008-03-05: Since self._modifyStructure calls 
            #self._createStructure() 
            #If in the near future, we actually permit modifying a
            #structure (such as dna) without actually recreating the whole 
            #structure, then the following properties must be set in 
            #self._modifyStructure as well. Needs more thought.
            props =(nanotube.getChirality(),
                    nanotube.getType(),
                    nanotube.getEndings(),
                    nanotube.getEndPoints())

            ntSegment.setProps(props)

            return ntSegment

        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            ntSegment.kill()
            raise PluginBug("Internal error while trying to create a NanotubeSegment.")
        return
Beispiel #40
0
    def _modifyStructure(self, params):
        """
        Modify the structure based on the parameters specified. 
        Overrides EditCommand._modifystructure. This method removes the old 
        structure and creates a new one using self._createStructure. This 
        was needed for the structures like this (Dna, Nanotube etc) . .
        See more comments in the method.
        """

        #It could happen that the self.struct is killed before this method
        #is called. For example: Enter Edit Dna strand, select the strand, hit
        #delete and then hit Done to exit strand edit. Whenever you hit Done,
        #modify structure gets called (if old params don't match new ones)
        #so it needs to return safely if the structure was not valid due
        #to some previous operation
        if not self.hasValidStructure():
            return

        # parameters have changed, update existing structure
        self._revertNumber()

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix,
                                      self.assy)  # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
            # (can't reuse name in this case -- not sure what prefix it was
            #  made with)
            name = self.name

        self.dna = B_Dna_PAM3_SingleStrand_Generator()

        numberOfBases, \
                     dnaForm, \
                     dnaModel, \
                     color_junk, \
                     name_junk = params
        #see a note about color_junk in DnaSegment_EditCommand._modifyStructure()

        numberOfBasesToAddOrRemove = self._determine_numberOfBases_to_change()

        if numberOfBasesToAddOrRemove != 0:
            resizeEndStrandAtom, resizeEndAxisAtom = \
                               self.get_strand_and_axis_endAtoms_at_resize_end()

            if resizeEndAxisAtom:
                dnaSegment = resizeEndAxisAtom.molecule.parent_node_of_class(
                    self.assy.DnaSegment)

                if dnaSegment:
                    #A DnaStrand can have multiple DNA Segments with different
                    #basesPerTurn and duplexRise so make sure that while
                    #resizing the strand, use the dna segment of the
                    #resizeEndAxisAtom. Fixes bug 2922 - Ninad 2008-08-04
                    basesPerTurn = dnaSegment.getBasesPerTurn()
                    duplexRise = dnaSegment.getDuplexRise()

                    resizeEnd_final_position = self._get_resizeEnd_final_position(
                        resizeEndAxisAtom, abs(numberOfBasesToAddOrRemove),
                        duplexRise)

                    self.dna.modify(dnaSegment,
                                    resizeEndAxisAtom,
                                    numberOfBasesToAddOrRemove,
                                    basesPerTurn,
                                    duplexRise,
                                    resizeEndAxisAtom.posn(),
                                    resizeEnd_final_position,
                                    resizeEndStrandAtom=resizeEndStrandAtom)

        return
    def _depositLibraryPart(self, newPart, hotspotAtom, atom_or_pos):
        # probably by Huaicai; revised by bruce 051227, 060627, 070501
        """
        This method serves as an overloaded method, <atom_or_pos> is
        the Singlet atom or the empty position that the new part <newPart>
        [which is an assy, at least sometimes] will be attached to or placed at.
        [If <atom_or_pos> is a singlet, <hotspotAtom> should be an atom in some
        chunk in <newPart>.]
        Currently, it doesn't consider group or jigs in the <newPart>.
        Not so sure if my attempt to copy a part into another assembly is all
        right. [It wasn't, so bruce 051227 revised it.]
        Copies all molecules in the <newPart>, change their assy attribute to
        current assembly, move them into <pos>.
        [bruce 051227 new feature:] return a list of new nodes created, and a
        message for history (currently almost a stub).
        [not sure if subrs ever print history messages...
        if they do we'd want to return those instead.]
        """

        attach2Bond = False
        stuff = []  # list of deposited nodes [bruce 051227 new feature]

        if isinstance(atom_or_pos, Atom):
            attch2Singlet = atom_or_pos
            if hotspotAtom and hotspotAtom.is_singlet() and attch2Singlet.is_singlet():

                newMol = hotspotAtom.molecule.copy_single_chunk(None)
                # [this can break interchunk bonds,
                #  thus it still has bug 2028]
                newMol.set_assy(self.o.assy)
                hs = newMol.hotspot
                ha = hs.singlet_neighbor()  # hotspot neighbor atom
                attch2Atom = attch2Singlet.singlet_neighbor()  # attach to atom

                rotCenter = newMol.center
                rotOffset = Q(ha.posn() - hs.posn(), attch2Singlet.posn() - attch2Atom.posn())
                newMol.rot(rotOffset)

                moveOffset = attch2Singlet.posn() - hs.posn()
                newMol.move(moveOffset)

                self.graphicsMode._createBond(hs, ha, attch2Singlet, attch2Atom)

                self.o.assy.addmol(newMol)
                stuff.append(newMol)

                # e if there are other chunks in <newPart>,
                # they are apparently copied below. [bruce 060627 comment]

            else:  ## something is wrong, do nothing
                return stuff, "internal error"
            attach2Bond = True
        else:
            placedPos = atom_or_pos
            if hotspotAtom:
                hotspotAtomPos = hotspotAtom.posn()
                moveOffset = placedPos - hotspotAtomPos
            else:
                if newPart.molecules:
                    moveOffset = placedPos - newPart.molecules[0].center  # e not
                    # the best choice of center [bruce 060627 comment]

        if attach2Bond:  # Connect part to a bondpoint of an existing chunk
            for m in newPart.molecules:
                if not m is hotspotAtom.molecule:
                    newMol = m.copy_single_chunk(None)
                    # [this can break interchunk bonds,
                    #  thus it still has bug 2028]
                    newMol.set_assy(self.o.assy)

                    ## Get each of all other chunks' center movement for the
                    ## rotation around 'rotCenter'
                    coff = rotOffset.rot(newMol.center - rotCenter)
                    coff = rotCenter - newMol.center + coff

                    # The order of the following 2 statements doesn't matter
                    newMol.rot(rotOffset)
                    newMol.move(moveOffset + coff)

                    self.o.assy.addmol(newMol)
                    stuff.append(newMol)
        else:  # Behaves like dropping a part anywhere you specify, independent
            # of existing chunks.
            # copy all nodes in newPart (except those in clipboard items),
            # regardless of node classes;
            # put it in a new Group if more than one thing [bruce 070501]
            # [TODO: this should be done in the cases above, too, but that's
            # not yet implemented,
            #  and requires adding rot or pivot to the Node API and revising
            #  the rot-calling code above,
            #  and also reviewing the definition of the "hotspot of a Part" and
            # maybe of a "depositable clipboard item".]
            assert newPart.tree.is_group()
            nodes = list(newPart.tree.members)  # might be []
            assy = self.o.assy
            newnodes = copied_nodes_for_DND(nodes, autogroup_at_top=True, assy=assy)
            # Note: that calls name_autogrouped_nodes_for_clipboard
            # internally, if it forms a Group,
            # but we ignore that and rename the new node differently below,
            # whether or not it was autogrouped. We could just as well do
            # the autogrouping ourselves...
            # Note [bruce 070525]: it's better to call copied_nodes_for_DND
            # here than copy_nodes_in_order, even if we didn't need to
            # autogroup. One reason is that if some node is not copied,
            # that's not necessarily an error, since we don't care about 1-1
            # orig-copy correspondence here.
            if not newnodes:
                if newnodes is None:
                    print "bug: newnodes should not be None; nodes was %r (saved in debug._bugnodes)" % (nodes,)
                    # TODO: This might be possible, for arbitrary partlib
                    # contents, just not for legitimate ones...
                    # but partlib will probably be (or is) user-expandable,
                    # so we should turn this into history message,
                    # not a bug print. But I'm not positive it's possible
                    # w/o a bug, so review first. ###FIX [bruce 070501 comment]
                    import utilities.debug as debug

                    debug._bugnodes = nodes
                    newnodes = []
                msg = redmsg("error: nothing to deposit in [%s]" % quote_html(str(newPart.name)))
                return [], msg
            assert len(newnodes) == 1  # due to autogroup_at_top = True
            # but the remaining code works fine regardless of len(newnodes),
            # in case we make autogroup a preference
            for newnode in newnodes:
                # Rename newnode based on the partlib name and a unique number.
                # It seems best to let the partlib mmp file contents (not just
                # filename)
                # control the name used here, so use newPart.tree.name rather
                # than just newPart.name.
                # (newPart.name is a complete file pathname; newPart.tree.name
                # is usually its basename w/o extension.)
                basename = str(newPart.tree.name)
                if basename == "Untitled":
                    # kluge, for the sake of 3 current partlib files, and files
                    # saved only once by users (due to NE1 bug in save)
                    dirjunk, base = os.path.split(newPart.name)
                    basename, extjunk = os.path.splitext(base)
                from utilities.constants import gensym

                newnode.name = gensym(basename, assy)  # name library part
                # bruce 080407 basename + " " --> basename, and pass assy
                # (per Mark NFR desire)
                # based on basename recorded in its mmp file's top node
                newnode.move(moveOffset)  # k not sure this method is correctly
                # implemented for measurement jigs, named views
                assy.addnode(newnode)
                stuff.append(newnode)

        ##            #bruce 060627 new code: fix bug 2028 (non-hotspot case only)
        ##            # about interchunk bonds being broken
        ##            nodes = newPart.molecules
        ##            newnodes = copied_nodes_for_DND(nodes)
        ##            if newnodes is None:
        ##                print "bug: newnodes should not be None; nodes was %r (saved in debug._bugnodes)" % (nodes,)
        ##                debug._bugnodes = nodes
        ##                newnodes = [] # kluge
        ##            for newMol in newnodes:
        ##                # some of the following probably only work for Chunks,
        ##                # though coding them for other nodes would not be hard
        ##                newMol.set_assy(self.o.assy)
        ##                newMol.move(moveOffset)
        ##                self.o.assy.addmol(newMol)
        ##                stuff.append(newMol)

        self.o.assy.update_parts()  # bruce 051227 see if this fixes the
        # atom_debug exception in checkparts

        msg = (
            greenmsg("Deposited library part: ") + " [" + quote_html(str(newPart.name)) + "]"
        )  # ninad060924 fix bug 1164

        return stuff, msg  ####@@@@ should revise this message
    def _createStructure(self):
        """
        Creates and returns the structure (in this case a L{Group} object that 
        contains the DNA strand and axis chunks. 
        @return : group containing that contains the DNA strand and axis chunks.
        @rtype: L{Group}  
        @note: This needs to return a DNA object once that model is implemented        
        """

        params = self._gatherParameters()

        # No error checking in build_struct, do all your error
        # checking in gather_parameters
        number_of_basePairs_from_struct, numberOfBases, dnaForm, dnaModel, basesPerTurn, duplexRise, endPoint1, endPoint2, color_junk = (
            params
        )
        # Note: color_junk is not used. Ideally it should do struct.setColor(color)
        # but the color combobox in the PM directly sets the color of the
        # structure to the specified one when the current index in the combobx
        # changes

        # If user enters the number of basepairs and hits preview i.e. endPoint1
        # and endPoint2 are not entered by the user and thus have default value
        # of V(0, 0, 0), then enter the endPoint1 as V(0, 0, 0) and compute
        # endPoint2 using the duplex length.
        # Do not use '==' equality check on vectors! its a bug. Use same_vals
        # or Veq instead.
        if Veq(endPoint1, endPoint2) and Veq(endPoint1, V(0, 0, 0)):
            endPoint2 = endPoint1 + self.win.glpane.right * getDuplexLength("B-DNA", numberOfBases)

        if numberOfBases < 1:
            msg = redmsg("Cannot preview/insert a DNA duplex with 0 bases.")
            self.propMgr.updateMessage(msg)
            self.dna = None  # Fixes bug 2530. Mark 2007-09-02
            return None

        if dnaForm == "B-DNA":
            if dnaModel == "PAM3":
                dna = B_Dna_PAM3_Generator()
            elif dnaModel == "PAM5":
                dna = B_Dna_PAM5_Generator()
            else:
                print "bug: unknown dnaModel type: ", dnaModel
        else:
            raise PluginBug("Unsupported DNA Form: " + dnaForm)

        self.dna = dna  # needed for done msg

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix, self.win.assy)  # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
            # (can't reuse name in this case -- not sure what prefix it was
            #  made with)
            name = self.name

        # Create the model tree group node.
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # DNa group will be placed), if the topnode is not a group, make it a
        # a 'Group' (applicable to Clipboard parts).See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()
        dnaSegment = DnaSegment(self.name, self.win.assy, self.win.assy.part.topnode, editCommand=self)
        try:
            # Make the DNA duplex. <dnaGroup> will contain three chunks:
            #  - Strand1
            #  - Strand2
            #  - Axis

            dna.make(dnaSegment, numberOfBases, basesPerTurn, duplexRise, endPoint1, endPoint2)

            # set some properties such as duplexRise and number of bases per turn
            # This information will be stored on the DnaSegment object so that
            # it can be retrieved while editing this object.
            # This works with or without dna_updater. Now the question is
            # should these props be assigned to the DnaSegment in
            # dnaDuplex.make() itself ? This needs to be answered while modifying
            # make() method to fit in the dna data model. --Ninad 2008-03-05

            # WARNING 2008-03-05: Since self._modifyStructure calls
            # self._createStructure()
            # If in the near future, we actually permit modifying a
            # structure (such as dna) without actually recreating the whole
            # structre, then the following properties must be set in
            # self._modifyStructure as well. Needs more thought.
            props = (duplexRise, basesPerTurn)
            dnaSegment.setProps(props)

            return dnaSegment

        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            dnaSegment.kill()
            raise PluginBug("Internal error while trying to create DNA duplex.")
    def modifySeparate(self, new_old_callback = None):
        """
        For each Chunk (named N) containing any selected atoms,
        move the selected atoms out of N (but without breaking any bonds)
        into a new Chunk which we name N-frag. If N is now empty, remove it.

        @param new_old_callback: If provided, then each time we create a new
            (and nonempty) fragment N-frag, call new_old_callback with the
            2 args N-frag and N (that is, with the new and old molecules).
        @type  new_old_callback: function

        @warning: we pass the old mol N to that callback, even if it has no
                  atoms and we deleted it from this assembly.
        """
        # bruce 040929 wrote or revised docstring, added new_old_callback feature
        # for use from Extrude.
        # Note that this is called both from a tool button and for internal uses.
        # bruce 041222 removed side effect on selection mode, after discussion
        # with Mark and Josh. Also added some status messages.
        # Questions: is it good to refrain from merging all moved atoms into one
        # new mol? If not, then if N becomes empty, should we rename N-frag to N?

        cmd = greenmsg("Separate: ")

        if not self.selatoms: # optimization, and different status msg
            msg =  redmsg("No atoms selected")
            env.history.message(cmd + msg)
            return
        if 1:
            #bruce 060313 mitigate bug 1627, or "fix it by doing something we'd rather not always have to do" --
            # create (if necessary) a new toplevel group right now (before addmol does), avoiding a traceback
            # when all atoms in a clipboard item part consisting of a single chunk are selected for this op,
            # and the old part.topnode (that chunk) disappears from loss of atoms before we add the newly made chunk
            # containing those same atoms.
            # The only things wrong with this fix are:
            # - It's inefficient (so is the main algorithm, and it'd be easy to rewrite it to be faster, as explained below).
            # - The user ends up with a new Group even if one would theoretically not have been needed.
            #   But that's better than a traceback and disabled session, so for A7 this fix is fine.
            # - The same problem might arise in other situations (though I don't know of any), so ideally we'd
            #   have a more general fix.
            # - It's nonmodular for this function to have to know anything about Parts.
            ##e btw, a simpler way to do part of the following is "part = self". should revise this when time to test it. [bruce 060329]
            someatom = self.selatoms.values()[0] # if atoms in multiple parts could be selected, we'd need this for all their mols
            part = someatom.molecule.part
            part.ensure_toplevel_group()
            # this is all a kluge; a better way would be to rewrite the main algorithm to find the mols
            # with selected atoms, only make numol for those, and add it (addmol) before transferring all the atoms to it.
            pass
        numolist=[]
        for mol in self.molecules[:]: # new mols are added during the loop!
            numol = Chunk(self.assy, gensym(mol.name + "-frag", self.assy)) # (in modifySeparate)
            for a in mol.atoms.values():
                if a.picked:
                    # leave the moved atoms picked, so still visible
                    a.hopmol(numol)
            if numol.atoms:
                numol.setDisplayStyle(mol.display) # Fixed bug 391.  Mark 050710
                numol.setcolor(mol.color, repaint_in_MT = False)
                    #bruce 070425, fix Extrude bug 2331 (also good for Separate in general), "nice to have" for A9
                self.addmol(numol) ###e move it to just after the one it was made from? or, end of same group??
                numolist+=[numol]
                if new_old_callback:
                    new_old_callback(numol, mol) # new feature 040929
        msg = fix_plurals("Created %d new chunk(s)" % len(numolist))
        env.history.message(cmd + msg)
        self.w.win_update() #e do this in callers instead?
    def _createStructure(self):
        """
        Creates and returns the structure (in this case a L{Group} object that
        contains the nanotube chunk.
        @return : group containing the nanotube chunk.
        @rtype: L{Group}
        @note: This needs to return a CNT object once that model is implemented
        """

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix,
                                      self.win.assy)  # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
            # (can't reuse name in this case -- not sure what prefix it was
            #  made with)
            name = self.name

        # Create the model tree group node.
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # Nanotube group will be placed), if the topnode is not a group, make it
        # a 'Group' (applicable to Clipboard parts). See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()

        ntSegment = NanotubeSegment(self.name,
                                    self.win.assy,
                                    self.win.assy.part.topnode,
                                    editCommand=self)
        try:
            # Make the nanotube. <ntGroup> will contain one chunk:
            #  - Axis (Segment)
            # No error checking here; do all error checking in _gatherParameters().
            nanotube = self._gatherParameters()
            position = V(0.0, 0.0, 0.0)
            self.nanotube = nanotube  # needed for done msg #@
            ntChunk = nanotube.build(self.name, self.win.assy, position)

            ntSegment.addchild(ntChunk)

            #set some properties such as ntRise and its two endpoints.
            #This information will be stored on the NanotubeSegment object so that
            #it can be retrieved while editing this object.

            #WARNING 2008-03-05: Since self._modifyStructure calls
            #self._createStructure() If in the near future, we actually permit
            #modifying a
            #structure (such as a nanotube) without actually recreating the
            #entire structure, then the following properties must be set in
            #self._modifyStructure as well. Needs more thought.
            #props =(nanotube.getChirality(),
            #        nanotube.getType(),
            #        nanotube.getEndings(),
            #        nanotube.getEndPoints())

            ntSegment.setProps(nanotube.getParameters())

            return ntSegment

        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            self._segmentList.remove(ntSegment)
            ntSegment.kill_with_contents()
            raise PluginBug("Internal error while trying to create Nanotube.")
Beispiel #45
0
    def _depositLibraryPart(self, newPart, hotspotAtom, atom_or_pos): 
        # probably by Huaicai; revised by bruce 051227, 060627, 070501
        """
        This method serves as an overloaded method, <atom_or_pos> is 
        the Singlet atom or the empty position that the new part <newPart>
        [which is an assy, at least sometimes] will be attached to or placed at.
        [If <atom_or_pos> is a singlet, <hotspotAtom> should be an atom in some 
        chunk in <newPart>.]
        Currently, it doesn't consider group or jigs in the <newPart>. 
        Not so sure if my attempt to copy a part into another assembly is all
        right. [It wasn't, so bruce 051227 revised it.]
        Copies all molecules in the <newPart>, change their assy attribute to 
        current assembly, move them into <pos>.
        [bruce 051227 new feature:] return a list of new nodes created, and a 
        message for history (currently almost a stub).
        [not sure if subrs ever print history messages...
        if they do we'd want to return those instead.]
        """

        attach2Bond = False
        stuff = [] # list of deposited nodes [bruce 051227 new feature]

        if isinstance(atom_or_pos, Atom):
            attch2Singlet = atom_or_pos
            if hotspotAtom and hotspotAtom.is_singlet() and \
               attch2Singlet .is_singlet():

                newMol = hotspotAtom.molecule.copy_single_chunk(None)
                    # [this can break interchunk bonds,
                    #  thus it still has bug 2028]
                newMol.setAssy(self.o.assy)
                hs = newMol.hotspot
                ha = hs.singlet_neighbor() # hotspot neighbor atom
                attch2Atom = attch2Singlet.singlet_neighbor() # attach to atom

                rotCenter = newMol.center
                rotOffset = Q(ha.posn()-hs.posn(), 
                              attch2Singlet.posn()-attch2Atom.posn())
                newMol.rot(rotOffset)

                moveOffset = attch2Singlet.posn() - hs.posn()
                newMol.move(moveOffset)
    
                self.graphicsMode._createBond(hs, ha, attch2Singlet, attch2Atom)

                self.o.assy.addmol(newMol)
                stuff.append(newMol)

                #e if there are other chunks in <newPart>, 
                #they are apparently copied below. [bruce 060627 comment]

            else: ## something is wrong, do nothing
                return stuff, "internal error"
            attach2Bond = True
        else:
            placedPos = atom_or_pos
            if hotspotAtom:
                hotspotAtomPos = hotspotAtom.posn()
                moveOffset = placedPos - hotspotAtomPos
            else:
                if newPart.molecules:
                    moveOffset = placedPos - newPart.molecules[0].center #e not 
                    #the best choice of center [bruce 060627 comment]

        if attach2Bond: # Connect part to a bondpoint of an existing chunk
            for m in newPart.molecules:
                if not m is hotspotAtom.molecule: 
                    newMol = m.copy_single_chunk(None)
                        # [this can break interchunk bonds,
                        #  thus it still has bug 2028]
                    newMol.setAssy(self.o.assy)

                    ## Get each of all other chunks' center movement for the 
                    ## rotation around 'rotCenter'
                    coff = rotOffset.rot(newMol.center - rotCenter)
                    coff = rotCenter - newMol.center + coff 

                    # The order of the following 2 statements doesn't matter
                    newMol.rot(rotOffset)
                    newMol.move(moveOffset + coff)

                    self.o.assy.addmol(newMol)
                    stuff.append(newMol)
        else: # Behaves like dropping a part anywhere you specify, independent 
            #of existing chunks.
            # copy all nodes in newPart (except those in clipboard items), 
            # regardless of node classes;
            # put it in a new Group if more than one thing [bruce 070501]
            # [TODO: this should be done in the cases above, too, but that's 
            # not yet implemented,
            #  and requires adding rot or pivot to the Node API and revising
            #  the rot-calling code above,
            #  and also reviewing the definition of the "hotspot of a Part" and 
            # maybe of a "depositable clipboard item".]
            assert newPart.tree.is_group()
            nodes = list(newPart.tree.members) # might be []
            assy = self.o.assy
            newnodes = copied_nodes_for_DND(nodes, 
                                            autogroup_at_top = True, 
                                            assy = assy)
                # Note: that calls name_autogrouped_nodes_for_clipboard 
                # internally, if it forms a Group,
                # but we ignore that and rename the new node differently below,
                # whether or not it was autogrouped. We could just as well do 
                # the autogrouping ourselves...
                # Note [bruce 070525]: it's better to call copied_nodes_for_DND 
                # here than copy_nodes_in_order, even if we didn't need to 
                # autogroup. One reason is that if some node is not copied,
                # that's not necessarily an error, since we don't care about 1-1
                # orig-copy correspondence here.
            if not newnodes:
                if newnodes is None:
                    print "bug: newnodes should not be None; nodes was %r (saved in debug._bugnodes)" % (nodes,)
                        # TODO: This might be possible, for arbitrary partlib 
                        # contents, just not for legitimate ones...
                        # but partlib will probably be (or is) user-expandable, 
                        #so we should turn this into history message,
                        # not a bug print. But I'm not positive it's possible
                        #w/o a bug, so review first. ###FIX [bruce 070501 comment]
                    import utilities.debug as debug
                    debug._bugnodes = nodes
                    newnodes = []
                msg = redmsg( "error: nothing to deposit in [%s]" % quote_html(str(newPart.name)) )
                return [], msg
            assert len(newnodes) == 1 # due to autogroup_at_top = True
            # but the remaining code works fine regardless of len(newnodes), 
            #in case we make autogroup a preference
            for newnode in newnodes:
                # Rename newnode based on the partlib name and a unique number.
                # It seems best to let the partlib mmp file contents (not just 
                # filename)
                # control the name used here, so use newPart.tree.name rather 
                # than just newPart.name.
                # (newPart.name is a complete file pathname; newPart.tree.name 
                #is usually its basename w/o extension.)
                basename = str(newPart.tree.name)
                if basename == 'Untitled':
                    # kluge, for the sake of 3 current partlib files, and files 
                    #saved only once by users (due to NE1 bug in save)
                    dirjunk, base = os.path.split(newPart.name)
                    basename, extjunk = os.path.splitext(base)
                from utilities.constants import gensym
                newnode.name = gensym( basename, assy) # name library part
                    #bruce 080407 basename + " " --> basename, and pass assy
                    # (per Mark NFR desire)
                #based on basename recorded in its mmp file's top node
                newnode.move(moveOffset) #k not sure this method is correctly 
                #implemented for measurement jigs, named views
                assy.addnode(newnode)
                stuff.append(newnode)

##            #bruce 060627 new code: fix bug 2028 (non-hotspot case only) 
##            about interchunk bonds being broken
##            nodes = newPart.molecules
##            newnodes = copied_nodes_for_DND(nodes)
##            if newnodes is None:
##                print "bug: newnodes should not be None; nodes was %r (saved in debug._bugnodes)" % (nodes,)
##                debug._bugnodes = nodes
##                newnodes = [] # kluge
##            for newMol in newnodes:
##                # some of the following probably only work for Chunks,
##                # though coding them for other nodes would not be hard
##                newMol.setAssy(self.o.assy)
##                newMol.move(moveOffset)
##                self.o.assy.addmol(newMol)
##                stuff.append(newMol)

##            # pre-060627 old code, breaks interchunk bonds since it copies 
##            #chunks one at a time (bug 2028)
##            for m in nodes:
##                newMol = m.copy(None) # later: renamed Chunk method to copy_single_chunk -- not sure if this was only called on chunks
##                newMol.setAssy(self.o.assy) #bruce 051227 revised this
##                
##                newMol.move(moveOffset)
##                
##                self.o.assy.addmol(newMol)
##                stuff.append(newMol)
##            pass
        self.o.assy.update_parts() #bruce 051227 see if this fixes the 
                                    #atom_debug exception in checkparts

        msg = greenmsg("Deposited library part: ") + " [" + \
            quote_html(str(newPart.name)) + "]"  #ninad060924 fix bug 1164 

        return stuff, msg  ####@@@@ should revise this message 
    def makeStrandChunkFromBrokenStrand(self, x1, x2): # by Mark
        """
        Makes a new strand chunk using the two singlets just created by
        busting the original strand, which is now broken. If the original
        strand was a ring, no new chunk is created.

        The new strand chunk, which includes the atoms between the 3' end of
        the original strand and the new 5' end (i.e. the break point), is
        added to the same DNA group as the original strand and assigned a
        different color.

        @param x1: The first of two singlets created by busting a strand
                   backbone bond. It is either the 3' or 5' open bond singlet,
                   but we don't know yet.
        @type  x1: L{Atom}

        @param x2: The second of two singlets created by busting a backbone
                   backbone bond. It is either the 3' or 5' open bond singlet,
                   but we don't know yet.
        @type  x2: L{Atom}

        @return: The new strand chunk. Returns B{None} if no new strand chunk
                 is created, as is the case of a ring.
        @rtype:  L{Chunk}
        """
        minimize = debug_pref("Adjust broken strand bondpoints using minimizer?",
                          #bruce 080415 revised text (to not use the developer-
                          # jargon-only term "singlet"), changed prefs_key,
                          # and removed non_debug = True, for .rc2 release,
                          # since the repositioning bug this worked around
                          # is now fixed.
                         Choice_boolean_False,
                         prefs_key = True,
                    )

        _five_prime_atom = None
        _three_prime_atom = None

        for singlet in (x1, x2):
            adjustSinglet(singlet, minimize = minimize)
            open_bond = singlet.bonds[0]
            if open_bond.isFivePrimeOpenBond():
                _five_prime_atom = open_bond.other(singlet)
            else:
                _three_prime_atom = open_bond.other(singlet)

        # Make sure we have exactly one 3' and one 5' singlet.
        # If not, there is probably a direction error on the open bond(s)
        # that x1 and/or x2 are members of.
        if not _five_prime_atom:
            print_compact_stack("No 5' bondpoint.")
            return None
        if not _three_prime_atom:
            print_compact_stack("No 3' bondpoint.")
            return None

        atomList = self.o.assy.getConnectedAtoms([_five_prime_atom])

        if _three_prime_atom in atomList:
            # The strand was a closed loop strand, so we're done.
            return None # Since no new chunk was created.

        # See self.ensure_toplevel_group() docstring for explanation.
        self.ensure_toplevel_group()
        _group_five_prime_was_in = _five_prime_atom.molecule.dad
        if env.prefs[assignColorToBrokenDnaStrands_prefs_key]:
            _new_strand_color = getNextStrandColor(_five_prime_atom.molecule.color)
        else:
            _new_strand_color = _five_prime_atom.molecule.color
        return self.makeChunkFromAtomList(atomList,
                                          group = _group_five_prime_was_in,
                                          name = gensym("Strand"),
                                              # doesn't need "DnaStrand" or self.assy,
                                              # since not normally seen by users
                                              # [bruce 080407 comment]
                                          color = _new_strand_color)
    def _createStructure(self):
        """
        Creates and returns the structure (in this case a L{Group} object that 
        contains the DNA strand and axis chunks. 
        @return : group containing that contains the DNA strand and axis chunks.
        @rtype: L{Group}  
        @note: This needs to return a DNA object once that model is implemented        
        """

        params = self._gatherParameters()

        # No error checking in build_struct, do all your error
        # checking in gather_parameters
        number_of_basePairs_from_struct,\
                                       numberOfBases, \
                                       dnaForm, \
                                       dnaModel, \
                                       basesPerTurn, \
                                       duplexRise, \
                                       endPoint1, \
                                       endPoint2, \
                                       color_junk = params
        #Note: color_junk is not used. Ideally it should do struct.setColor(color)
        #but the color combobox in the PM directly sets the color of the
        #structure to the specified one when the current index in the combobx
        #changes

        #If user enters the number of basepairs and hits preview i.e. endPoint1
        #and endPoint2 are not entered by the user and thus have default value
        #of V(0, 0, 0), then enter the endPoint1 as V(0, 0, 0) and compute
        #endPoint2 using the duplex length.
        #Do not use '==' equality check on vectors! its a bug. Use same_vals
        # or Veq instead.
        if Veq(endPoint1, endPoint2) and Veq(endPoint1, V(0, 0, 0)):
            endPoint2 = endPoint1 + \
                      self.win.glpane.right*getDuplexLength('B-DNA',
                                                            numberOfBases)

        if numberOfBases < 1:
            msg = redmsg("Cannot preview/insert a DNA duplex with 0 bases.")
            self.propMgr.updateMessage(msg)
            self.dna = None  # Fixes bug 2530. Mark 2007-09-02
            return None

        if dnaForm == 'B-DNA':
            if dnaModel == 'PAM3':
                dna = B_Dna_PAM3()
            elif dnaModel == 'PAM5':
                dna = B_Dna_PAM5()
            else:
                print "bug: unknown dnaModel type: ", dnaModel
        else:
            raise PluginBug("Unsupported DNA Form: " + dnaForm)

        self.dna = dna  # needed for done msg

        # self.name needed for done message
        if self.create_name_from_prefix:
            # create a new name
            name = self.name = gensym(self.prefix,
                                      self.win.assy)  # (in _build_struct)
            self._gensym_data_for_reusing_name = (self.prefix, name)
        else:
            # use externally created name
            self._gensym_data_for_reusing_name = None
            # (can't reuse name in this case -- not sure what prefix it was
            #  made with)
            name = self.name

        # Create the model tree group node.
        # Make sure that the 'topnode'  of this part is a Group (under which the
        # DNa group will be placed), if the topnode is not a group, make it a
        # a 'Group' (applicable to Clipboard parts).See part.py
        # --Part.ensure_toplevel_group method. This is an important line
        # and it fixes bug 2585
        self.win.assy.part.ensure_toplevel_group()
        dnaSegment = DnaSegment(self.name,
                                self.win.assy,
                                self.win.assy.part.topnode,
                                editCommand=self)
        try:
            # Make the DNA duplex. <dnaGroup> will contain three chunks:
            #  - Strand1
            #  - Strand2
            #  - Axis

            dna.make(dnaSegment, numberOfBases, basesPerTurn, duplexRise,
                     endPoint1, endPoint2)

            #set some properties such as duplexRise and number of bases per turn
            #This information will be stored on the DnaSegment object so that
            #it can be retrieved while editing this object.
            #This works with or without dna_updater. Now the question is
            #should these props be assigned to the DnaSegment in
            #dnaDuplex.make() itself ? This needs to be answered while modifying
            #make() method to fit in the dna data model. --Ninad 2008-03-05

            #WARNING 2008-03-05: Since self._modifyStructure calls
            #self._createStructure()
            #If in the near future, we actually permit modifying a
            #structure (such as dna) without actually recreating the whole
            #structre, then the following properties must be set in
            #self._modifyStructure as well. Needs more thought.
            props = (duplexRise, basesPerTurn)
            dnaSegment.setProps(props)

            return dnaSegment

        except (PluginBug, UserError):
            # Why do we need UserError here? Mark 2007-08-28
            dnaSegment.kill()
            raise PluginBug(
                "Internal error while trying to create DNA duplex.")