コード例 #1
0
    def _bond_bare_strandAtoms_with_orig_axisAtoms(
            self, axis_and_strand_atomPairs_to_bond):
        """
        Create bonds between the bare strand atoms of the new dna with the
        corresponding axis atoms of the original dna. This method should be
        called ONLY from self._replace_overlapping_axisAtoms_of_new_dna() where
        we remove the axis atoms of new dna that overlap the ones in old dna.
        We need to re-bond the bare strand atoms as a result of this replacment,
        with the axis atoms of the old dna.
        @param axis_and_strand_atomPairs_to_bond: A list containing pairs of the
               axis and strand atoms that will be bonded together. It is of the
               format [(atm1, atm2) , ....]
               Where,
               atm1 is always axisAtom of original dna
               atm2 is always bare strandAtom of new dna

        @type  axis_and_strand_atomPairs_to_bond: list
        @see: self._replace_overlapping_axisAtoms_of_new_dna() which calls this.
        """

        #@REVIEW/ TODO: Make sure that we are not bondingan axis atom with a
        #5' or 3' bondpoint of the strand atom.Skip the pair if its one and
        #the same atom (?) This may not be needed because of the other
        #code but is not obvious , so better to make sure in this method
        #-- Ninad 2008-04-11

        for atm1, atm2 in axis_and_strand_atomPairs_to_bond:
            #Skip the pair if its one and the same atom.
            if atm1 is not atm2:
                for s1 in atm1.singNeighbors():
                    if atm2.singNeighbors():
                        s2 = atm2.singNeighbors()[0]
                        bond_at_singlets(s1, s2, move=False)
                        #REVIEW 2008-04-10 if 'break' is needed -
                        break
コード例 #2
0
    def _make_bonds_1(self, assy = None):
        """
        Make bonds -- part 1.
        (Actually make the bonds using bond_at_singlets,
         call assy.changed() if you make any bonds,
         and record some info in several attrs of self.)
        """
        if assy == None:
            assy = self.o.assy
        self.bondable_pairs_atoms = []
        self.merged_chunks = []
        singlet_found_with_multiple_bonds = False # True when there are singlets with multiple bonds.
        self.total_bonds_made = 0 # The total number of bondpoint pairs that formed bonds.
        singlets_not_bonded = 0 # Number of bondpoints not bonded.

#        print self.bondable_pairs

        # This first section of code bonds each bondable pair of singlets.
        for s1, s2 in self.bondable_pairs:
            # Make sure each singlet of the pair has only one way of bonding.
            # If either singlet has more than one ways to bond, we aren't going to bond them.
            if self.ways_of_bonding[s1.key] == 1 and self.ways_of_bonding[s2.key] == 1:
                # Record the real atoms in case I want to undo the bond later (before general Undo exists)
                # Currently, this undo feature is not implemented here. Mark 050325
                a1 = s1.singlet_neighbor()
                a2 = s2.singlet_neighbor()
                self.bondable_pairs_atoms.append( (a1,a2) ) # Add this pair to the list
                bond_at_singlets(s1, s2, move = False) # Bond the singlets.
                assy.changed() # The assy has changed.
            else:
                singlet_found_with_multiple_bonds = True
        self.singlet_found_with_multiple_bonds = singlet_found_with_multiple_bonds
コード例 #3
0
    def pasteBond(self, sing):
        """
        If self.pastable has an unambiguous hotspot,
        paste a copy of self.pastable onto the given singlet;
        return (the copy, description) or (None, whynot)
        """
        self.update_pastable()
        pastable = self.pastable
        # as of 050316 addmol can change self.pastable! See comments in
        #pasteFree.
        # bruce 041123 added return values (and guessed docstring).
        # bruce 050121 using subr split out from this code
        ok, hotspot_or_whynot = find_hotspot_for_pasting(pastable)
        if not ok:
            whynot = hotspot_or_whynot
            return None, whynot

        hotspot = hotspot_or_whynot

        if isinstance(pastable, Chunk):
            numol = pastable.copy_single_chunk(None)
            #bruce 080314 use new name copy_single_chunk
            # bruce 041116 added (implicitly, by default) cauterize = 1
            # to mol.copy() above; change this to cauterize = 0 here if
            # unwanted, and for other uses of mol.copy in this file.
            # For this use, there's an issue that new singlets make it harder to
            # find a default hotspot! Hmm... I think copy should set one then.
            # So now it does [041123].
            hs = numol.hotspot or numol.singlets[0]  #e should use
            #find_hotspot_for_pasting again
            bond_at_singlets(hs, sing)  # this will move hs.molecule (numol) to
            #match
            # bruce 050217 comment: hs is now an invalid hotspot for numol,
            # and that used to cause bug 312, but this is now fixed in getattr
            # every time the
            # hotspot is retrieved (since it can become invalid in many other
            # ways too),so there's no need to explicitly forget it here.
            if self.pickit():
                numol.pickatoms()
                #bruce 060412 worries whether pickatoms is illegal or
                #ineffective (in both pasteBond and pasteFree)
                # given that numol.part is presumably not yet set (until after
                #addmol). But these seem to work
                # (assuming I'm testing them properly), so I'm not changing
                #this. [Why do they work?? ###@@@]
            self.o.assy.addmol(numol)  # do this last, in case it computes bbox
            return numol, "copy of %r" % pastable.name
        elif isinstance(pastable, Group):
            msg = "Pasting a group with hotspot onto a bond point " \
                  "is not implemented"
            return None, msg
コード例 #4
0
ファイル: PasteMode.py プロジェクト: ematvey/NanoEngineer-1
    def pasteBond(self, sing):
        """
        If self.pastable has an unambiguous hotspot,
        paste a copy of self.pastable onto the given singlet;
        return (the copy, description) or (None, whynot)
        """
        self.update_pastable()
        pastable = self.pastable
            # as of 050316 addmol can change self.pastable! See comments in 
            #pasteFree.
        # bruce 041123 added return values (and guessed docstring).
        # bruce 050121 using subr split out from this code
        ok, hotspot_or_whynot = find_hotspot_for_pasting(pastable)
        if not ok:
            whynot = hotspot_or_whynot
            return None, whynot

        hotspot = hotspot_or_whynot

        if isinstance(pastable, Chunk):
            numol = pastable.copy_single_chunk(None)
            #bruce 080314 use new name copy_single_chunk
            # bruce 041116 added (implicitly, by default) cauterize = 1
            # to mol.copy() above; change this to cauterize = 0 here if 
            # unwanted, and for other uses of mol.copy in this file.
            # For this use, there's an issue that new singlets make it harder to
            # find a default hotspot! Hmm... I think copy should set one then.
            # So now it does [041123].
            hs = numol.hotspot or numol.singlets[0] #e should use 
            #find_hotspot_for_pasting again
            bond_at_singlets(hs,sing) # this will move hs.molecule (numol) to 
            #match
            # bruce 050217 comment: hs is now an invalid hotspot for numol, 
            # and that used to cause bug 312, but this is now fixed in getattr 
            # every time the
            # hotspot is retrieved (since it can become invalid in many other
            # ways too),so there's no need to explicitly forget it here.
            if self.pickit():
                numol.pickatoms()
                #bruce 060412 worries whether pickatoms is illegal or 
                #ineffective (in both pasteBond and pasteFree)
                # given that numol.part is presumably not yet set (until after 
                #addmol). But these seem to work
                # (assuming I'm testing them properly), so I'm not changing 
                #this. [Why do they work?? ###@@@]
            self.o.assy.addmol(numol) # do this last, in case it computes bbox
            return numol, "copy of %r" % pastable.name
        elif isinstance(pastable, Group):
            msg = "Pasting a group with hotspot onto a bond point " \
                  "is not implemented"
            return None, msg
コード例 #5
0
    def _bond_axisNeighbors_with_orig_axisAtoms(self,
                                                axis_and_axis_atomPairs_to_bond
                                                ):
        """
        The operation that replaces the overlapping axis atoms of the new dnas
        with the corresponding axis atoms of the original dna could leave out
        some neighboring axis atoms of the *new dna* without bonds. So those
        need to be bonded with the axis atom of the original dna which replaced
        their neighbor. This method does that job.
        @param axis_and_axis_atomPairs_to_bond: A list containing pairs of the
               axis atom of original dna and axis atom on the new dna (which was
               a neighbor of the atom deleted in the replacement operation and
               was bonded to it) . Thies eatoms will be bonded with each other.
               It is of the format [(atm1, atm2) , ....]
               Where,
               atm1 is always axisAtom of original dna which replaced the
                     overlapping axis atom of new dna (and thereby broke
                     bond between that atom's axis neighbors )

               atm2 is always axis Atom of new dna , that was a neighbor to
                    an axis atom, say 'A', replaced by original dna axis atom
                    (because 'A' was overlapping) and was previously
                    bonded to 'A'.
        """
        for atm1, atm2 in axis_and_axis_atomPairs_to_bond:
            #Skip the pair if its one and the same atom.
            if atm1 is atm2:
                continue
            #the following check (atm1.singNeighbors()) < 1....)doesn't work in
            #some cases! So explicitly checking the length of the bondpoint
            #neighbors of the atom in the for loop afterwords.
            ##if len(atm1.singNeighbors()) < 1 or len(atm2.singNeighbors()) < 1:
                ##continue

            #Loop through bondpoints of atm1
            for s1 in atm1.singNeighbors():
                #bond point neigbors of atm2
                if atm2.singNeighbors():
                    s2 = atm2.singNeighbors()[0]
                    bond_at_singlets(s1, s2, move = False)
                    break
コード例 #6
0
    def _bond_axisNeighbors_with_orig_axisAtoms(self,
                                                axis_and_axis_atomPairs_to_bond
                                                ):
        """
        The operation that replaces the overlapping axis atoms of the new dnas
        with the corresponding axis atoms of the original dna could leave out
        some neighboring axis atoms of the *new dna* without bonds. So those
        need to be bonded with the axis atom of the original dna which replaced
        their neighbor. This method does that job.
        @param axis_and_axis_atomPairs_to_bond: A list containing pairs of the
               axis atom of original dna and axis atom on the new dna (which was
               a neighbor of the atom deleted in the replacement operation and
               was bonded to it) . Thies eatoms will be bonded with each other.
               It is of the format [(atm1, atm2) , ....]
               Where,
               atm1 is always axisAtom of original dna which replaced the
                     overlapping axis atom of new dna (and thereby broke
                     bond between that atom's axis neighbors )

               atm2 is always axis Atom of new dna , that was a neighbor to
                    an axis atom, say 'A', replaced by original dna axis atom
                    (because 'A' was overlapping) and was previously
                    bonded to 'A'.
        """
        for atm1, atm2 in axis_and_axis_atomPairs_to_bond:
            #Skip the pair if its one and the same atom.
            if atm1 is atm2:
                continue
            #the following check (atm1.singNeighbors()) < 1....)doesn't work in
            #some cases! So explicitly checking the length of the bondpoint
            #neighbors of the atom in the for loop afterwords.
            ##if len(atm1.singNeighbors()) < 1 or len(atm2.singNeighbors()) < 1:
                ##continue

            #Loop through bondpoints of atm1
            for s1 in atm1.singNeighbors():
                #bond point neigbors of atm2
                if atm2.singNeighbors():
                    s2 = atm2.singNeighbors()[0]
                    bond_at_singlets(s1, s2, move = False)
                    break
コード例 #7
0
    def _bond_bare_strandAtoms_with_orig_axisAtoms(self,
                                                   axis_and_strand_atomPairs_to_bond):
        """
        Create bonds between the bare strand atoms of the new dna with the
        corresponding axis atoms of the original dna. This method should be
        called ONLY from self._replace_overlapping_axisAtoms_of_new_dna() where
        we remove the axis atoms of new dna that overlap the ones in old dna.
        We need to re-bond the bare strand atoms as a result of this replacment,
        with the axis atoms of the old dna.
        @param axis_and_strand_atomPairs_to_bond: A list containing pairs of the
               axis and strand atoms that will be bonded together. It is of the
               format [(atm1, atm2) , ....]
               Where,
               atm1 is always axisAtom of original dna
               atm2 is always bare strandAtom of new dna

        @type  axis_and_strand_atomPairs_to_bond: list
        @see: self._replace_overlapping_axisAtoms_of_new_dna() which calls this.
        """

        #@REVIEW/ TODO: Make sure that we are not bondingan axis atom with a
        #5' or 3' bondpoint of the strand atom.Skip the pair if its one and
        #the same atom (?) This may not be needed because of the other
        #code but is not obvious , so better to make sure in this method
        #-- Ninad 2008-04-11


        for atm1, atm2 in axis_and_strand_atomPairs_to_bond:
            #Skip the pair if its one and the same atom.
            if atm1 is not atm2:
                for s1 in atm1.singNeighbors():
                    if atm2.singNeighbors():
                        s2 = atm2.singNeighbors()[0]
                        bond_at_singlets(s1, s2, move = False)
                        #REVIEW 2008-04-10 if 'break' is needed -
                        break
コード例 #8
0
    def _bond_two_strandAtoms(self, atm1, atm2):
        """
        Bonds the given strand atoms (sugar atoms) together. To bond these atoms, 
        it always makes sure that a 3' bondpoint on one atom is bonded to 5'
        bondpoint on the other atom.         

        @param atm1: The first sugar atom of PAM3 (i.e. the strand atom) to be 
                     bonded with atm2. 
        @param atm2: Second sugar atom
        
        @see: self.joinNeighboringStrands() which calls this
        
        TODO 2008-10-26: This method is originally from 
        B_DNA_PAM3_SingleStrand class. It is modified further to account for 
        color change after bonding the two strand atoms. It needs to 
        be refactored and moved to a dna_helper package. [-- Ninad comment]
        """
        #Moved from B_Dna_PAM3_SingleStrand_Generator to here, to fix bugs like 
        #2711 in segment resizing-- Ninad 2008-04-14
        assert atm1.element.role == 'strand' and atm2.element.role == 'strand'
        #Initialize all possible bond points to None

        five_prime_bondPoint_atm1  = None
        three_prime_bondPoint_atm1 = None
        five_prime_bondPoint_atm2  = None
        three_prime_bondPoint_atm2 = None
        #Initialize the final bondPoints we will use to create bonds
        bondPoint1 = None
        bondPoint2 = None

        #Find 5' and 3' bondpoints of atm1 (BTW, as of 2008-04-11, atm1 is 
        #the new dna strandend atom See self._fuse_new_dna_with_original_duplex
        #But it doesn't matter here. 
        for s1 in atm1.singNeighbors():
            bnd = s1.bonds[0]            
            if bnd.isFivePrimeOpenBond():
                five_prime_bondPoint_atm1 = s1                
            if bnd.isThreePrimeOpenBond():
                three_prime_bondPoint_atm1 = s1

        #Find 5' and 3' bondpoints of atm2
        for s2 in atm2.singNeighbors():
            bnd = s2.bonds[0]
            if bnd.isFivePrimeOpenBond():
                five_prime_bondPoint_atm2 = s2
            if bnd.isThreePrimeOpenBond():
                three_prime_bondPoint_atm2 = s2
        #Determine bondpoint1 and bondPoint2 (the ones we will bond). See method
        #docstring for details.
        if five_prime_bondPoint_atm1 and three_prime_bondPoint_atm2:
            bondPoint1 = five_prime_bondPoint_atm1
            bondPoint2 = three_prime_bondPoint_atm2
        #Following will overwrite bondpoint1 and bondPoint2, if the condition is
        #True. Doesn't matter. See method docstring to know why.
        if three_prime_bondPoint_atm1 and five_prime_bondPoint_atm2:
            bondPoint1 = three_prime_bondPoint_atm1
            bondPoint2 = five_prime_bondPoint_atm2

        #Do the actual bonding        
        if bondPoint1 and bondPoint2:
            bond_at_singlets(bondPoint1, bondPoint2, move = False)
        else:
            print_compact_stack("Bug: unable to bond atoms %s and %s: " %
                                (atm1, atm2) )
            
            
        #The following fixes bug 2770
        #Set the color of the whole dna strandGroup to the color of the
        #strand, whose bondpoint, is dropped over to the bondboint of the 
        #other strandchunk (thus joining the two strands together into
        #a single dna strand group) - Ninad 2008-04-09
        color = atm1.molecule.color 
        if color is None:
            color = atm1.element.color
        strandGroup1 = atm1.molecule.parent_node_of_class(self.assy.DnaStrand)
        
        #Temporary fix for bug 2829 that Damian reported. 
        #Bruce is planning to fix the underlying bug in the dna updater 
        #code. Once its fixed, The following block of code under 
        #"if DEBUG_BUG_2829" can be deleted -- Ninad 2008-05-01
        
        DEBUG_BUG_2829 = True
        
        if DEBUG_BUG_2829:            
            strandGroup2 = atm2.molecule.parent_node_of_class(
                self.assy.DnaStrand)                
            if strandGroup2 is not None:
                #set the strand color of strandGroup2 to the one for 
                #strandGroup1. 
                strandGroup2.setStrandColor(color)
                strandChunkList = strandGroup2.getStrandChunks()
                for c in strandChunkList:
                    if hasattr(c, 'invalidate_ladder'):
                        c.invalidate_ladder()
コード例 #9
0
 def _bond_two_strandAtoms(self, atm1, atm2):
     """
     Bonds the given strand atoms (sugar atoms) together. To bond these atoms, 
     it always makes sure that a 3' bondpoint on one atom is bonded to 5'
     bondpoint on the other atom. 
             
     @param atm1: The first sugar atom of PAM3 (i.e. the strand atom) to be 
                  bonded with atm2. 
     @param atm2: Second sugar atom
     @Note: This method is copied from DnaDuplex.py
     
     """
     #Moved from B_Dna_PAM3_SingleStrand_Generator to here, to fix bugs like 
     #2711 in segment resizing-- Ninad 2008-04-14
     assert atm1.element.role == 'strand' and atm2.element.role == 'strand'
     #Initialize all possible bond points to None
             
     five_prime_bondPoint_atm1  = None
     three_prime_bondPoint_atm1 = None
     five_prime_bondPoint_atm2  = None
     three_prime_bondPoint_atm2 = None
     #Initialize the final bondPoints we will use to create bonds
     bondPoint1 = None
     bondPoint2 = None
     
     #Find 5' and 3' bondpoints of atm1 (BTW, as of 2008-04-11, atm1 is 
     #the new dna strandend atom See self._fuse_new_dna_with_original_duplex
     #But it doesn't matter here. 
     for s1 in atm1.singNeighbors():
         bnd = s1.bonds[0]            
         if bnd.isFivePrimeOpenBond():
             five_prime_bondPoint_atm1 = s1                
         if bnd.isThreePrimeOpenBond():
             three_prime_bondPoint_atm1 = s1
             
     #Find 5' and 3' bondpoints of atm2
     for s2 in atm2.singNeighbors():
         bnd = s2.bonds[0]
         if bnd.isFivePrimeOpenBond():
             five_prime_bondPoint_atm2 = s2
         if bnd.isThreePrimeOpenBond():
             three_prime_bondPoint_atm2 = s2
     #Determine bondpoint1 and bondPoint2 (the ones we will bond). See method
     #docstring for details.
     if five_prime_bondPoint_atm1 and three_prime_bondPoint_atm2:
         bondPoint1 = five_prime_bondPoint_atm1
         bondPoint2 = three_prime_bondPoint_atm2
     #Following will overwrite bondpoint1 and bondPoint2, if the condition is
     #True. Doesn't matter. See method docstring to know why.
     if three_prime_bondPoint_atm1 and five_prime_bondPoint_atm2:
         bondPoint1 = three_prime_bondPoint_atm1
         bondPoint2 = five_prime_bondPoint_atm2
         
     #Copied over from BuildAtoms_GraphicsMode._singletLeftUp_joinStrands()
     #The following fixes bug 2770 
     #Set the color of the whole dna strandGroup to the color of the
     #strand, whose bondpoint, is dropped over to the bondboint of the 
     #other strandchunk (thus joining the two strands together into
     #a single dna strand group) - Ninad 2008-04-09
     color = atm1.molecule.color 
     if color is None:
         color = atm1.element.color
     strandGroup1 = atm1.molecule.parent_node_of_class(self.win.assy.DnaStrand)
                
     strandGroup2 = atm2.molecule.parent_node_of_class(
         self.win.assy.DnaStrand)                
     if strandGroup2 is not None:
         #set the strand color of strandGroup2 to the one for 
         #strandGroup1. 
         strandGroup2.setStrandColor(color)
         strandChunkList = strandGroup2.getStrandChunks()
         for c in strandChunkList:
             if hasattr(c, 'invalidate_ladder'):
                 c.invalidate_ladder()
     
     #Do the actual bonding        
     if bondPoint1 and bondPoint2:
         try:
             bond_at_singlets(bondPoint1, bondPoint2, move = False)
         except:
             print_compact_traceback("Bug: unable to bond atoms %s and %s"%(atm1, 
                                                                        atm2))
             
         if strandGroup1 is not None:
             strandGroup1.setStrandColor(color) 
コード例 #10
0
    def _bond_two_strandAtoms(self, atm1, atm2):
        """
        Bonds the given strand atoms (sugar atoms) together. To bond these atoms,
        it always makes sure that a 3' bondpoint on one atom is bonded to 5'
        bondpoint on the other atom.

        @param atm1: The first sugar atom of PAM3 (i.e. the strand atom) to be
                     bonded with atm2.
        @param atm2: Second sugar atom

        @see: self.joinNeighboringStrands() which calls this

        TODO 2008-10-26: This method is originally from
        B_DNA_PAM3_SingleStrand class. It is modified further to account for
        color change after bonding the two strand atoms. It needs to
        be refactored and moved to a dna_helper package. [-- Ninad comment]
        """
        #Moved from B_Dna_PAM3_SingleStrand_Generator to here, to fix bugs like
        #2711 in segment resizing-- Ninad 2008-04-14
        assert atm1.element.role == 'strand' and atm2.element.role == 'strand'
        #Initialize all possible bond points to None

        five_prime_bondPoint_atm1 = None
        three_prime_bondPoint_atm1 = None
        five_prime_bondPoint_atm2 = None
        three_prime_bondPoint_atm2 = None
        #Initialize the final bondPoints we will use to create bonds
        bondPoint1 = None
        bondPoint2 = None

        #Find 5' and 3' bondpoints of atm1 (BTW, as of 2008-04-11, atm1 is
        #the new dna strandend atom See self._fuse_new_dna_with_original_duplex
        #But it doesn't matter here.
        for s1 in atm1.singNeighbors():
            bnd = s1.bonds[0]
            if bnd.isFivePrimeOpenBond():
                five_prime_bondPoint_atm1 = s1
            if bnd.isThreePrimeOpenBond():
                three_prime_bondPoint_atm1 = s1

        #Find 5' and 3' bondpoints of atm2
        for s2 in atm2.singNeighbors():
            bnd = s2.bonds[0]
            if bnd.isFivePrimeOpenBond():
                five_prime_bondPoint_atm2 = s2
            if bnd.isThreePrimeOpenBond():
                three_prime_bondPoint_atm2 = s2
        #Determine bondpoint1 and bondPoint2 (the ones we will bond). See method
        #docstring for details.
        if five_prime_bondPoint_atm1 and three_prime_bondPoint_atm2:
            bondPoint1 = five_prime_bondPoint_atm1
            bondPoint2 = three_prime_bondPoint_atm2
        #Following will overwrite bondpoint1 and bondPoint2, if the condition is
        #True. Doesn't matter. See method docstring to know why.
        if three_prime_bondPoint_atm1 and five_prime_bondPoint_atm2:
            bondPoint1 = three_prime_bondPoint_atm1
            bondPoint2 = five_prime_bondPoint_atm2

        #Do the actual bonding
        if bondPoint1 and bondPoint2:
            bond_at_singlets(bondPoint1, bondPoint2, move=False)
        else:
            print_compact_stack("Bug: unable to bond atoms %s and %s: " %
                                (atm1, atm2))

        #The following fixes bug 2770
        #Set the color of the whole dna strandGroup to the color of the
        #strand, whose bondpoint, is dropped over to the bondboint of the
        #other strandchunk (thus joining the two strands together into
        #a single dna strand group) - Ninad 2008-04-09
        color = atm1.molecule.color
        if color is None:
            color = atm1.element.color
        strandGroup1 = atm1.molecule.parent_node_of_class(self.assy.DnaStrand)

        #Temporary fix for bug 2829 that Damian reported.
        #Bruce is planning to fix the underlying bug in the dna updater
        #code. Once its fixed, The following block of code under
        #"if DEBUG_BUG_2829" can be deleted -- Ninad 2008-05-01

        DEBUG_BUG_2829 = True

        if DEBUG_BUG_2829:
            strandGroup2 = atm2.molecule.parent_node_of_class(
                self.assy.DnaStrand)
            if strandGroup2 is not None:
                #set the strand color of strandGroup2 to the one for
                #strandGroup1.
                strandGroup2.setStrandColor(color)
                strandChunkList = strandGroup2.getStrandChunks()
                for c in strandChunkList:
                    if hasattr(c, 'invalidate_ladder'):
                        c.invalidate_ladder()