Difference between revisions of "User:Remig/plico/tug"

From Jmol
Jump to navigation Jump to search
m (Add description)
(Add full chain mode and fix sync issues)
Line 1: Line 1:
'''Tug''' allows the user to pull or push by mouse actions to move or rotate one part of a polypeptide against the rest by rotation on its psi and phi bonds with collision detection and restriction.
+
'''Tug''' allows the user to pull or push by mouse actions to move or rotate one part of a polypeptide against the rest by rotation on its psi and phi bonds with collision detection and restriction.  It also allows the user to move an entire chain to nest against another chain.
  
 
'''Tug''' is a member of the Plico suite of protein folding tools described in [[User:Remig/plico]] . It may be installed and accessed as a macro with the file:
 
'''Tug''' is a member of the Plico suite of protein folding tools described in [[User:Remig/plico]] . It may be installed and accessed as a macro with the file:
Line 8: Line 8:
 
Copy and paste the following into a text editor and save in your scripts folder as tug.spt.  
 
Copy and paste the following into a text editor and save in your scripts folder as tug.spt.  
 
<pre>#  tug - Jmol script by Ron Mignery
 
<pre>#  tug - Jmol script by Ron Mignery
#  v1.0 beta    2/4/2014 for Jmol 13.4
+
#  v1.1 beta    2/12/2014 for Jmol 14
 
#
 
#
These functions support protein manipulation in Jmol
+
Translate or rotate a stretch of a polypeptide against itself by mouse actions
var kTug = 1
+
#
 +
var kTug = 2
 
var kDtolerance = 0.2
 
var kDtolerance = 0.2
 
var kAtolerance = 5.0
 
var kAtolerance = 5.0
 
var kCtolerance = 1.85
 
var kCtolerance = 1.85
 
var kMtolerance = 0.8
 
var kMtolerance = 0.8
var kMinRama = 2
 
var kCollisionLimit = 200
 
 
var gCanchorIdx = -1
 
var gCanchorIdx = -1
 
var gCanchorNo = -1
 
var gCanchorNo = -1
 
var gNanchorIdx = -1
 
var gNanchorIdx = -1
 
var gNanchorNo = -1
 
var gNanchorNo = -1
var gNanchorPidx = -1
 
var gCanchorXyz = {0 0 0}
 
var gNanchorXyz = {0 0 0}
 
var gNanchorPxyz = {0 0 0}
 
 
var gCcargoIdx = -1
 
var gCcargoIdx = -1
 
var gNcargoIdx = -1
 
var gNcargoIdx = -1
var gCcargoXyz = {0 0 0}
 
var gNcargoXyz = {0 0 0}
 
 
var gCcargoNo = -1
 
var gCcargoNo = -1
 
var gNcargoNo = -1
 
var gNcargoNo = -1
Line 35: Line 28:
 
var g1pivotIdx = -1
 
var g1pivotIdx = -1
 
var g2pivotIdx = -1
 
var g2pivotIdx = -1
var gSelSaves = ({})  
+
var gSelSaves = ({})
 
var gCrotors = array()
 
var gCrotors = array()
var gNrotors = array()  
+
var gNrotors = array()
 
var gMouseX = 0
 
var gMouseX = 0
 
var gMouseY = 0
 
var gMouseY = 0
var gAc = ({})
+
var gOkCollide = ({})
 
var gChain = ""
 
var gChain = ""
var gMinNo = 1  
+
var gMinNo = 1
var gMaxNo = 9999  
+
var gMaxNo = 9999
 
var gScheme = "Jmol"
 
var gScheme = "Jmol"
 
var gAltScheme = "Rasmol"
 
var gAltScheme = "Rasmol"
var gCargoAtoms = ({})
+
var gCargoSet = ({})
var gSeed = 23423.52
+
var gMovingSet = ({})
 
var gBusy = FALSE
 
var gBusy = FALSE
 
var gSCidx = -1
 
var gSCidx = -1
Line 57: Line 50:
 
var gNewDrag = FALSE
 
var gNewDrag = FALSE
 
var gEcho = ""
 
var gEcho = ""
var gCountdown = 0
 
 
var gZoom = ""
 
var gZoom = ""
 
var gRotate = ""
 
var gRotate = ""
 +
var gTow = FALSE
 +
var g1dynamicIdx = -1
 +
var g2dynamicIdx = -1
  
 
# Return L tetrahedron point if i1<i2<i3, else R point
 
# Return L tetrahedron point if i1<i2<i3, else R point
Line 266: Line 261:
 
     var ret = FALSE
 
     var ret = FALSE
 
     if (isBBidx(aIDx) == FALSE) {
 
     if (isBBidx(aIDx) == FALSE) {
       
+
 
 
         ret = TRUE
 
         ret = TRUE
 
         switch({atomIndex=aIdx}.atomName) {
 
         switch({atomIndex=aIdx}.atomName) {
Line 285: Line 280:
 
             {(atomno=iNo) and (chain=gChain)}.selected = addOXT
 
             {(atomno=iNo) and (chain=gChain)}.selected = addOXT
 
         }
 
         }
         iNo++  
+
         iNo++
 
     }
 
     }
 
}
 
}
Line 291: Line 286:
 
function selectAddSideChain(fromIdx) {
 
function selectAddSideChain(fromIdx) {
 
     var iNo = {atomIndex=fromIdx}.atomno
 
     var iNo = {atomIndex=fromIdx}.atomno
 +
    var iChain = {atomIndex=fromIdx}.chain
 
     select none
 
     select none
     while ({(atomno=iNo) and (chain=gChain)}.atomName != "N") {
+
     while ({(atomno=iNo) and (chain=iChain)}.atomName != "N") {
         {(atomno=iNo) and (chain=gChain)}.selected = TRUE
+
         {(atomno=iNo) and (chain=iChain)}.selected = TRUE
         iNo++
+
         iNo++
         if (iNo > gMaxNo) {
+
         if (iNo > {chain=iChain}.atomno.max) {
 
             break
 
             break
         }
+
         }
 
     }
 
     }
 
}
 
}
Line 303: Line 299:
 
# First and last are BB atoms
 
# First and last are BB atoms
 
# Any side atoms in the range are also selected
 
# Any side atoms in the range are also selected
function selectNward (firstIdx, lastIdx) {
+
function selectNwardIdx (firstIdx, lastIdx) {
 
     var firstno = ((firstIdx < 0) ? {atomIndex=lastIdx}.atomno : {atomIndex=firstIdx}.atomno)
 
     var firstno = ((firstIdx < 0) ? {atomIndex=lastIdx}.atomno : {atomIndex=firstIdx}.atomno)
 
     var lastno = ((lastIdx < 0) ? firstno : {atomIndex=lastIdx}.atomno)
 
     var lastno = ((lastIdx < 0) ? firstno : {atomIndex=lastIdx}.atomno)
 
+
    var iChain = ((firstIdx < 0)
     select (atomno <= firstno) and (atomno >= lastno)
+
        ? {atomIndex=lastIdx}.chain : {atomIndex=firstIdx}.chain)
   
+
 
 +
     select (atomno <= firstno) and (atomno >= lastno) and (chain = iChain)
 +
 
 
     if ({(atomno=firstno) and (chain=gChain)}.atomName == "C") { # if psi
 
     if ({(atomno=firstno) and (chain=gChain)}.atomName == "C") { # if psi
 
         addSideChainToSelection(firstno-1, TRUE, TRUE)
 
         addSideChainToSelection(firstno-1, TRUE, TRUE)
         {(atomno=@{firstno+1}) and (chain=gChain)}.selected = TRUE # add O
+
         {(atomno=@{firstno+1}) and (chain=iChain)}.selected = TRUE # add O
 
     }
 
     }
     if ({(atomno=firstno) and (chain=gChain)}.atomName == "CA") {
+
     if ({(atomno=firstno) and (chain=iChain)}.atomName == "CA") {
 
         addSideChainToSelection(firstno, TRUE, FALSE)
 
         addSideChainToSelection(firstno, TRUE, FALSE)
 
     }
 
     }
     if ({(atomno=lastno) and (chain=gChain)}.atomName == "C") { # if psi
+
     if ({(atomno=lastno) and (chain=iChain)}.atomName == "C") { # if psi
 
         addSideChainToSelection(lastno-1, FALSE, FALSE)
 
         addSideChainToSelection(lastno-1, FALSE, FALSE)
 
     }
 
     }
Line 323: Line 321:
 
# First and last are BB atoms
 
# First and last are BB atoms
 
# Any side atoms in the range are also selected
 
# Any side atoms in the range are also selected
function selectCward (firstIdx, lastIdx) {
+
function selectCwardIdx (firstIdx, lastIdx) {
 
     var firstno = ((firstIdx < 0) ? gMaxNo : {atomIndex=firstIdx}.atomno)
 
     var firstno = ((firstIdx < 0) ? gMaxNo : {atomIndex=firstIdx}.atomno)
 
     var lastno = ((lastIdx < 0) ? 1 : {atomIndex=lastIdx}.atomno)
 
     var lastno = ((lastIdx < 0) ? 1 : {atomIndex=lastIdx}.atomno)
      
+
     var iChain = ((firstIdx < 0)
 +
        ? {atomIndex=lastIdx}.chain : {atomIndex=firstIdx}.chain)
 +
 
 
     # If nWard anchor in range, begin selection with it
 
     # If nWard anchor in range, begin selection with it
     if (gNanchorIdx >= 0) {
+
     if ((gNanchorIdx >= 0) and ({atomIndex=gNanchorIdx}.chain == iChain))  {
 
         var aNo = {atomIndex=gNanchorIdx}.atomno
 
         var aNo = {atomIndex=gNanchorIdx}.atomno
 
         if (aNo > firstNo) {
 
         if (aNo > firstNo) {
Line 334: Line 334:
 
         }
 
         }
 
     }
 
     }
   
+
 
 
     # If cWard anchor in range, end selection with it
 
     # If cWard anchor in range, end selection with it
     if (gCanchorIdx >= 0) {
+
     if ((gCanchorIdx >= 0) and ({atomIndex=gCanchorIdx}.chain == iChain))  {
 
         var aNo = {atomIndex=gCanchorIdx}.atomno
 
         var aNo = {atomIndex=gCanchorIdx}.atomno
 
         if (aNo < lastNo) {
 
         if (aNo < lastNo) {
Line 342: Line 342:
 
         }
 
         }
 
     }
 
     }
   
+
 
     select (atomno >= firstno) and (atomno <= lastno)
+
     select (atomno >= firstno) and (atomno <= lastno) and (chain = iChain)
   
+
 
     if ({(atomno=firstno) and (chain=gChain)}.atomName == "C") { # if psi
+
     if ({(atomno=firstno) and (chain=iChain)}.atomName == "C") { # if psi
 
         addSideChainToSelection(firstno-1, FALSE, FALSE)
 
         addSideChainToSelection(firstno-1, FALSE, FALSE)
 
     }
 
     }
     if ({(atomno=lastno) and (chain=gChain)}.atomName == "CA") {
+
     if ({(atomno=lastno) and (chain=iChain)}.atomName == "CA") {
 
         addSideChainToSelection(lastno, TRUE, FALSE)
 
         addSideChainToSelection(lastno, TRUE, FALSE)
 
     }
 
     }
     if ({(atomno=lastno) and (chain=gChain)}.atomName == "C") { # if psi
+
     if ({(atomno=lastno) and (chain=iChain)}.atomName == "C") { # if psi
 
         addSideChainToSelection(lastno-1, TRUE, TRUE)
 
         addSideChainToSelection(lastno-1, TRUE, TRUE)
         {(atomno=@{lastno+1}) and (chain=gChain)}.selected = TRUE # add O
+
         {(atomno=@{lastno+1}) and (chain=iChain)}.selected = TRUE # add O
 
     }
 
     }
 
}
 
}
Line 398: Line 398:
  
 
function countCollisions(rc) {
 
function countCollisions(rc) {
     var lcAtoms = ({})
+
     var cAtoms = ({})
 
     for (var idx = {*}.min.atomIndex; idx <= {*}.max.atomIndex; idx++) {
 
     for (var idx = {*}.min.atomIndex; idx <= {*}.max.atomIndex; idx++) {
         lcAtoms = lcAtoms or (within(kCtolerance, FALSE, {atomIndex=idx})
+
         if ({atomIndex=idx}.size > 0) {
            and {atomIndex > idx}
+
            var lcAtoms = (within(kCtolerance, FALSE, {atomIndex=idx})
            and not {rc}
+
                and {atomIndex > idx}
            and not connected({atomIndex=idx}))
+
                and not {rc}
 +
                and not connected({atomIndex=idx}))
 +
            if (lcAtoms.size > 0) {
 +
                cAtoms = cAtom or lcAtoms
 +
            }
 +
        }
 
     }
 
     }
     return lcAtoms.size
+
     return cAtoms.size
 
}
 
}
  
 
# Resolve collisions
 
# Resolve collisions
function handleCollisions( nWard, targetIdx) {
+
function handleCollisions2( targetIdx) {
  
 
     # For all selected atoms
 
     # For all selected atoms
Line 415: Line 420:
 
         var idx = {(atomno=iNo) and (chain=gchain)}.atomIndex
 
         var idx = {(atomno=iNo) and (chain=gchain)}.atomIndex
 
         if ({atomindex=idx}.selected) {
 
         if ({atomindex=idx}.selected) {
     
+
 
 
             # Collect local colliders
 
             # Collect local colliders
 
             var lcAtoms = (within(kCtolerance, FALSE, {atomIndex=idx})
 
             var lcAtoms = (within(kCtolerance, FALSE, {atomIndex=idx})
Line 421: Line 426:
 
                 and not {gOkCollide}
 
                 and not {gOkCollide}
 
                 and not connected({atomIndex=idx}))
 
                 and not connected({atomIndex=idx}))
           
 
 
             if (lcAtoms.size > 0) {
 
             if (lcAtoms.size > 0) {
           
+
 
 
                 # Ignore kinked BB
 
                 # Ignore kinked BB
 
                 if (isBBidx(idx) and angle({atomIndex=@{getCwardBBidx(idx)}},
 
                 if (isBBidx(idx) and angle({atomIndex=@{getCwardBBidx(idx)}},
Line 429: Line 433:
 
                     continue
 
                     continue
 
                 }
 
                 }
           
+
 
                #gCountdown--
+
                 # For all local colliders
                if (gCountdown < 0) {
 
                    timedOut("Too many collsions")
 
                }   
 
               
 
                 # For all local water colliders, delete
 
                var recollect = FALSE
 
 
                 for (var c = 1; c <= lcAtoms.size; c++ ) {
 
                 for (var c = 1; c <= lcAtoms.size; c++ ) {
 +
                    var cidx = lcAtoms[c].atomIndex
 +
 +
                    # If it is with water, delete it
 
                     if (lcAtoms[c].group = "HOH") {
 
                     if (lcAtoms[c].group = "HOH") {
                         delete {atomIndex=@{lcAtoms[c].atomIndex}}
+
                         delete {atomIndex=cidx}
                        recollect = TRUE
 
 
                     }
 
                     }
                }
+
 
               
+
                    # else if it is with side chain not proline, fix it
                # Recollect local colliders if needed
+
                     else if (isSCidx(cidx) and ({atomIndex=cidx}.group != "PRO")) {
                if (recollect) {
+
                         fixSCcollision2(cidx)
                    lcAtoms = (within(kCtolerance, FALSE, {atomIndex=idx})
 
                        and not {atomIndex=idx}
 
                        and not {gOkCollide}
 
                        and not connected({atomIndex=idx}))
 
                    recollect = FALSE
 
                }
 
                   
 
                # For all local colliders
 
                for (var c = 1; c <= lcAtoms.size; c++ ) {
 
               
 
                    # If it is with side chain not proline, fix it
 
                     var cidx = lcAtoms[c].atomIndex
 
                    if (isSCidx(cidx) and ({atomIndex=cidx}.group != "PRO")) {
 
                         fixSCcollision2(cidx)
 
 
                         recollect = TRUE
 
                         recollect = TRUE
                       
+
 
 
                         # If not fixed, exit fail
 
                         # If not fixed, exit fail
 
                         if (gOk2 == FALSE) {
 
                         if (gOk2 == FALSE) {
Line 467: Line 453:
 
                         }
 
                         }
 
                     }
 
                     }
                }
+
 
               
+
                    # else if it is itself a side chain not proline, fix it
                # Recollect local colliders if needed
+
                    else if (isSCidx(idx) and ({atomIndex=idx}.group != "PRO")) {
                if (recollect) {
+
                         fixSCcollision2(idx)
                    lcAtoms = (within(kCtolerance, FALSE, {atomIndex=idx})
+
                         recollect = TRUE
                        and not {atomIndex=idx}
+
 
                        and not {gOkCollide}
 
                        and not connected({atomIndex=idx}))
 
                }
 
                   
 
                # For all local colliders
 
                for (var c = 1; c <= lcAtoms.size; c++ ) {
 
               
 
                    # If it is with O, counter-rotate
 
                    if ({atomIndex=@{lcAtoms[c].atomIndex}}.atomName = "O") {
 
                         counterRotate2(idx, lcAtoms[c].atomIndex, targetIdx)
 
                          
 
 
                         # If not fixed, exit fail
 
                         # If not fixed, exit fail
 
                         if (gOk2 == FALSE) {
 
                         if (gOk2 == FALSE) {
Line 489: Line 464:
 
                         }
 
                         }
 
                     }
 
                     }
                }
 
            }
 
        }
 
    } # endfor iNo
 
}
 
  
# Rotate rotor set to move target atom to its proper place
+
                    # Else if it is with O, counter-rotate
function tugTrackIdx(targetIdx, targetPt, nWard, cDetect) {
+
                    else if (lcAtoms[c].atomName = "O") {
 +
                        counterRotate2(lcAtoms[c].atomIndex,
 +
                            {atomIndex=idx}.xyz, targetIdx, FALSE)
 +
 
 +
                        # If not fixed, exit fail
 +
                        if (gOk2 == FALSE) {
 +
                            return # early exit (break n jmol bug)
 +
                        }
 +
                    }
 +
 
 +
                    # Else if it is itself O, counter-rotate
 +
                    else if ({atomIndex=idx}.atomName = "O") {
 +
                        counterRotate2(idx, lcAtoms[c].xyz, targetIdx, FALSE)
 +
 
 +
                        # If not fixed, exit fail
 +
                        if (gOk2 == FALSE) {
 +
                            return # early exit (break n jmol bug)
 +
                        }
 +
                    }
 +
 
 +
                    else {    # Else not fixed, exit fail
 +
                        gOk2 = FALSE
 +
                        return # early exit (break n jmol bug)
 +
                    }
 +
                } # endfor
 +
            }
 +
        }
 +
    } # endfor iNo
 +
}
 +
 
 +
# Rotate rotor set to move target atom to its proper place
 +
function tugTrackIdx(targetIdx, targetPt, nWard, cDetect) {
 
     gOK = FALSE
 
     gOK = FALSE
 
     var pt = targetPt
 
     var pt = targetPt
Line 502: Line 503:
  
 
     var rotors = (nWard ? gNrotors : gCrotors)
 
     var rotors = (nWard ? gNrotors : gCrotors)
 +
 
     # For a number of passes
 
     # For a number of passes
 
     for (var pass1 = 0; pass1 < 20; pass1++) {
 
     for (var pass1 = 0; pass1 < 20; pass1++) {
 
         var blocked = ({})
 
         var blocked = ({})
 
         for (var pass2 = 0; pass2 < (rotors.size/4); pass2++) {
 
         for (var pass2 = 0; pass2 < (rotors.size/4); pass2++) {
     
+
 
 
             var v1 = {atomIndex=targetIdx}.xyz - pt
 
             var v1 = {atomIndex=targetIdx}.xyz - pt
           
+
 
 
             # Find the most orthgonal unused rotor
 
             # Find the most orthgonal unused rotor
 
             var imax = 0
 
             var imax = 0
Line 519: Line 521:
 
                     if ({blocked and {atomIndex=i2}}.count == 0) {
 
                     if ({blocked and {atomIndex=i2}}.count == 0) {
 
                         var v2 = {atomIndex=i3}.xyz - {atomIndex=i2}.xyz
 
                         var v2 = {atomIndex=i3}.xyz - {atomIndex=i2}.xyz
                       
+
 
 
                         var s = sin(abs(angle(v1, {0 0 0}, v2)))
 
                         var s = sin(abs(angle(v1, {0 0 0}, v2)))
 
                         if (s > smax) {
 
                         if (s > smax) {
Line 528: Line 530:
 
                 }
 
                 }
 
             }
 
             }
           
+
 
 
             # If no more rotors, break to next full try
 
             # If no more rotors, break to next full try
 
             if (imax == 0) {
 
             if (imax == 0) {
Line 537: Line 539:
 
             var i3 = rotors[imax+2]
 
             var i3 = rotors[imax+2]
 
             var i4 = rotors[imax+3]
 
             var i4 = rotors[imax+3]
           
+
 
 
             # Get dihedral of rotor with target point
 
             # Get dihedral of rotor with target point
 
             var dt = angle({atomIndex=targetIdx}, {atomIndex=i2}, {atomIndex=i3}, pt)
 
             var dt = angle({atomIndex=targetIdx}, {atomIndex=i2}, {atomIndex=i3}, pt)
Line 546: Line 548:
 
             var psi = dh + dt
 
             var psi = dh + dt
 
             var phi = dh + dt
 
             var phi = dh + dt
           
+
 
 
             # Compute resultant psi and phi
 
             # Compute resultant psi and phi
 
             #  and select from target atom to first half of rotor
 
             #  and select from target atom to first half of rotor
Line 559: Line 561:
 
                         {atomIndex=i3}, {atomIndex=@{getNwardBBidx(i3)}}) + dt
 
                         {atomIndex=i3}, {atomIndex=@{getNwardBBidx(i3)}}) + dt
 
                 }
 
                 }
               
+
 
 
                 if ({atomIndex=i2}.atomno > {atomIndex=targetIdx}.atomno) {
 
                 if ({atomIndex=i2}.atomno > {atomIndex=targetIdx}.atomno) {
 
                     movePt = TRUE
 
                     movePt = TRUE
                     selectNward(i3, getCwardBBidx(targetIdx))
+
                     selectNwardIdx(i3, getCwardBBidx(targetIdx))
 
                     {atomIndex=targetIdx}.selected = TRUE
 
                     {atomIndex=targetIdx}.selected = TRUE
 
                 }
 
                 }
 
                 else {
 
                 else {
                     selectCward(i2, targetIdx)
+
                     selectCwardIdx(i2, targetIdx)
 
                 }
 
                 }
 
             }
 
             }
Line 578: Line 580:
 
                         {atomIndex=i4}, {atomIndex=@{getCwardBBidx(i4)}}) + dt
 
                         {atomIndex=i4}, {atomIndex=@{getCwardBBidx(i4)}}) + dt
 
                 }
 
                 }
               
+
 
 
                 if ({atomIndex=i2}.atomno < {atomIndex=targetIdx}.atomno) {
 
                 if ({atomIndex=i2}.atomno < {atomIndex=targetIdx}.atomno) {
 
                     movePt = TRUE
 
                     movePt = TRUE
                     selectCward(i3, getNwardBBidx(targetIdx))
+
                     selectCwardIdx(i3, getNwardBBidx(targetIdx))
 
                     {atomIndex=targetIdx}.selected = TRUE
 
                     {atomIndex=targetIdx}.selected = TRUE
 
                 }
 
                 }
 
                 else {
 
                 else {
                     selectNward(i2, targetIdx)
+
                     selectNwardIdx(i2, targetIdx)
 
                 }
 
                 }
 
             }
 
             }
              
+
 
 +
             # Relax rules if desperate
 +
            if (pass1 > 10) {
 +
                phi = -50
 +
            }
 +
 
 
             # If rotation within ramachandran limits
 
             # If rotation within ramachandran limits
            #var ridx = 1 + (36*(((-psi\10)%180)+18)+(((phi\10)%180)+18))
+
             if ((abs(dt) >= 0.1) and
             if ((abs(dt) >= 0.1) and  
+
                 (({atomIndex=i2}.group=="GLY") or (phi < 0))) {
                 (({atomIndex=i2}.group=="GLY") or (phi < 0))) {#kRama[ridx] >= kMinRama))) {
 
  
 
                 # If moving target point, put the target atom there
 
                 # If moving target point, put the target atom there
Line 600: Line 606:
 
                     {atomIndex=targetIdx}.xyz = pt
 
                     {atomIndex=targetIdx}.xyz = pt
 
                 }
 
                 }
               
+
 
 
                 # Rotate to minimize vector ====================
 
                 # Rotate to minimize vector ====================
 
                 rotateSelected {atomIndex=i2} {atomIndex=i3} @dt
 
                 rotateSelected {atomIndex=i2} {atomIndex=i3} @dt
Line 606: Line 612:
 
                 # If collision checking
 
                 # If collision checking
 
                 if (cDetect) {
 
                 if (cDetect) {
               
+
 
 
                     # If collision, back off by eighths
 
                     # If collision, back off by eighths
 
                     var wasCollision = FALSE
 
                     var wasCollision = FALSE
Line 612: Line 618:
 
                         if (ci < 3) {
 
                         if (ci < 3) {
 
                             dt /= 2
 
                             dt /= 2
                         }                      
+
                         }
                         handleCollisions( nWard, targetIdx)
+
                         handleCollisions2( nWard, targetIdx)
 
                         if (gOk2==FALSE) {
 
                         if (gOk2==FALSE) {
 
                             wasCollision = TRUE
 
                             wasCollision = TRUE
Line 626: Line 632:
 
                             break
 
                             break
 
                         }
 
                         }
                       
+
 
 
                         if (dt < 0.01) {
 
                         if (dt < 0.01) {
 
                             break
 
                             break
Line 632: Line 638:
 
                     } # endfor
 
                     } # endfor
 
                 }
 
                 }
               
+
 
 
                 # If moving target point, put the target atom back
 
                 # If moving target point, put the target atom back
 
                 if (movePt) {
 
                 if (movePt) {
Line 638: Line 644:
 
                     {atomIndex=targetIdx}.xyz = cp
 
                     {atomIndex=targetIdx}.xyz = cp
 
                 }
 
                 }
               
+
 
 
             }
 
             }
           
+
 
 
             # If close enough, stop
 
             # If close enough, stop
 
             if (distance(pt, {atomIndex=targetIdx}) < kDtolerance) {
 
             if (distance(pt, {atomIndex=targetIdx}) < kDtolerance) {
Line 647: Line 653:
 
                 break
 
                 break
 
             }
 
             }
               
+
 
 
             # Block rotor
 
             # Block rotor
 
             blocked |= {atomIndex=i2}
 
             blocked |= {atomIndex=i2}
           
+
 
 
         }  # endfor num rotors passes
 
         }  # endfor num rotors passes
       
+
 
 
         if (gOK) {
 
         if (gOK) {
 
             break
 
             break
Line 660: Line 666:
  
 
# Counter rotate bonds on either side of a BB O
 
# Counter rotate bonds on either side of a BB O
function docounterRotate2(caPhiIDx, nIdx, cIdx, oIdx, caPsiIdx, dir, nWard) {
+
function docounterRotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, nWard) {
  
     # Rotate phi
+
     # Rotate psi
 
     {atomIndex=nIdx}.selected = nWard
 
     {atomIndex=nIdx}.selected = nWard
 
     {atomIndex=cIdx}.selected = nWard
 
     {atomIndex=cIdx}.selected = nWard
     {atomIndex=oIdx}.selected = nWard
+
     {atomIndex=oIdx}.selected = nward
 
     rotateSelected {atomIndex=caPsiIdx} {atomIndex=cIdx} @{dir}
 
     rotateSelected {atomIndex=caPsiIdx} {atomIndex=cIdx} @{dir}
  
     # Counter-rotate psi
+
     # Counter-rotate phi
 
     {atomIndex=nIdx}.selected = not nWard
 
     {atomIndex=nIdx}.selected = not nWard
 
     {atomIndex=cIdx}.selected = not nWard
 
     {atomIndex=cIdx}.selected = not nWard
     {atomIndex=oIdx}.selected = not nWard
+
     {atomIndex=oIdx}.selected = not nward
 
     rotateSelected {atomIndex=nIdx} {atomIndex=caPhiIdx} @{-dir}
 
     rotateSelected {atomIndex=nIdx} {atomIndex=caPhiIdx} @{-dir}
 
}
 
}
       
+
 
function counterRotate2(aIdx, bIdx, targetIdx) {
+
function counterRotate(oIdx, dir, nWard) {
 +
 
 
     var selsave = {selected}
 
     var selsave = {selected}
    gOk2 = TRUE
+
     var iChain = {atomIndex=oIdx}.chain
    var oIdx = aIdx
 
     var xIdx = bIdx
 
    if ({atomIndex=bIdx}.atomName=="O") {
 
        oIdx = bIdx
 
        xIdx = aIdx
 
    }
 
 
     var cIdx = getScBBidx(oIdx)
 
     var cIdx = getScBBidx(oIdx)
 
     var nIdx = getCwardBBidx(cIdx)
 
     var nIdx = getCwardBBidx(cIdx)
 
     var caPhiIdx = getCwardBBidx(nIdx)
 
     var caPhiIdx = getCwardBBidx(nIdx)
 
     var caPsiIdx = getNwardBBidx(cIdx)
 
     var caPsiIdx = getNwardBBidx(cIdx)
   
+
 
    var nWard = ({atomIndex=oIdx}.atomno < {atomIndex=targetIdx}.atomno)
 
 
     if (nWard) {
 
     if (nWard) {
         selectCward(cIdx, targetIdx)
+
         nNo = {chain=iChain}.atomno.min
 +
        selectNwardIdx(caPsiIdx, {(atomno=nNo) and (chain=iChain)}.atomIndex)
 
     }
 
     }
 
     else {
 
     else {
         selectNward(nIdx, targetIdx)
+
         cNo = {chain=iChain}.atomno.max
 +
        selectCwardIdx(caPhiIdx, {(atomno=cNo) and (chain=iChain)}.atomIndex)
 
     }
 
     }
   
+
 
 +
    # Counter-rotate
 +
    docounterRotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, not nWard)
 +
    select selsave
 +
}
 +
 
 +
function counterRotate2(oIdx, toPt, terminalIdx, oDrag) {
 +
 
 +
    var selsave = {selected}
 +
    var gOk2 = TRUE
 +
    var cIdx = getScBBidx(oIdx)
 +
    var nIdx = getCwardBBidx(cIdx)
 +
    var caPhiIdx = getCwardBBidx(nIdx)
 +
    var caPsiIdx = getNwardBBidx(cIdx)
 +
 
 +
    var nTward = ({atomIndex=oIdx}.atomno < {atomIndex=terminalIdx}.atomno)
 +
    if (nTward) {
 +
        selectCwardIdx(cIdx, terminalIdx)
 +
    }
 +
    else {
 +
        selectNwardIdx(nIdx, terminalIdx)
 +
    }
 +
 
 
     # Until all collisions cancelled
 
     # Until all collisions cancelled
 
     var dir = 5
 
     var dir = 5
     var ang = angle({atomIndex=xIdx}, {atomIndex=oIdx}, {atomIndex=cIdx})
+
     var ang = angle(toPt, {atomIndex=oIdx}, {atomIndex=cIdx})
 
     var tcount = 0
 
     var tcount = 0
     while (within(kCtolerance, FALSE, {atomIndex=oIdx})
+
     while (oDrag or (within(kCtolerance, FALSE, {atomIndex=oIdx})
 
             and not {atomIndex=oIdx} and not connected({atomIndex=oIdx})
 
             and not {atomIndex=oIdx} and not connected({atomIndex=oIdx})
             and not {gOkCollide} > 0) {
+
             and not {gOkCollide} > 0)) {
       
+
 
 
         # Counter-rotate
 
         # Counter-rotate
         docounterRotate2(caPhiIDx, nIdx, cIdx, oIdx, caPsiIdx, dir, nWard)
+
         docounterRotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, nTward)
         var newang = angle({atomIndex=xIdx}, {atomIndex=oIdx}, {atomIndex=cIdx})
+
         var newang = angle(toPt, {atomIndex=oIdx}, {atomIndex=cIdx})
       
+
 
 
         # If wrong direction once, undo and reverse
 
         # If wrong direction once, undo and reverse
 
         if (newang > ang) {
 
         if (newang > ang) {
             docounterRotate2(caPhiIDx, nIdx, cIdx, oIdx, caPsiIdx, -dir, nWard)
+
             docounterRotate(caPhiIDx, nIdx, cIdx, oIdx, caPsiIdx, -dir, nTward)
           
+
 
 
             # If first time, continue in opposite direction
 
             # If first time, continue in opposite direction
 
             dir *= -1
 
             dir *= -1
Line 718: Line 742:
 
                 continue
 
                 continue
 
             }
 
             }
 +
        }
 +
 +
        if (oDrag) {
 +
            break
 
         }
 
         }
  
Line 726: Line 754:
 
             break
 
             break
 
         }
 
         }
       
+
 
 
     } # endwhile
 
     } # endwhile
     select selsave  
+
     select selsave
 
}
 
}
  
 
# Repair proline
 
# Repair proline
function repairProline(idx) {
+
function repairProline(BBidx) {
     var cbidx = getCBidx(idx)
+
     var cbidx = getCBidx(BBidx)
 
     var cbno = {atomIndex=cbidx}.atomno
 
     var cbno = {atomIndex=cbidx}.atomno
 
     var cgidx = {(atomno=@{cbno+1}) and (chain=gChain)}.atomIndex
 
     var cgidx = {(atomno=@{cbno+1}) and (chain=gChain)}.atomIndex
Line 739: Line 767:
 
     var caidx = {(atomno=@{cbno-3}) and (chain=gChain)}.atomIndex
 
     var caidx = {(atomno=@{cbno-3}) and (chain=gChain)}.atomIndex
 
     var nidx = {(atomno=@{cbno-4}) and (chain=gChain)}.atomIndex
 
     var nidx = {(atomno=@{cbno-4}) and (chain=gChain)}.atomIndex
   
+
 
 
     select {atomIndex=cbidx}
 
     select {atomIndex=cbidx}
 
     setAngleIdx(nidx, caidx, cbidx, 109.5)
 
     setAngleIdx(nidx, caidx, cbidx, 109.5)
   
+
 
 
     select {atomIndex=cdidx}
 
     select {atomIndex=cdidx}
 
     setDistanceIdx(nidx, cdidx, 1.47)
 
     setDistanceIdx(nidx, cdidx, 1.47)
 
     setAngleIdx(caidx, nidx, cdidx, 102.7)
 
     setAngleIdx(caidx, nidx, cdidx, 102.7)
 
     setDihedralIdx(cbidx, caidx, nidx, cdidx, 16.2)
 
     setDihedralIdx(cbidx, caidx, nidx, cdidx, 16.2)
   
+
 
 
     select {atomIndex=cgidx}
 
     select {atomIndex=cgidx}
 
     setDistanceIdx(cdidx, cgidx, 1.51)
 
     setDistanceIdx(cdidx, cgidx, 1.51)
Line 755: Line 783:
  
 
# Repair side chain
 
# Repair side chain
function plicoRepairSideChain(targetIdx, nWard) {
+
function repairSideChain(targetIdx, nWard) {
  
 
     var idx = (nWard ? getCwardBBidx(targetIdx) : getNwardBBidx(targetIdx))
 
     var idx = (nWard ? getCwardBBidx(targetIdx) : getNwardBBidx(targetIdx))
Line 783: Line 811:
 
         }
 
         }
 
     }
 
     }
   
+
 
 
     else if ({atomIndex=targetIdx}.atomName == "C") {
 
     else if ({atomIndex=targetIdx}.atomName == "C") {
 
         var oidx = getOidx(targetIdx)
 
         var oidx = getOidx(targetIdx)
Line 801: Line 829:
 
             for (var i = 1; i <= colliders.size; i++) {
 
             for (var i = 1; i <= colliders.size; i++) {
 
                 if (colliders[i].atomName == "O") {
 
                 if (colliders[i].atomName == "O") {
                     counterRotate2(dIdx, colliders[i].atomIndex, targetIdx)
+
                     counterRotate2(colliders[i].atomIndex,
 +
                        {atomIndex=dIdx}.xyz, targetIdx, FALSE)
 
                 }
 
                 }
 
             }
 
             }
Line 814: Line 843:
 
     var targetIdx = gCcargoIdx
 
     var targetIdx = gCcargoIdx
 
     var okCount = 0
 
     var okCount = 0
       
+
 
 
     # Allow collisions with cargo
 
     # Allow collisions with cargo
     gOkCollide = gCargoAtoms
+
     gOkCollide = gCargoSet
     var tcount = 0              
+
     var tcount = 0
 
     while (targetIdx != gCanchorIdx) {
 
     while (targetIdx != gCanchorIdx) {
   
+
 
 
         # Step to next atom
 
         # Step to next atom
 
         targetIdx = getCwardBBidx(targetIdx)
 
         targetIdx = getCwardBBidx(targetIdx)
       
+
 
 
         # No collision with cargo allowed after two atoms placed
 
         # No collision with cargo allowed after two atoms placed
 
         if (tcount == 2) {
 
         if (tcount == 2) {
Line 828: Line 857:
 
         }
 
         }
 
         tcount++
 
         tcount++
       
+
 
 
         # Compute targets desired coords
 
         # Compute targets desired coords
 
         var c1idx = getCwardBBidx(targetIdx)
 
         var c1idx = getCwardBBidx(targetIdx)
Line 838: Line 867:
 
             var oidx = getOidx(n1idx)
 
             var oidx = getOidx(n1idx)
 
             select {atomIndex=oidx}
 
             select {atomIndex=oidx}
           
+
 
             # Desired target location is trigonal O      
+
             # Desired target location is trigonal O
 
             setDistanceIdx(n1idx, oidx, 1.5)
 
             setDistanceIdx(n1idx, oidx, 1.5)
 
             pt = getTrigonal(n2idx, n1idx, oidx, 1.37)
 
             pt = getTrigonal(n2idx, n1idx, oidx, 1.37)
Line 846: Line 875:
 
         else if (({atomIndex=targetIdx}.atomName == "C")
 
         else if (({atomIndex=targetIdx}.atomName == "C")
 
             and ({atomIndex=targetIdx}.group != "GLY")) {
 
             and ({atomIndex=targetIdx}.group != "GLY")) {
           
+
 
 
             # Desired target location is tetragonal CB
 
             # Desired target location is tetragonal CB
 
             var cbidx = getCBidx(n1idx)
 
             var cbidx = getCBidx(n1idx)
Line 852: Line 881:
 
         }
 
         }
 
         else { # CA (or GLY C)
 
         else { # CA (or GLY C)
           
+
 
 
             # Save current target coords
 
             # Save current target coords
 
             var cp = {atomIndex=targetIdx}.xyz
 
             var cp = {atomIndex=targetIdx}.xyz
   
+
 
 
             # Set target atom at desired distance and angle
 
             # Set target atom at desired distance and angle
 
             select {atomIndex=targetIdx}
 
             select {atomIndex=targetIdx}
 
             setDistanceIdx(n1idx, targetIdx, 1.5)
 
             setDistanceIdx(n1idx, targetIdx, 1.5)
             setAngleIdx(n2idx, n1idx, targetIdx, 110.0)
+
             setAngleIdx(n2idx, n1idx, targetIdx, 120.0)
 
             if ({atomIndex=targetIdx}.atomName == "CA") {
 
             if ({atomIndex=targetIdx}.atomName == "CA") {
 
                 setDihedralIdx(n3idx, n2idx, n1idx, targetIdx, 180)
 
                 setDihedralIdx(n3idx, n2idx, n1idx, targetIdx, 180)
 
             }
 
             }
           
+
 
 
             # Record and restore target
 
             # Record and restore target
 
             pt = {atomIndex=targetIdx}.xyz
 
             pt = {atomIndex=targetIdx}.xyz
Line 869: Line 898:
 
         }
 
         }
  
         # If target not at desired location      
+
         # If target not at desired location
 
         if (distance(pt, {atomIndex=targetIdx}) > kDtolerance) {
 
         if (distance(pt, {atomIndex=targetIdx}) > kDtolerance) {
 
             okCount = 0
 
             okCount = 0
Line 876: Line 905:
 
             gOK = FALSE
 
             gOK = FALSE
 
             while ((xcount < 20) and (gOK == FALSE)) {
 
             while ((xcount < 20) and (gOK == FALSE)) {
           
+
 
 
                 # Rotate on cWard rotor set to move it there
 
                 # Rotate on cWard rotor set to move it there
 
                 tugTrackIdx(targetIdx, pt, FALSE, FALSE)
 
                 tugTrackIdx(targetIdx, pt, FALSE, FALSE)
Line 886: Line 915:
 
             gOK = TRUE
 
             gOK = TRUE
 
             okCount++
 
             okCount++
         }  
+
         }
       
+
 
 
         # If successful
 
         # If successful
 
         if (gOK == TRUE) {
 
         if (gOK == TRUE) {
       
+
 
 
             # Adust any side atoms
 
             # Adust any side atoms
             plicoRepairSideChain(targetIdx, FALSE)
+
             repairSideChain(targetIdx, FALSE)
 
         }
 
         }
       
+
 
 
         # Else fail
 
         # Else fail
 
         else {
 
         else {
 
             break
 
             break
 
         }
 
         }
       
+
 
 
         # If no movement in 4 tries, we are done
 
         # If no movement in 4 tries, we are done
 
         if (okCount > 3) {
 
         if (okCount > 3) {
Line 911: Line 940:
  
 
     gOK = TRUE
 
     gOK = TRUE
   
+
 
 
     # For all bb atoms nWard of cargo
 
     # For all bb atoms nWard of cargo
 
     var targetIdx = gNcargoIdx
 
     var targetIdx = gNcargoIdx
 
     var okCount = 0
 
     var okCount = 0
   
+
 
 
     # Allow collisions with cargo
 
     # Allow collisions with cargo
     gOkCollide = gCargoAtoms
+
     gOkCollide = gCargoSet
     var tcount = 0              
+
     var tcount = 0
 
     while (targetIdx != gNanchorIdx) {
 
     while (targetIdx != gNanchorIdx) {
  
 
         # Step to next atom
 
         # Step to next atom
 
         targetIdx = getNwardBBidx(targetIdx)
 
         targetIdx = getNwardBBidx(targetIdx)
       
+
 
 
         # No collision with cargo allowed after two atoms placed
 
         # No collision with cargo allowed after two atoms placed
 
         if (tcount == 2) {
 
         if (tcount == 2) {
Line 929: Line 958:
 
         }
 
         }
 
         tcount++
 
         tcount++
       
+
 
 
         # Compute targets desired coords
 
         # Compute targets desired coords
 
         var n1idx = getNwardBBidx(targetIdx)
 
         var n1idx = getNwardBBidx(targetIdx)
Line 937: Line 966:
 
         var pt = {0 0 0}
 
         var pt = {0 0 0}
 
         if ({atomIndex=targetIdx}.atomName == "CA") {
 
         if ({atomIndex=targetIdx}.atomName == "CA") {
       
+
 
             # Desired target location is trigonal O      
+
             # Desired target location is trigonal O
 
             var oidx = getOidx(c1idx)
 
             var oidx = getOidx(c1idx)
 
             select {atomIndex=oidx}
 
             select {atomIndex=oidx}
Line 947: Line 976:
 
         else if (({atomIndex=targetIdx}.atomName == "N")
 
         else if (({atomIndex=targetIdx}.atomName == "N")
 
             and ({atomIndex=targetIdx}.group != "GLY")) {
 
             and ({atomIndex=targetIdx}.group != "GLY")) {
           
+
 
             # Desired target location is r-tetragonal CB      
+
             # Desired target location is r-tetragonal CB
 
             var cbidx = getCBidx(c1idx)
 
             var cbidx = getCBidx(c1idx)
 
             pt = getTet(cbidx, c1idx, c2idx, 1.5)
 
             pt = getTet(cbidx, c1idx, c2idx, 1.5)
 
         }
 
         }
 
         else { # C
 
         else { # C
       
+
 
 
             # Save current target coords
 
             # Save current target coords
 
             var cp = {atomIndex=targetIdx}.xyz
 
             var cp = {atomIndex=targetIdx}.xyz
           
+
 
 
             # Set target atom at desired distance and angle
 
             # Set target atom at desired distance and angle
 
             select {atomIndex=targetIdx}
 
             select {atomIndex=targetIdx}
 
             setDistanceIdx(c1idx, targetIdx, 1.37)
 
             setDistanceIdx(c1idx, targetIdx, 1.37)
 
             setAngleIdx(c2idx, c1idx, targetIdx, 110.0)
 
             setAngleIdx(c2idx, c1idx, targetIdx, 110.0)
               
+
 
 
             if ({atomIndex=targetIdx}.group == "PRO") {
 
             if ({atomIndex=targetIdx}.group == "PRO") {
 
                 setDihedralIdx(c3idx, c2idx, c1idx, targetIdx, -57.0)
 
                 setDihedralIdx(c3idx, c2idx, c1idx, targetIdx, -57.0)
 
             }
 
             }
       
+
 
 
             # Record and restore target
 
             # Record and restore target
 
             pt = {atomIndex=targetIdx}.xyz
 
             pt = {atomIndex=targetIdx}.xyz
 
             {atomIndex=targetIdx}.xyz = cp
 
             {atomIndex=targetIdx}.xyz = cp
 
         }
 
         }
       
+
 
         # If target not at desired location      
+
         # If target not at desired location
 
         if (distance(pt, {atomIndex=targetIdx}) > kDtolerance) {
 
         if (distance(pt, {atomIndex=targetIdx}) > kDtolerance) {
 
             var okCount = 0
 
             var okCount = 0
Line 978: Line 1,007:
 
             gOK = FALSE
 
             gOK = FALSE
 
             while ((xcount < 20) and (gOK == FALSE)) {
 
             while ((xcount < 20) and (gOK == FALSE)) {
           
+
 
 
                 # Rotate on cWard rotor set to move it there
 
                 # Rotate on cWard rotor set to move it there
 
                 tugTrackIdx(targetIdx, pt, TRUE, FALSE)
 
                 tugTrackIdx(targetIdx, pt, TRUE, FALSE)
Line 988: Line 1,017:
 
             gOK = TRUE
 
             gOK = TRUE
 
             okCount++
 
             okCount++
         }  
+
         }
  
 
         # If sucessful
 
         # If sucessful
 
         if (gOK == TRUE) {
 
         if (gOK == TRUE) {
       
+
 
 
             # Adust any side atoms
 
             # Adust any side atoms
             plicoRepairSideChain(targetIdx, TRUE)
+
             repairSideChain(targetIdx, TRUE)
 
         }
 
         }
       
+
 
 
         # Else fail
 
         # Else fail
 
         else {
 
         else {
 
             break
 
             break
 
         }
 
         }
       
+
 
 
         # If no movement in 4 tries, we are done
 
         # If no movement in 4 tries, we are done
 
         if (okCount > 3) {
 
         if (okCount > 3) {
 
             break
 
             break
 
         }
 
         }
       
+
 
 
     }  # endwhile (targetIdx != gNanchorIdx) {
 
     }  # endwhile (targetIdx != gNanchorIdx) {
  
Line 1,040: Line 1,069:
 
}
 
}
  
function collectSCrotors(no) {
+
function collectSCrotors(no, iChain) {
 
     var scBondIdxs = array()
 
     var scBondIdxs = array()
 
     for (var iNo = no; iNo >= 0; iNo--) {
 
     for (var iNo = no; iNo >= 0; iNo--) {
 
         var ile = 0
 
         var ile = 0
         switch ({(atomno=iNo) and (chain=gChain)}.atomName) {
+
         switch ({(atomno=iNo) and (chain=iChain)}.atomName) {
 
         case "CA" :
 
         case "CA" :
 
             return scBondIdxs # Early exit since break 1 appears broken
 
             return scBondIdxs # Early exit since break 1 appears broken
            #break 1
 
 
         case "CZ" :
 
         case "CZ" :
             if ({(atomno=iNo) and (chain=gChain)}.group == "TYR") {
+
             if ({(atomno=iNo) and (chain=iChain)}.group == "TYR") {
 
                 break
 
                 break
 
             }
 
             }
 
         case "CE" :
 
         case "CE" :
             if ({(atomno=iNo) and (chain=gChain)}.group == "MET") {
+
             if ({(atomno=iNo) and (chain=iChain)}.group == "MET") {
 
                 break
 
                 break
 
             }
 
             }
 
         case "CG1" :
 
         case "CG1" :
             if ({(atomno=iNo) and (chain=gChain)}.group == "VAL") {
+
             if ({(atomno=iNo) and (chain=iChain)}.group == "VAL") {
 
                 break
 
                 break
 
             }
 
             }
             if ({(atomno=iNo) and (chain=gChain)}.group == "ILE") {
+
             if ({(atomno=iNo) and (chain=iChain)}.group == "ILE") {
 
                 ile = 1
 
                 ile = 1
 
             }
 
             }
Line 1,068: Line 1,096:
 
         case "CG" :
 
         case "CG" :
 
         case "CB" :
 
         case "CB" :
             scBondIdxs += {(atomno=@{iNo+1+ile}) and (chain=gChain)}.atomIndex
+
             scBondIdxs += {(atomno=@{iNo+1+ile}) and (chain=iChain)}.atomIndex
             scBondIdxs += {(atomno=@{iNo+0}) and (chain=gChain)}.atomIndex
+
             scBondIdxs += {(atomno=@{iNo+0}) and (chain=iChain)}.atomIndex
             if ({(atomno=iNo) and (chain=gChain)}.atomName%2 == "CG") {
+
             if ({(atomno=iNo) and (chain=iChain)}.atomName%2 == "CG") {
                 scBondIdxs += {(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex
+
                 scBondIdxs += {(atomno=@{iNo-1}) and (chain=iChain)}.atomIndex
                 scBondIdxs += {(atomno=@{iNo-4}) and (chain=gChain)}.atomIndex
+
                 scBondIdxs += {(atomno=@{iNo-4}) and (chain=iChain)}.atomIndex
 
             }
 
             }
             else if ({(atomno=iNo) and (chain=gChain)}.atomName == "CB") {
+
             else if ({(atomno=iNo) and (chain=iChain)}.atomName == "CB") {
                 scBondIdxs += {(atomno=@{iNo-3}) and (chain=gChain)}.atomIndex
+
                 scBondIdxs += {(atomno=@{iNo-3}) and (chain=iChain)}.atomIndex
                 scBondIdxs += {(atomno=@{iNo-4}) and (chain=gChain)}.atomIndex
+
                 scBondIdxs += {(atomno=@{iNo-4}) and (chain=iChain)}.atomIndex
 
             }
 
             }
 
             else {
 
             else {
                 scBondIdxs += {(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex
+
                 scBondIdxs += {(atomno=@{iNo-1}) and (chain=iChain)}.atomIndex
                 scBondIdxs += {(atomno=@{iNo-2}) and (chain=gChain)}.atomIndex
+
                 scBondIdxs += {(atomno=@{iNo-2}) and (chain=iChain)}.atomIndex
 
             }
 
             }
 
             break
 
             break
 
         }
 
         }
   
+
 
 
     }
 
     }
   
+
 
     return scBondIdxs  
+
     return scBondIdxs
 
}
 
}
  
Line 1,094: Line 1,122:
  
 
     var iNo = {atomIndex=gSCidx}.atomno
 
     var iNo = {atomIndex=gSCidx}.atomno
      
+
     var iChain  = {atomIndex=gSCidx}.chain
 +
 
 
     if ({atomIndex=gSCidx}.group != "PRO") {
 
     if ({atomIndex=gSCidx}.group != "PRO") {
   
+
 
         var scBondIdxs = collectSCrotors( iNo)
+
         var scBondIdxs = collectSCrotors( iNo, iChain)
 
         var numChi = scBondIdxs.size / 4
 
         var numChi = scBondIdxs.size / 4
 
         var dist = distance({atomIndex=gSCidx}.xyz, gSCpt)
 
         var dist = distance({atomIndex=gSCidx}.xyz, gSCpt)
 +
 
         # For all rotor combinations
 
         # For all rotor combinations
 
         var dh = array()
 
         var dh = array()
Line 1,116: Line 1,146:
 
                     scBondIdxs[2+(4*i)], scBondIdxs[1+(4*i)], rot)
 
                     scBondIdxs[2+(4*i)], scBondIdxs[1+(4*i)], rot)
 
                 var newDist = distance({atomIndex=gSCidx}.xyz, gSCpt)
 
                 var newDist = distance({atomIndex=gSCidx}.xyz, gSCpt)
               
+
 
 
                 # Find the best
 
                 # Find the best
 
                 if (newDist < dist) {
 
                 if (newDist < dist) {
Line 1,129: Line 1,159:
 
             }
 
             }
 
         }
 
         }
       
+
 
 
         # Now set the best
 
         # Now set the best
 
         for (var i = 0; i < numChi; i++) {
 
         for (var i = 0; i < numChi; i++) {
Line 1,138: Line 1,168:
 
     }
 
     }
 
     else { # PRO - toggle between puckers up and down
 
     else { # PRO - toggle between puckers up and down
         var icd = {(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex
+
         var icd = {(atomno=@{iNo+1}) and (chain=iChain)}.atomIndex
         var icb = {(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex
+
         var icb = {(atomno=@{iNo-1}) and (chain=iChain)}.atomIndex
         var ica = {(atomno=@{iNo-4}) and (chain=gChain)}.atomIndex
+
         var ica = {(atomno=@{iNo-4}) and (chain=iChain)}.atomIndex
         var in = {(atomno=@{iNo-5}) and (chain=gChain)}.atomIndex
+
         var in = {(atomno=@{iNo-5}) and (chain=iChain)}.atomIndex
 
         select {atomIndex=gSCidx}
 
         select {atomIndex=gSCidx}
     
+
 
 
         if (angle({atomIndex=ica}, {atomIndex=in},
 
         if (angle({atomIndex=ica}, {atomIndex=in},
 
             {atomIndex=icd}, {atomIndex=gSCidx}) < -10.0) {
 
             {atomIndex=icd}, {atomIndex=gSCidx}) < -10.0) {
Line 1,156: Line 1,186:
 
         }
 
         }
 
     }
 
     }
   
+
 
 
     draw gSCcircle CIRCLE {atomIndex=gSCidx} MESH NOFILL
 
     draw gSCcircle CIRCLE {atomIndex=gSCidx} MESH NOFILL
 
     gSCpt = {atomIndex=gSCidx}.xyz
 
     gSCpt = {atomIndex=gSCidx}.xyz
 
}
 
}
+
 
 
# Fix side chain collisions
 
# Fix side chain collisions
 
function fixSCcollision2(idx) {
 
function fixSCcollision2(idx) {
 
     gOk2 = FALSE
 
     gOk2 = FALSE
 
     var iNo = {atomIndex=idx}.atomno
 
     var iNo = {atomIndex=idx}.atomno
     var resno = {(atomno=iNo) and (chain=gChain)}.resno
+
    var iChain = {atomIndex=idx}.chain
   
+
     var resno = {(atomno=iNo) and (chain=iChain)}.resno
 +
 
 
     # Get SC terminus
 
     # Get SC terminus
     while (resno == {(atomno=iNo) and (chain=gChain)}.resno) {
+
     while (resno == {(atomno=iNo) and (chain=iChain)}.resno) {
 
         iNo++
 
         iNo++
 
     }
 
     }
 
     iNo--
 
     iNo--
   
+
 
 
     var sc = array()
 
     var sc = array()
 
     var iBno = iNo
 
     var iBno = iNo
     while ({(atomno=iBno) and (chain=gChain)}.atomName != "CB") {
+
     while ({(atomno=iBno) and (chain=iChain)}.atomName != "CB") {
         sc += {(atomno=iBno) and (chain=gChain)}
+
         sc += {(atomno=iBno) and (chain=iChain)}
 
         iBno--
 
         iBno--
 
     }
 
     }
     var cbidx = {(atomno=iBno) and (chain=gChain)}.atomIndex
+
     var cbidx = {(atomno=iBno) and (chain=iChain)}.atomIndex
   
+
 
     var scBondIdxs = collectSCrotors( iNo)
+
     var scBondIdxs = collectSCrotors( iNo, iChain)
 
     var numChi = scBondIdxs.size / 4
 
     var numChi = scBondIdxs.size / 4
 +
 
     # For all rotor combinations
 
     # For all rotor combinations
 
     for (var i = 0; i < numChi; i++) {
 
     for (var i = 0; i < numChi; i++) {
Line 1,189: Line 1,221:
 
             rot += 60
 
             rot += 60
 
             selectAddSideChain(scBondIdxs[1+(4*i)])
 
             selectAddSideChain(scBondIdxs[1+(4*i)])
            #setDihedralIdx(scBondIdxs[4+(4*i)], scBondIdxs[3+(4*i)],
 
            #    scBondIdxs[2+(4*i)], scBondIdxs[1+(4*i)], rot)
 
 
             setDihedralIdx(scBondIdxs[1+(4*i)], scBondIdxs[2+(4*i)],
 
             setDihedralIdx(scBondIdxs[1+(4*i)], scBondIdxs[2+(4*i)],
 
                 scBondIdxs[3+(4*i)], scBondIdxs[4+(4*i)], rot)
 
                 scBondIdxs[3+(4*i)], scBondIdxs[4+(4*i)], rot)
 +
               
 
             # If no collision, exit
 
             # If no collision, exit
 
             colliders = (within(kCtolerance, FALSE, {sc})
 
             colliders = (within(kCtolerance, FALSE, {sc})
 
                 and not {atomIndex=cbidx} and not {sc})
 
                 and not {atomIndex=cbidx} and not {sc})
                       
+
 
 
             # If it is with water, delete the water
 
             # If it is with water, delete the water
 
             for (var c = 1; c < colliders.size; c++ ) {
 
             for (var c = 1; c < colliders.size; c++ ) {
Line 1,204: Line 1,235:
 
                 }
 
                 }
 
             }
 
             }
               
+
 
 
             if (colliders.size == 0) {
 
             if (colliders.size == 0) {
 
                 gOk2 = TRUE
 
                 gOk2 = TRUE
 
                 return # Early exit since break 1 appears broken
 
                 return # Early exit since break 1 appears broken
                #break 1
 
 
             }
 
             }
           
+
 
 
         }
 
         }
 
     }
 
     }
Line 1,248: Line 1,278:
 
     var rotors = array()
 
     var rotors = array()
 
     if (cargoNo < anchorNo) {
 
     if (cargoNo < anchorNo) {
   
+
 
        # If cWard cargo is C, include its psi
 
        if ({(atomno=cargoNo) and (chain=gChain)}.atomName == "C") {
 
            #cargoNo--
 
        }
 
       
 
 
         for (var iNo = cargoNo; iNo <= anchorNo; iNo++) {
 
         for (var iNo = cargoNo; iNo <= anchorNo; iNo++) {
 
             if ({(atomno=iNo) and (chain=gChain)}.atomName == "CA") {
 
             if ({(atomno=iNo) and (chain=gChain)}.atomName == "CA") {
Line 1,274: Line 1,299:
 
     }
 
     }
 
     else {
 
     else {
   
+
 
        # If nWard cargo is C, include its phi
 
        if ({(atomno=cargoNo) and (chain=gChain)}.atomName == "N") {
 
            #cargoNo++
 
        }
 
       
 
 
         for (var iNo = cargoNo; iNo >= anchorNo; iNo--) {
 
         for (var iNo = cargoNo; iNo >= anchorNo; iNo--) {
 
             if ({(atomno=iNo) and (chain=gChain)}.atomName == "CA") {
 
             if ({(atomno=iNo) and (chain=gChain)}.atomName == "CA") {
Line 1,299: Line 1,319:
 
         }
 
         }
 
     }
 
     }
   
+
 
 
     if (nWard) {
 
     if (nWard) {
 
         gNrotors = rotors
 
         gNrotors = rotors
        if (FALSE) {#DEBUG
 
            print ("gNrotors atomnos")#DEBUG
 
            for (var i = 1; i < gNrotors.size; i += 4) {#DEBUG
 
                print format("%d %d %s", {atomIndex=@{gNrotors[i]}}.atomno,
 
                    {atomIndex=@{gNrotors[i+1]}}.atomno, format("%d %d",
 
                    {atomIndex=@{gNrotors[i+2]}}.atomno,
 
                    {atomIndex=@{gNrotors[i+3]}}.atomno))
 
            }
 
        }#DEBUG
 
 
     }
 
     }
 
     else {
 
     else {
 
         gCrotors = rotors
 
         gCrotors = rotors
        if (FALSE) {#DEBUG
 
            print ("gCrotors atomnos")#DEBUG
 
            for (var i = 1; i < gCrotors.size; i += 4) {#DEBUG
 
                print format("%d %d %s", {atomIndex=@{gCrotors[i]}}.atomno,
 
                    {atomIndex=@{gCrotors[i+1]}}.atomno, format("%d %d",
 
                    {atomIndex=@{gCrotors[i+2]}}.atomno,
 
                    {atomIndex=@{gCrotors[i+3]}}.atomno))
 
            }
 
        }#DEBUG
 
 
     }
 
     }
 
}
 
}
Line 1,332: Line 1,334:
  
 
function tugSideChain(pt) {
 
function tugSideChain(pt) {
               
+
 
 
     # If destination atom defined
 
     # If destination atom defined
 
     if (gDestAtomIdx >= 0) {
 
     if (gDestAtomIdx >= 0) {
         v = {atomIndex=gDestAtomIdx}.xyz - {atomIndex=gSCidx}.xyz
+
         var v = {atomIndex=gDestAtomIdx}.xyz - {atomIndex=gSCidx}.xyz
 
         if (abs(angle({atomIndex=gDestAtomIdx}.xyz, {0 0 0}, pt)) < 90) {
 
         if (abs(angle({atomIndex=gDestAtomIdx}.xyz, {0 0 0}, pt)) < 90) {
 
             pt = -v/20.0
 
             pt = -v/20.0
Line 1,344: Line 1,346:
 
     }
 
     }
 
     gSCpt += pt
 
     gSCpt += pt
     draw arrow {atomIndex=gSCidx} @gSCpt  
+
     draw arrow {atomIndex=gSCidx} @gSCpt
 
}
 
}
  
Line 1,350: Line 1,352:
 
     select all
 
     select all
 
     color {selected} @gScheme
 
     color {selected} @gScheme
    color {atomIndex=gCanchorIdx} @gAltScheme
 
    color {atomIndex=gNanchorIdx} @gAltScheme
 
 
     color {atomIndex=g1pivotIdx} green
 
     color {atomIndex=g1pivotIdx} green
 
     color {atomIndex=g2pivotIdx} green
 
     color {atomIndex=g2pivotIdx} green
     color @gCargoAtoms @gAltScheme
+
     color @gCargoSet @gAltScheme
     select {(atomIndex=gCcargoIdx) or (atomIndex=gNcargoIdx)}
+
     select {(atomIndex=gCcargoIdx) or (atomIndex=gNcargoIdx)
 +
        or (atomIndex=gCanchorIdx) or (atomIndex=gNanchorIdx)}
 
     halo on
 
     halo on
 
     select {atomIndex=gDestAtomIdx}
 
     select {atomIndex=gDestAtomIdx}
Line 1,379: Line 1,380:
 
     background ECHO yellow
 
     background ECHO yellow
 
     restore state gState
 
     restore state gState
     select gCargoAtoms
+
     select gCargoSet
 
     refresh
 
     refresh
 
     quit
 
     quit
Line 1,390: Line 1,391:
 
     ls += format("gNanchorIdx = %d;", gNanchorIdx)
 
     ls += format("gNanchorIdx = %d;", gNanchorIdx)
 
     ls += format("gNanchorNo = %d;", gNanchorNo)
 
     ls += format("gNanchorNo = %d;", gNanchorNo)
    ls += format("gNanchorPidx = %d;", gNanchorPidx)
 
    ls += format("gCanchorXyz = %s;", gCanchorXyz)
 
    ls += format("gNanchorXyz = %s;", gNanchorXyz)
 
    ls += format("gNanchorPxyz = %s;", gNanchorPxyz)
 
 
     ls += format("gCcargoIdx = %d;", gCcargoIdx)
 
     ls += format("gCcargoIdx = %d;", gCcargoIdx)
 
     ls += format("gNcargoIdx = %d;", gNcargoIdx)
 
     ls += format("gNcargoIdx = %d;", gNcargoIdx)
    ls += format("gCcargoXyz = %s;", gCcargoXyz)
 
    ls += format("gNcargoXyz = %s;", gNcargoXyz)
 
 
     ls += format("gCcargoNo = %d;", gCcargoNo)
 
     ls += format("gCcargoNo = %d;", gCcargoNo)
 
     ls += format("gNcargoNo = %d;", gNcargoNo)
 
     ls += format("gNcargoNo = %d;", gNcargoNo)
Line 1,403: Line 1,398:
 
     ls += format("g1pivotIdx = %d;", g1pivotIdx)
 
     ls += format("g1pivotIdx = %d;", g1pivotIdx)
 
     ls += format("g2pivotIdx = %d;", g2pivotIdx)
 
     ls += format("g2pivotIdx = %d;", g2pivotIdx)
     ls += format("gAc = %s;", gAc)
+
     ls += format("gOkCollide = %s;", gOkCollide)
 
     ls += format("gChain = \"%s\";", gChain)
 
     ls += format("gChain = \"%s\";", gChain)
 
     ls += format("gMinNo = %d;", gMinNo)
 
     ls += format("gMinNo = %d;", gMinNo)
     ls += format("gMaxNo = %d;", gMaxNo)  
+
     ls += format("gMaxNo = %d;", gMaxNo)
     ls += format("gCargoAtoms = %s;", gCargoAtoms)
+
     ls += format("gCargoSet = %s;", gCargoSet)
 
     ls += format("gSCidx = %d;", gSCidx)
 
     ls += format("gSCidx = %d;", gSCidx)
 
     ls += format("gSCcircle = %d;", gSCcircle)
 
     ls += format("gSCcircle = %d;", gSCcircle)
Line 1,418: Line 1,413:
 
# Bound to LEFT-UP by tugEnableDrag
 
# Bound to LEFT-UP by tugEnableDrag
 
function tugDragDoneMB() {
 
function tugDragDoneMB() {
     if (gPlicoRecord != "") {
+
     if (gBusy == FALSE) {
        recordDrag()
+
        if (gPlicoRecord != "") {
    }
+
            recordDrag()
+
        }
    # Move by rotation on rotor sets, smallest first
+
 
    gBusy = TRUE
+
        # Move by rotation on rotor sets, smallest first
    background ECHO pink
+
        gBusy = TRUE
    refresh
+
        background ECHO pink
 +
        refresh
  
    # If side chain mode
+
        # If side chain mode
    if (gSCidx >= 0) {
+
        if (gSCidx >= 0) {
        dragSC()
+
            dragSC()
    }
 
   
 
    # Else
 
    else {
 
        gOK = TRUE
 
        timeout ID"tug" 20.0 "timedOut(\"Tug timed out\")"
 
        gCountdown = kCollisionLimit
 
        if ((gCrotors.size < gNrotors.size) or (gNanchorIdx < 0)) {
 
            if (gCrotors.size > 4) {
 
                tugTrackC()  # PULLSTRING MODEL
 
            }
 
            if (gOK and (gNrotors.size > 4)) {
 
                tugTrackN()  # PULLSTRING MODEL
 
            }
 
 
         }
 
         }
         else {
+
 
            if (gNrotors.size > 4) {
+
        # Else
                tugTrackN()  # PULLSTRING MODEL
+
         else if (not gTow) {
            }
+
             gOK = TRUE
             if (gOK and (gCrotors.size > 4)) {
+
             timeout ID"tug" 20.0 "timedOut(\"Tug timed out\")"
                tugTrackC()  # PULLSTRING MODEL
+
             if ((gCrotors.size < gNrotors.size) or (gNanchorIdx < 0)) {
             }
+
                 if (gCrotors.size > 4) {
        }
+
                     tugTrackC() # PULLSTRING MODEL
        timeout ID"tug" OFF
 
       
 
        # If anchor angles acute, fail
 
        if (gOK == TRUE) {
 
             if (gCanchorIdx >= 0) {
 
                var ic = getCwardBBidx(gCanchorIdx)
 
                var in = getNwardBBidx(gCanchorIdx)
 
                 if ((ic >= 0) and
 
                     angle({atomIndex=ic}, {atomIndex=gCanchorIdx}, {atomIndex=in})
 
                    < 100.0) {
 
                    gOK = FALSE
 
 
                 }
 
                 }
            }
+
                 if (gOK and (gNrotors.size > 4)) {
            if (gNanchorIdx >= 0) {
+
                     tugTrackN() # PULLSTRING MODEL
                var ic = getCwardBBidx(gNanchorIdx)
 
                var in = getNwardBBidx(gNanchorIdx)
 
                 if ((in >= 0) and
 
                     angle({atomIndex=ic}, {atomIndex=gNanchorIdx}, {atomIndex=in})
 
                    < 100.0) {
 
                    gOK = FALSE
 
 
                 }
 
                 }
 
             }
 
             }
        }
+
            else {
       
+
                if (gNrotors.size > 4) {
        # If too far
+
                    tugTrackN()  # PULLSTRING MODEL
        if (gOK == FALSE) {
+
                }
            timedOut("TUG TOO FAR!")
+
                if (gOK and (gCrotors.size > 4)) {
        }
+
                    tugTrackC()  # PULLSTRING MODEL
       
+
                }
        # Else OK
+
            }
        else {
+
            timeout ID"tug" OFF
 +
 
 +
            # If anchor angles acute, fail
 +
            if (gOK == TRUE) {
 +
                if (gCanchorIdx >= 0) {
 +
                    var ic = getCwardBBidx(gCanchorIdx)
 +
                    var in = getNwardBBidx(gCanchorIdx)
 +
                    if ((ic >= 0) and
 +
                        angle({atomIndex=ic}, {atomIndex=gCanchorIdx}, {atomIndex=in})
 +
                        < 100.0) {
 +
                        gOK = FALSE
 +
                    }
 +
                }
 +
                if (gNanchorIdx >= 0) {
 +
                    var ic = getCwardBBidx(gNanchorIdx)
 +
                    var in = getNwardBBidx(gNanchorIdx)
 +
                    if ((in >= 0) and
 +
                        angle({atomIndex=ic}, {atomIndex=gNanchorIdx}, {atomIndex=in})
 +
                        < 100.0) {
 +
                        gOK = FALSE
 +
                    }
 +
                }
 +
            }
 +
 
 +
            # If too far
 +
            if (gOK == FALSE) {
 +
                timedOut("TUG TOO FAR!")
 +
            }
 +
 
 +
            # Else OK
 +
            else {
 +
 
 +
                select ((atomno >= gNanchorNo) and (atomno <= gCanchorNo)
 +
                    and (chain = gChain))
 +
                var idx = {(atomno=@{{chain=gChain}.atomno.min})
 +
                    and (chain=gChain)}.atomIndex
  
            select ((atomno >= gNanchorNo) and (atomno <= gCanchorNo)
 
                and (chain = gChain))
 
            var idx = {atomno=@{{chain=gChain}.min.atomno}}.atomIndex
 
  
           
+
                var ihc = 0
            var i = 0
+
                for (ihc = 0; ihc < 10; ihc++) {
            for (; i < 3; i++) {
+
                    handleCollisions2(FALSE, idx)
                handleCollisions(FALSE, idx)
+
                    if (countCollisions(({})) == 0) {
                if (countCollisions() == 0) {
+
                        break
                     break
+
                     }
 +
                }
 +
                if (ihc == 10) {
 +
                    var p = prompt("Unable to handle all collisions!")
 +
                    restore state gState
 
                 }
 
                 }
 
             }
 
             }
            if (i == 3) {
 
                var p = prompt("Unable to handle all collisions!")
 
                restore state gState
 
            }                   
 
 
         }
 
         }
 +
        select {gCargoSet}
 +
        gBusy = FALSE
 +
        background ECHO yellow
 +
        refresh
 
     }
 
     }
    select {gCargoAtoms}
+
}
    gBusy = FALSE
+
 
    background ECHO yellow
+
# Bound to ALT-SHIFT-LEFT-DRAG by tugEnableDrag
     refresh
+
function tugDrag2MB() {
 +
     tugDragMB(TRUE)
 
}
 
}
  
 
# Bound to ALT-LEFT-DRAG by tugEnableDrag
 
# Bound to ALT-LEFT-DRAG by tugEnableDrag
function tugDragMB() {
+
function tugDragMB(alt) {
 
     if (gBusy == FALSE) {
 
     if (gBusy == FALSE) {
         var dx = (20.0 * (_mouseX - gMouseX))/_width
+
        gBusy = TRUE
         var dy = (20.0 * (_mouseY - gMouseY))/_height
+
         var dx = (40.0 * (_mouseX - gMouseX))/_width
         var q = quaternion(script("show rotation"))
+
         var dy = (40.0 * (_mouseY - gMouseY))/_height
 +
         var q = quaternion()
 
         var ptd = {@dx @dy 0}
 
         var ptd = {@dx @dy 0}
 
         var pt = (!q)%ptd
 
         var pt = (!q)%ptd
   
+
        var axis = {0 0 0}
 
         if (distance(pt,  {0 0 0}) > 0.004) {
 
         if (distance(pt,  {0 0 0}) > 0.004) {
 
 
             # If sidechain mode
 
             # If sidechain mode
 
             if (gSCidx >= 0) {
 
             if (gSCidx >= 0) {
                 tugSideChain(pt)
+
                 if ({atomIndex=gSCidx}.atomName == "O") {
 +
                    dir = ((abs(dx) > abs(dy))
 +
                        ? ((dx < 0) ? 2 : -2)
 +
                        : ((dy < 0) ? 2 : -2))
 +
                    counterRotate(gSCidx, dir, not alt)
 +
                }
 +
                else {
 +
                    tugSideChain(pt)
 +
                }
 
             }
 
             }
  
             # Else          
+
             # Else
 
             else {
 
             else {
           
+
 
 
                 # If new drag
 
                 # If new drag
 
                 if (gNewDrag) {
 
                 if (gNewDrag) {
Line 1,533: Line 1,543:
 
                     save state gState
 
                     save state gState
 
                 }
 
                 }
               
+
 
                #mp = ({atomIndex=gNcargoIdx}.xyz + {atomIndex=gCcargoIdx}.xyz )/2.0
 
               
 
 
                 # If destination atom defined
 
                 # If destination atom defined
 
                 if (gDestAtomIdx >= 0) {
 
                 if (gDestAtomIdx >= 0) {
Line 1,546: Line 1,554:
 
                     }
 
                     }
 
                 }
 
                 }
               
+
 
 
                 # Move the cargo
 
                 # Move the cargo
                 select {gCargoAtoms}
+
                 select {gCargoSet}
               
+
 
 
                 # If pivots defined, rotate it
 
                 # If pivots defined, rotate it
 
                 if (g1pivotIdx >= 0) {
 
                 if (g1pivotIdx >= 0) {
               
+
 
 
                     # If two pivots
 
                     # If two pivots
                    var axis = {0 0 0}
 
 
                     if (g2pivotIdx >= 0) {
 
                     if (g2pivotIdx >= 0) {
 
                         axis = {atomIndex=g2pivotIdx}
 
                         axis = {atomIndex=g2pivotIdx}
 
                     }
 
                     }
                   
+
 
 
                     # Else
 
                     # Else
 
                     else {
 
                     else {
 
                         axis = cross(pt, {0 0 0}) + {atomIndex=g1pivotIdx}.xyz
 
                         axis = cross(pt, {0 0 0}) + {atomIndex=g1pivotIdx}.xyz
 
                     }
 
                     }
                   
+
 
                     var a = ((abs(angle(pt + {atomIndex=g1pivotIdx}.xyz,
+
                     dir = ((abs(dx) > abs(dy))
                         {atomIndex=g1pivotIdx}.xyz, axis)) < 90) ? -2 : 2)
+
                         ? ((dx < 0) ? 2 : -2)
                    #rotateSelected {atomIndex=g1pivotIdx} @axis @a
+
                        : ((dy < 0) ? 2 : -2))
                     rotateSelectedRecord(g1pivotIdx, axis, a)
+
                     rotateSelectedRecord(g1pivotIdx, axis, dir)
       
+
 
 
                 }
 
                 }
               
+
 
 
                 # Else translate it
 
                 # Else translate it
 
                 else {
 
                 else {
                    #translateSelected @pt
 
 
                     translateSelectedRecord(pt)
 
                     translateSelectedRecord(pt)
 
                 }
 
                 }
               
+
 
 
                 # If collisions
 
                 # If collisions
                 var aset = {((atomno < gCanchorNo) and (atomno > gNanchorNo)
+
                 var cNotSels = (within(kCtolerance, FALSE, {selected})
                    and (chain=gChain))} 
+
                    and not {gMovingSet})
                var ac = (within(kCtolerance, FALSE, {aset}) and not {aset}
+
                 if (cNotSels.size > 0) {
                    and not connected(aset))
+
                     gOk2 = TRUE
                 if (ac.size > 0) {
+
                     for (var i = 1; i <= cNotSels.size;  i++) {
                     # Resolve them
+
 
                     for (var i = 1; i <= ac.size;  i++) {
+
                        # If net collision vector same as move vector
                         select ac[i]
+
                        cSels = (within(kCtolerance, FALSE, cNotSels[i]) and {selected})
                         handleCollisions()
+
                         for (var j = 1; j <= cSels.size;  j++) {
                     }
+
                            var v1 = cNotSels[i].xyz - cSels[j].xyz
                   
+
                            if (abs(angle(v1, {0 0 0}, pt)) < 90) {
                     # If unable
+
 
                     if (gOk2 == FALSE) {
+
                                # If tow mode
                   
+
                                if (gTow) {
                         # Back off
+
 
                         background ECHO pink
+
                                    # Make a dynamic pivot
 +
                                    if (g1pivotIdx < 0) {
 +
                                        g1pivotIdx = cSels[j].atomIndex
 +
                                        g1dynamicIdx = cNotSels[i].atomIndex
 +
                                        color {atomIndex=g1pivotIdx} lightgreen
 +
                                        setDistanceIdx(cNotSels[i].atomIndex,
 +
                                            cSels[j].atomIndex,
 +
                                            kCtolerance + kDtolerance)
 +
                                    }
 +
                                    else if (g2pivotIdx < 0) {
 +
                                        g2pivotIdx = cSels[j].atomIndex
 +
                                        g2dynamicIdx = cNotSels[i].atomIndex
 +
                                        color {atomIndex=g2pivotIdx} lightgreen
 +
                                        setDistanceIdx(cNotSels[i].atomIndex,
 +
                                            cSels[j].atomIndex,
 +
                                            kCtolerance + kDtolerance)
 +
                                    }
 +
                                    else {
 +
                                        gOk2 = FALSE
 +
                                    }
 +
                                }
 +
                                else {
 +
 
 +
                                    # Try to resolve
 +
                                    select cSels[j]
 +
                                    var idx = {(atomno=@{{chain=gChain}.atomno.min})
 +
                                        and (chain=gChain)}.atomIndex
 +
                                    handleCollisions2(FALSE, idx)
 +
                                }
 +
                            }
 +
                         } # endfor
 +
                        if (gOk2 == FALSE) {
 +
                            break
 +
                        }
 +
                     } # endfor
 +
 
 +
                     # If unable and not alt
 +
                     if ((gOk2 == FALSE) and (not alt)) {
 +
 
 +
                         # Back off
 +
                         background ECHO pink
 
                         delay 1
 
                         delay 1
 
                         if (g1pivotIdx >= 0) {
 
                         if (g1pivotIdx >= 0) {
Line 1,604: Line 1,650:
 
                     }
 
                     }
 
                 }
 
                 }
                #tugDragDoneMB()
 
 
             }
 
             }
              
+
 
 +
             # If dynamic pivotsTBD
 +
            if (g1dynamicIdx >= 0) {
 +
                var v1 = {atomIndex=g1dynamicIdx}.xyz - {atomIndex=g1pivotIdx}.xyz
 +
                if (abs(angle(v1, {0 0 0}, pt)) > 90) {
 +
                    color {atomIndex=g1pivotIdx} @gAltScheme
 +
                    g1pivotIdx = -1
 +
                    g1dynamicIdx = -1
 +
 
 +
                }
 +
            }
 +
            if (g2dynamicIdx >= 0) {
 +
                var v1 = {atomIndex=g2dynamicIdx}.xyz - {atomIndex=g2pivotIdx}.xyz
 +
                if (abs(angle(v1, {0 0 0}, pt)) > 90) {
 +
                    color {atomIndex=g2pivotIdx} @gAltScheme
 +
                    g2pivotIdx = -1
 +
                    g2dynamicIdx = -1
 +
                }
 +
            }
 +
 
 
             gMouseX = _mouseX
 
             gMouseX = _mouseX
 
             gMouseY = _mouseY
 
             gMouseY = _mouseY
 
         }
 
         }
         select {gCargoAtoms}
+
         select {gCargoSet}
 +
        gBusy = FALSE
 
     }
 
     }
 
}
 
}
Line 1,618: Line 1,683:
 
     gMouseX = _mouseX
 
     gMouseX = _mouseX
 
     gMouseY = _mouseY
 
     gMouseY = _mouseY
    gNcargoXyz = {atomIndex=gNcargoIdx}.xyz
 
    gCcargoXyz = {atomIndex=gCcargoIdx}.xyz
 
 
     gNewDrag = TRUE
 
     gNewDrag = TRUE
 
}
 
}
  
# Called by tugCargoMB and by tugAnchorMB or tugPivotMB when cargo exists
+
# Called by tugCargoMB
 
function tugEnableDrag() {
 
function tugEnableDrag() {
     gEcho = "ALT-CLICK=set cargo range|ALT-DRAG=move|SHIFT=set anchors|ALT-CTRL=set pivots|ALT-SHIFT=set dest atom |DOUBLE-CLICK=exit"
+
     gEcho = "__________TUG__________|ALT-CLICK=mark block|SHIFT-CLICK=anchors" +
 +
        "|ALT-CTRL-CLICK=pivots|ALT-SHIFT-CLICK=dest atom|ALT-DRAG=move" +
 +
        "|SHIFT-ALT-DRAG=alt move|DOUBLE-CLICK=exit"
 
     echo @gEcho
 
     echo @gEcho
   
+
 
 
     # Allow atoms to be dragged
 
     # Allow atoms to be dragged
 
     bind "ALT-LEFT-DOWN" "tugMarkMB";
 
     bind "ALT-LEFT-DOWN" "tugMarkMB";
     bind "ALT-LEFT-UP" "tugDragDoneMB";#ONCEMODE
+
     bind "ALT-LEFT-UP" "tugDragDoneMB";
 
     bind "ALT-LEFT-DRAG" "tugDragMB";
 
     bind "ALT-LEFT-DRAG" "tugDragMB";
}
+
    bind "ALT-SHIFT-LEFT-DRAG" "tugDrag2MB";
 +
 
 +
    unbind "SHIFT-LEFT-CLICK"
 +
    bind "SHIFT-LEFT-CLICK" "_pickAtom";
 +
    bind "SHIFT-LEFT-CLICK" "+:tugAnchorMB";
  
# Bound to SHIFT-LEFT-CLICK by tugCargoMB
+
    bind "ALT-CTRL-LEFT-CLICK" "_pickAtom";
function tugAnchorMB() {
+
    bind "ALT-CTRL-LEFT-CLICK" "+:tugPivotMB";
     if ({atomIndex=_atomPicked}.chain == gChain) {
+
 
 +
    bind "ALT-SHIFT-LEFT-CLICK" "_pickAtom";
 +
    bind "ALT-SHIFT-LEFT-CLICK" "+:tugDestAtomMB";
 +
}
 +
 
 +
# Bound to SHIFT-LEFT-CLICK by tugCargoMB
 +
function tugAnchorMB() {
 +
     if ({atomIndex=_atomPicked}.chain == gChain) {
 
         var aPidx = getScBBidx( _atomPicked)
 
         var aPidx = getScBBidx( _atomPicked)
       
+
 
 
         var pno = {atomIndex=aPidx}.atomno
 
         var pno = {atomIndex=aPidx}.atomno
 
         if (pno > {atomIndex=gCcargoIdx}.atomno) {
 
         if (pno > {atomIndex=gCcargoIdx}.atomno) {
             color {atomIndex=gCanchorIdx} @gScheme
+
             select {atomIndex=gCanchorIdx}
 +
            halo off
 
             if (gCanchorIdx == aPidx) {
 
             if (gCanchorIdx == aPidx) {
 
                 gCanchorIdx = -1
 
                 gCanchorIdx = -1
Line 1,649: Line 1,726:
 
                 gCanchorIdx = aPidx
 
                 gCanchorIdx = aPidx
 
                 gCanchorNo = {atomIndex=gCanchorIdx}.atomno
 
                 gCanchorNo = {atomIndex=gCanchorIdx}.atomno
                 gCanchorXyz = {atomIndex=gCanchorIdx}.xyz
+
                 select {atomIndex=gCanchorIdx}
                 color {atomIndex=gCanchorIdx} @gAltScheme
+
                 halo on
 
             }
 
             }
 
             collectBBrotors(FALSE)
 
             collectBBrotors(FALSE)
 
         }
 
         }
         if (pno < {atomIndex=gNcargoIdx}.atomno) {
+
         else if (pno < {atomIndex=gNcargoIdx}.atomno) {
             color {atomIndex=gNanchorIdx} @gScheme
+
             select {atomIndex=gNanchorIdx}
 +
            halo off
 
             if (gNanchorIdx == aPidx) {
 
             if (gNanchorIdx == aPidx) {
 
                 gNanchorIdx = -1
 
                 gNanchorIdx = -1
Line 1,663: Line 1,741:
 
                 gNanchorIdx = aPidx
 
                 gNanchorIdx = aPidx
 
                 gNanchorNo = {atomIndex=gNanchorIdx}.atomno
 
                 gNanchorNo = {atomIndex=gNanchorIdx}.atomno
                 gNanchorPidx = getCwardBBidx( aPidx)
+
                 select {atomIndex=gNanchorIdx}
                gNanchorXyz = {atomIndex=gNanchorIdx}.xyz
+
                 halo on
                gNanchorPxyz = {atomIndex=gNanchorPidx}.xyz
 
                 color {atomIndex=gNanchorIdx} @gAltScheme
 
 
             }
 
             }
 
             collectBBrotors(TRUE)
 
             collectBBrotors(TRUE)
 
         }
 
         }
 +
        else {
 +
            towCargoMB()
 +
        }
 +
 +
        # Get moving atoms set
 +
        gMovingSet = {((atomno < gCanchorNo) and (atomno > gNanchorNo)
 +
            and (chain=gChain))}
 
     }
 
     }
   
+
     select {gCargoSet}
    # Get connectors between fixed and moving part
 
    var aset = {((atomno < gCanchorNo) and (atomno > gNanchorNo)
 
        and (chain=gChain))} 
 
    gAc = (within(kCtolerance, FALSE, {aset}) and not {aset})
 
     select {gCargoAtoms}
 
 
}
 
}
  
Line 1,700: Line 1,778:
 
             star on
 
             star on
 
         }
 
         }
         select {gCargoAtoms}
+
         select {gCargoSet}
 
     }
 
     }
 
}
 
}
Line 1,724: Line 1,802:
 
             color {atomIndex=g2pivotIdx} @gScheme
 
             color {atomIndex=g2pivotIdx} @gScheme
 
         }
 
         }
           
+
 
 
         g2pivotIdx = _atomPicked
 
         g2pivotIdx = _atomPicked
 
         color {atomIndex=g2pivotIdx} green
 
         color {atomIndex=g2pivotIdx} green
Line 1,732: Line 1,810:
 
         color {atomIndex=g1pivotIdx} green
 
         color {atomIndex=g1pivotIdx} green
 
     }
 
     }
     select {gCargoAtoms}
+
     select {gCargoSet}
 +
}
 +
 
 +
# Bound to SHIFT-LEFT-CLICK by plicoTug
 +
function towCargoMB() {
 +
    gTow = TRUE
 +
    gChain = {atomIndex=_atomPicked}.chain
 +
    gMinNo = {chain=gChain}.atomno.min
 +
    gMaxNo = {chain=gChain}.atomno.max
 +
    gCcargoIdx = -1
 +
    gNcargoIdx = -1
 +
    gCanchorIdx = -1
 +
    gCanchorNo = gMaxNo + 1
 +
    gNanchorIdx = -1
 +
    gNanchorNo = gMinNo - 1
 +
 
 +
    # Highlight cargo cluster
 +
    select {chain=gChain}
 +
    gCargoSet = {selected}
 +
    gMovingSet = {selected}
 +
    setColors()
 +
 
 +
    # Enable dragging
 +
    tugEnableDrag()
 +
    select {gCargoSet}
 +
    halo off
 +
    var es = gEcho.replace("anchors","mark chain")
 +
    echo @es
 +
    unbind "SHIFT-LEFT-CLICK"
 +
    bind "SHIFT-LEFT-CLICK" "_pickAtom";
 +
    bind "SHIFT-LEFT-CLICK" "+:towCargoMB";
 
}
 
}
  
 
# Bound to ALT-LEFT-CLICK by plicoTug
 
# Bound to ALT-LEFT-CLICK by plicoTug
 
function tugCargoMB() {
 
function tugCargoMB() {
      
+
 
     if (gChain != {atomIndex=_atomPicked}.chain) {
+
     # If O or movable side chain atom picked
        clearAtomIdxs()
+
     if (({atomIndex=_atomPicked}.atomName = "O")
         setColors()
+
         or (isMovableSideChain( _atomPicked))) {
        gChain = {atomIndex=_atomPicked}.chain
 
    }
 
   
 
    # If movable side chain atom picked
 
    if (isMovableSideChain( _atomPicked)) {
 
 
         if (gSCidx >= 0) {
 
         if (gSCidx >= 0) {
 
             draw gSCcircle DELETE
 
             draw gSCcircle DELETE
Line 1,751: Line 1,854:
 
         if (gSCidx == _atomPicked) {
 
         if (gSCidx == _atomPicked) {
 
             gSCidx = -1
 
             gSCidx = -1
         }      
+
         }
 
         else {
 
         else {
 
             gSCidx = _atomPicked
 
             gSCidx = _atomPicked
Line 1,758: Line 1,861:
 
         }
 
         }
 
     }
 
     }
     else if ((gChain == "") or ({atomIndex=_atomPicked}.chain == gChain)) {
+
     else {
         gMinNo = {chain=gChain}.atomno.min  
+
        if ({atomIndex=_atomPicked}.chain != gChain) {
 +
            if (gTow) {
 +
                select {chain=gChain}
 +
                color {selected} @gScheme
 +
            }
 +
            gChain = {atomIndex=_atomPicked}.chain
 +
            select ({atomIndex=gCcargoIdx} or {atomIndex=gNcargoIdx}
 +
                or {atomIndex=gCanchorIdx} or {atomIndex=gNanchorIdx})
 +
            halo off
 +
            gCcargoIdx = -1
 +
            gNcargoIdx = -1
 +
            gCanchorIdx = -1
 +
            gNanchorIdx = -1
 +
        }
 +
        gTow = FALSE
 +
         gMinNo = {chain=gChain}.atomno.min
 
         gMaxNo = {chain=gChain}.atomno.max
 
         gMaxNo = {chain=gChain}.atomno.max
 
         if (gNanchorIdx < 0) {
 
         if (gNanchorIdx < 0) {
Line 1,765: Line 1,883:
 
         }
 
         }
 
         if (gCanchorIdx < 0) {
 
         if (gCanchorIdx < 0) {
             gCanchorNo = gMaxNo + 1  
+
             gCanchorNo = gMaxNo + 1
 
         }
 
         }
 
         var aPidx = getScBBidx( _atomPicked)
 
         var aPidx = getScBBidx( _atomPicked)
       
+
 
 
         gSCidx = -1
 
         gSCidx = -1
 
         draw gSCcircle DELETE
 
         draw gSCcircle DELETE
Line 1,774: Line 1,892:
 
         # If existing cWard cargo picked
 
         # If existing cWard cargo picked
 
         if (gCcargoIdx == aPidx) {
 
         if (gCcargoIdx == aPidx) {
           
+
 
 
             # Clear the highlight
 
             # Clear the highlight
 
             select {atomIndex=gCcargoIdx}
 
             select {atomIndex=gCcargoIdx}
 
             halo off
 
             halo off
           
+
 
 
             # If nWard cargo exists, mark it as the cWard cargo
 
             # If nWard cargo exists, mark it as the cWard cargo
 
             if (gNcargoIdx != gCcargoIdx) {
 
             if (gNcargoIdx != gCcargoIdx) {
Line 1,796: Line 1,914:
 
         }
 
         }
 
         else if (gCcargoIdx >= 0) {
 
         else if (gCcargoIdx >= 0) {
 +
 
             var no = {atomIndex=aPidx}.atomno
 
             var no = {atomIndex=aPidx}.atomno
       
+
 
 
             # If pick is nWard of it
 
             # If pick is nWard of it
 
             if (no < {atomIndex=gCcargoIdx}.atomno) {
 
             if (no < {atomIndex=gCcargoIdx}.atomno) {
           
+
 
 
                 # If exists, clear its highlight
 
                 # If exists, clear its highlight
 
                 if (gNcargoIdx != gCcargoIdx) {
 
                 if (gNcargoIdx != gCcargoIdx) {
Line 1,806: Line 1,925:
 
                     halo off
 
                     halo off
 
                 }
 
                 }
               
+
 
 
                 # Set new nWard cargo and highlight it
 
                 # Set new nWard cargo and highlight it
 
                 gNcargoIdx = getNmIdx(aPidx)
 
                 gNcargoIdx = getNmIdx(aPidx)
 
                 gNcargoNo = {atomIndex=gNcargoIdx}.atomno
 
                 gNcargoNo = {atomIndex=gNcargoIdx}.atomno
 
             }
 
             }
           
+
 
 
             # Else cWard
 
             # Else cWard
 
             else {
 
             else {
           
+
 
 
                 # Clear its old highlight
 
                 # Clear its old highlight
 
                 select {atomIndex=gCcargoIdx}
 
                 select {atomIndex=gCcargoIdx}
Line 1,820: Line 1,939:
 
                     halo off
 
                     halo off
 
                 }
 
                 }
           
+
 
 
                 # Set new cWard cargo and highlight
 
                 # Set new cWard cargo and highlight
 
                 gCcargoIdx = getCpIdx(aPidx)
 
                 gCcargoIdx = getCpIdx(aPidx)
Line 1,826: Line 1,945:
 
             }
 
             }
 
         }
 
         }
       
+
 
 
         # Else no cWard cargo
 
         # Else no cWard cargo
 
         else {
 
         else {
       
+
 
 
             # Set new cWard cargo and highlight
 
             # Set new cWard cargo and highlight
 
             gCcargoIdx = getCpIdx(aPidx)
 
             gCcargoIdx = getCpIdx(aPidx)
Line 1,835: Line 1,954:
 
             gNcargoIdx = getNmIdx(gCcargoIdx)
 
             gNcargoIdx = getNmIdx(gCcargoIdx)
 
             gNcargoNo = {atomIndex=gNcargoIdx}.atomno
 
             gNcargoNo = {atomIndex=gNcargoIdx}.atomno
           
+
 
 
             # Set default cWard anchor at cWard N
 
             # Set default cWard anchor at cWard N
 
             var iNo = gMaxNo
 
             var iNo = gMaxNo
Line 1,845: Line 1,964:
 
             gCanchorIdx = {(atomno=iNo) and (chain=gChain)}.atomIndex
 
             gCanchorIdx = {(atomno=iNo) and (chain=gChain)}.atomIndex
 
             gCanchorNo = {atomIndex=gCanchorIdx}.atomno
 
             gCanchorNo = {atomIndex=gCanchorIdx}.atomno
            gCanchorXyz = {atomIndex=gCanchorIdx}.xyz
 
 
         }
 
         }
       
+
 
 
         # If any anchor now inside cargo cluster, kill it
 
         # If any anchor now inside cargo cluster, kill it
 
         if ({atomIndex=gCanchorIdx}.atomno <= {atomIndex=gCcargoIdx}.atomno) {
 
         if ({atomIndex=gCanchorIdx}.atomno <= {atomIndex=gCcargoIdx}.atomno) {
Line 1,857: Line 1,975:
 
             gNanchorNo = gMinNo - 1
 
             gNanchorNo = gMinNo - 1
 
         }
 
         }
       
+
 
 
         # Highlight cargo cluster
 
         # Highlight cargo cluster
         selectNward(gCcargoIdx, gNcargoIdx)
+
         selectNwardIdx(gCcargoIdx, gNcargoIdx)
         gCargoAtoms = {selected}
+
         gCargoSet = {selected}
 
         setColors()
 
         setColors()
                           
+
 
 
         # Collect the rotor sets
 
         # Collect the rotor sets
 
         collectRotors()
 
         collectRotors()
   
+
 
         # Get connectors between fixed and moving part
+
         # Get moving atoms set
         var aset = {((atomno < gCanchorNo) and (atomno > gNanchorNo)
+
         gMovingSet = {((atomno < gCanchorNo) and (atomno > gNanchorNo)
             and (chain=gChain))}  
+
             and (chain=gChain))}
        gAc = (within(kCtolerance, FALSE, {aset}) and not {aset})
 
 
     }
 
     }
   
+
 
 
     # Enable dragging
 
     # Enable dragging
 
     tugEnableDrag()
 
     tugEnableDrag()
  
    # Bind other keys
+
     select {gCargoSet}
    bind "SHIFT-LEFT-CLICK" "_pickAtom";
 
    bind "SHIFT-LEFT-CLICK" "+:tugAnchorMB";
 
   
 
    bind "ALT-CTRL-LEFT-CLICK" "_pickAtom";
 
    bind "ALT-CTRL-LEFT-CLICK" "+:tugPivotMB";
 
   
 
    bind "ALT-SHIFT-LEFT-CLICK" "_pickAtom";
 
    bind "ALT-SHIFT-LEFT-CLICK" "+:tugDestAtomMB";
 
   
 
     select {gCargoAtoms}
 
 
}
 
}
  
 
# Top level of Tug
 
# Top level of Tug
 
function plicoTug() {
 
function plicoTug() {
     set allowModelKit TRUE
+
 
     set allowRotateSelected TRUE
+
     # Bad idea to proceed when collisions present
     set allowMoveAtoms TRUE
+
     var cc = countCollisions(({}))
      
+
     if (cc > 0) {
 +
        var p = prompt(format("%d collision%s present! Proceed anyway?",
 +
            cc, ((cc > 1) ? "s" : "")), "OK|Cancel", TRUE)
 +
        if (p == "Cancel") {
 +
            quit
 +
        }
 +
     }
 +
 
 
     # Push selected
 
     # Push selected
 
     gSelSaves = {selected}
 
     gSelSaves = {selected}
Line 1,901: Line 2,015:
 
     write tugsave.pdb
 
     write tugsave.pdb
 
     select none
 
     select none
   
+
 
 
     gScheme = defaultColorScheme
 
     gScheme = defaultColorScheme
 
     gAltScheme = ((gScheme == "Jmol") ? "Rasmol" : "Jmol")
 
     gAltScheme = ((gScheme == "Jmol") ? "Rasmol" : "Jmol")
 
     set echo TOP LEFT
 
     set echo TOP LEFT
 
     background ECHO yellow
 
     background ECHO yellow
     gEcho = "ALT-CLICK=set cargo range"
+
     gEcho = ("_________TUG_________|ALT-CLICK=mark block|SHIFT-CLICK=mark chain" +
 +
        "|DOUBLE-CLICK=exit")
 
     echo @gEcho
 
     echo @gEcho
 
     gCrotors = array()
 
     gCrotors = array()
Line 1,914: Line 2,029:
 
     unbind
 
     unbind
  
    set picking ON
 
 
     bind "ALT-LEFT-CLICK" "_pickAtom";
 
     bind "ALT-LEFT-CLICK" "_pickAtom";
 
     bind "ALT-LEFT-CLICK" "+:tugCargoMB";
 
     bind "ALT-LEFT-CLICK" "+:tugCargoMB";
 +
    bind "SHIFT-LEFT-CLICK" "_pickAtom";
 +
    bind "SHIFT-LEFT-CLICK" "+:towCargoMB";
 
     bind "DOUBLE" "tugExit";
 
     bind "DOUBLE" "tugExit";
 
}
 
}
Line 1,934: Line 2,050:
 
     if (p != "No") {
 
     if (p != "No") {
 
         unbind
 
         unbind
         halo off  
+
         halo off
 
         set allowMoveAtoms FALSE
 
         set allowMoveAtoms FALSE
 
         echo
 
         echo
Line 1,944: Line 2,060:
 
         gBusy = FALSE
 
         gBusy = FALSE
 
         background ECHO yellow
 
         background ECHO yellow
       
+
 
 
         # Pop selected
 
         # Pop selected
 
         select gSelSaves
 
         select gSelSaves

Revision as of 20:49, 12 February 2014

Tug allows the user to pull or push by mouse actions to move or rotate one part of a polypeptide against the rest by rotation on its psi and phi bonds with collision detection and restriction. It also allows the user to move an entire chain to nest against another chain.

Tug is a member of the Plico suite of protein folding tools described in User:Remig/plico . It may be installed and accessed as a macro with the file:

Title=PLICO Tug
Script=script <path to your scripts folder>/tug.spt;plicotug

saved as plicotug.macro in your .jmol/macros folder as described in Macro.

Copy and paste the following into a text editor and save in your scripts folder as tug.spt.

#   tug - Jmol script by Ron Mignery
#   v1.1 beta    2/12/2014 for Jmol 14
#
#   Translate or rotate a stretch of a polypeptide against itself by mouse actions
#
var kTug = 2
var kDtolerance = 0.2
var kAtolerance = 5.0
var kCtolerance = 1.85
var kMtolerance = 0.8
var gCanchorIdx = -1
var gCanchorNo = -1
var gNanchorIdx = -1
var gNanchorNo = -1
var gCcargoIdx = -1
var gNcargoIdx = -1
var gCcargoNo = -1
var gNcargoNo = -1
var gDestAtomIdx = -1
var g1pivotIdx = -1
var g2pivotIdx = -1
var gSelSaves = ({})
var gCrotors = array()
var gNrotors = array()
var gMouseX = 0
var gMouseY = 0
var gOkCollide = ({})
var gChain = ""
var gMinNo = 1
var gMaxNo = 9999
var gScheme = "Jmol"
var gAltScheme = "Rasmol"
var gCargoSet = ({})
var gMovingSet = ({})
var gBusy = FALSE
var gSCidx = -1
var gSCcircle = -1
var gSCpt = {0 0 0}
var gOK = TRUE # global return value to work around jmol *feature*
var gOk2 = TRUE # "    "
var gTargetPt = {0 0 0}
var gNewDrag = FALSE
var gEcho = ""
var gZoom = ""
var gRotate = ""
var gTow = FALSE
var g1dynamicIdx = -1
var g2dynamicIdx = -1

# Return L tetrahedron point if i1<i2<i3, else R point
function getTet(i1, i2, i3, dist) {
    var v1 = {atomIndex=i3}.xyz - {atomIndex=i2}.xyz
    var v2 = {atomIndex=i1}.xyz - {atomIndex=i2}.xyz
    var axis = cross(v1, v2)
    var pma = ({atomIndex=i1}.xyz + {atomIndex=i3}.xyz)/2
    var pmo = {atomIndex=i2}.xyz + {atomIndex=i2}.xyz - pma
    var pt = pmo + (axis/axis)

    var v = pt - {atomIndex=i2}.xyz
    var cdist = distance(pt, {atomIndex=i2})
    var factor = (dist/cdist)
    var lpt = v * factor

    return lpt + {atomIndex=i2}.xyz
}

function getTrigonal(i1, i2, i3, dist) {
    var v1 = {atomIndex=i1}.xyz - {atomIndex=i2}.xyz
    var v2 = {atomIndex=i3}.xyz - {atomIndex=i2}.xyz
    var pt = {atomIndex=i2}.xyz - (v1 + v2)

    var v = pt - {atomIndex=i2}.xyz
    var cdist = distance(pt, {atomIndex=i2})
    var factor = (dist/cdist)
    var lpt = (v * factor)

    return lpt + {atomIndex=i2}.xyz
}

function abs( x) {
    if (x < 0) {
        x = -x
    }
    return x
}

function getCAmNo (iNo) {
    while ((iNo > 0) and ({(atomno=iNo) and (chain=gChain)}.atomName != "CA")) {
        iNo--
    }
    return iNo
}

function getCAmIdx (idx) {
    var no = {atomIndex=idx and (chain=gChain)}.atomno
    no = getCAmNo( no)
    return ({(atomno=no) and (chain=gChain)}.atomIndex)
}

function getCApNo (iNo) {
    while ((iNo < gMaxNo) and ({(atomno=iNo) and (chain=gChain)}.atomName != "CA")) {
        iNo++
    }
    return iNo
}

function getCpIdx (idx) {
    var no = {atomIndex=idx and (chain=gChain)}.atomno
    no = getCpNo( no)
    return ({(atomno=no) and (chain=gChain)}.atomIndex)
}

function getCpNo (iNo) {
    while ((iNo < gMaxNo) and ({(atomno=iNo) and (chain=gChain)}.atomName != "C")) {
        iNo++
    }
    return iNo
}

function getCApIdx (idx) {
    var no = {atomIndex=idx and (chain=gChain)}.atomno
    no = getCApNo( no)
    return ({(atomno=no) and (chain=gChain)}.atomIndex)
}

function getCmNo (iNo) {
    while ((iNo > 0) and ({(atomno=iNo) and (chain=gChain)}.atomName != "C")) {
        iNo--
    }
    return iNo
}

function getCmIdx (idx) {
    var no = {atomIndex=idx and (chain=gChain)}.atomno
    no = getCmNo( no)
    return ({(atomno=no) and (chain=gChain)}.atomIndex)
}

function getNmNo (iNo) {
    while ((iNo > 0) and ({(atomno=iNo) and (chain=gChain)}.atomName != "N")) {
        iNo--
    }
    return iNo
}

function getNmIdx (idx) {
    var no = {atomIndex=idx and (chain=gChain)}.atomno
    no = getNmNo( no)
    return ({(atomno=no) and (chain=gChain)}.atomIndex)
}

function getNpNo (iNo) {
    while ((iNo < gMaxNo) and ({(atomno=iNo) and (chain=gChain)}.atomName != "N")) {
        iNo++
    }
    return iNo
}

function getNpIdx (idx) {
    var no = {atomIndex=idx}.atomno
    no = getNpNo( no)
    return ({(atomno=no) and (chain=gChain)}.atomIndex)
}

function getCBidx (BBidx) {
    var no = {atomIndex=BBidx}.atomno
    var i = 1
    for (; i < 5; i++) {
        if ({(atomno=@{no+i}) and (chain=gChain)}.atomName == "CB") {
            break
        }
    }
    return {(atomno=@{no+i}) and (chain=gChain)}.atomIndex
}

function getOidx (BBidx) {
    var no = {atomIndex=BBidx}.atomno
    var i = 1
    for (; i < 4; i++) {
        if ({(atomno=@{no+i}) and (chain=gChain)}.atomName == "O") {
            break
        }
    }
    return {(atomno=@{no+i}) and (chain=gChain)}.atomIndex
}

function getNwardBBno (iNo) {
    while ((iNo >= 0) and (
        ({(atomno=iNo) and (chain=gChain)}.atomName != "N")
        and ({(atomno=iNo) and (chain=gChain)}.atomName != "C")
        and ({(atomno=iNo) and (chain=gChain)}.atomName != "CA"))) {
        iNo--
    }
    return iNo
}

function getNwardBBidx (idx) {
    var no = {atomIndex=idx}.atomno - 1
    no = getNwardBBno( no)
    return ((no >= 0) ? ({(atomno=no) and (chain=gChain)}.atomIndex) : -1)
}

function getCwardBBno (iNo) {
    while ((iNo < gMaxNo) and (
        ({(atomno=iNo) and (chain=gChain)}.atomName != "N")
        and ({(atomno=iNo) and (chain=gChain)}.atomName != "C")
        and ({(atomno=iNo) and (chain=gChain)}.atomName != "CA"))) {
        iNo++
    }
    return iNo
}

function getCwardBBidx (idx) {
    var no = {atomIndex=idx}.atomno + 1
    no = getCwardBBno( no)
    return ((no >= 0) ? ({(atomno=no) and (chain=gChain)}.atomIndex) : -1)
}

function getScBBidx (idx) {
    var no = {atomIndex=idx}.atomno
    for (; no > 0; no--) {
        if ({(atomno=no) and (chain=gChain)}.atomName == "CA") {
            break
        }
        else if ({(atomno=no) and (chain=gChain)}.atomName == "C") {
            break
        }
        else if ({(atomno=no) and (chain=gChain)}.atomName == "N") {
            break
        }
        else if ({(atomno=no) and (chain=gChain)}.atomName == "CB") {
            no -= 3
            break
        }
    }
    return {(atomno=no) and (chain=gChain)}.atomIndex
}

function isBBidx(aIdx) {
    var ret = FALSE
    switch({atomIndex=aIdx}.atomName) {
    case "N":
    case "CA":
    case "C":
        ret = TRUE
        break
    }
    return ret
}

function isSCidx(aIdx) {

    var ret = FALSE
    if (isBBidx(aIDx) == FALSE) {

        ret = TRUE
        switch({atomIndex=aIdx}.atomName) {
        case "O":
        case "CB":
            ret = FALSE
            break
        }
    }
    return ret
}

function addSideChainToSelection(CAno, isAdd, addOXT) {
    var iNo = CAno+3
    while ({(atomno=iNo) and (chain=gChain)}.resno == {(atomno=CAno) and (chain=gChain)}.resno) {
        {(atomno=iNo) and (chain=gChain)}.selected = isAdd
        if ({(atomno=iNo) and (chain=gChain)}.atomName == "OXT") {
            {(atomno=iNo) and (chain=gChain)}.selected = addOXT
        }
        iNo++
    }
}

function selectAddSideChain(fromIdx) {
    var iNo = {atomIndex=fromIdx}.atomno
    var iChain = {atomIndex=fromIdx}.chain
    select none
    while ({(atomno=iNo) and (chain=iChain)}.atomName != "N") {
        {(atomno=iNo) and (chain=iChain)}.selected = TRUE
        iNo++
        if (iNo > {chain=iChain}.atomno.max) {
            break
        }
    }
}

# First and last are BB atoms
# Any side atoms in the range are also selected
function selectNwardIdx (firstIdx, lastIdx) {
    var firstno = ((firstIdx < 0) ? {atomIndex=lastIdx}.atomno : {atomIndex=firstIdx}.atomno)
    var lastno = ((lastIdx < 0) ? firstno : {atomIndex=lastIdx}.atomno)
    var iChain = ((firstIdx < 0)
        ? {atomIndex=lastIdx}.chain : {atomIndex=firstIdx}.chain)

    select (atomno <= firstno) and (atomno >= lastno) and (chain = iChain)

    if ({(atomno=firstno) and (chain=gChain)}.atomName == "C") { # if psi
        addSideChainToSelection(firstno-1, TRUE, TRUE)
        {(atomno=@{firstno+1}) and (chain=iChain)}.selected = TRUE # add O
    }
    if ({(atomno=firstno) and (chain=iChain)}.atomName == "CA") {
        addSideChainToSelection(firstno, TRUE, FALSE)
    }
    if ({(atomno=lastno) and (chain=iChain)}.atomName == "C") { # if psi
        addSideChainToSelection(lastno-1, FALSE, FALSE)
    }
}

# First and last are BB atoms
# Any side atoms in the range are also selected
function selectCwardIdx (firstIdx, lastIdx) {
    var firstno = ((firstIdx < 0) ? gMaxNo : {atomIndex=firstIdx}.atomno)
    var lastno = ((lastIdx < 0) ? 1 : {atomIndex=lastIdx}.atomno)
    var iChain = ((firstIdx < 0)
        ? {atomIndex=lastIdx}.chain : {atomIndex=firstIdx}.chain)

    # If nWard anchor in range, begin selection with it
    if ((gNanchorIdx >= 0) and ({atomIndex=gNanchorIdx}.chain == iChain))  {
        var aNo = {atomIndex=gNanchorIdx}.atomno
        if (aNo > firstNo) {
            firstno = aNo
        }
    }

    # If cWard anchor in range, end selection with it
    if ((gCanchorIdx >= 0) and ({atomIndex=gCanchorIdx}.chain == iChain))  {
        var aNo = {atomIndex=gCanchorIdx}.atomno
        if (aNo < lastNo) {
            lastno = aNo
        }
    }

    select (atomno >= firstno) and (atomno <= lastno) and (chain = iChain)

    if ({(atomno=firstno) and (chain=iChain)}.atomName == "C") { # if psi
        addSideChainToSelection(firstno-1, FALSE, FALSE)
    }
    if ({(atomno=lastno) and (chain=iChain)}.atomName == "CA") {
        addSideChainToSelection(lastno, TRUE, FALSE)
    }
    if ({(atomno=lastno) and (chain=iChain)}.atomName == "C") { # if psi
        addSideChainToSelection(lastno-1, TRUE, TRUE)
        {(atomno=@{lastno+1}) and (chain=iChain)}.selected = TRUE # add O
    }
}

# Selected must include second parameter but not the first
function setDistanceIdx (staticIdx, mobileIdx, desired) {
    try {
        var v = {atomIndex=mobileIdx}.xyz - {atomIndex=staticIdx}.xyz
        var dist = distance({atomIndex=staticIdx}, {atomIndex=mobileIdx})
        translateSelected @{((v * (desired/dist)) - v)}
    }
    catch {
    }
}


# Selected must include third parameter but not the first
function setAngleIdx (statorIdx, pivotIdx, rotorIdx, toangle) {
    try {
        var v1={atomIndex=statorIdx}.xyz - {atomIndex=pivotIdx}.xyz
        var v2={atomIndex=rotorIdx}.xyz - {atomIndex=pivotIdx}.xyz
        var axis = cross(v1, v2) + {atomIndex=pivotIdx}.xyz
        var curangle =  angle({atomIndex=statorIdx},
            {atomIndex=pivotIdx}, {atomIndex=rotorIdx})
        rotateselected @axis {atomIndex = pivotIdx} @{curangle-toangle}
    }
    catch {
    }
}

# Selected must include fourth parameter but not the first
function setDihedralIdx (stator1idx, stator2idx, rotor1idx, rotor2idx, toangle) {
    try {
        var a1={atomIndex = stator1idx}.xyz
        var a2={atomIndex = stator2idx}.xyz
        var a3={atomIndex = rotor1idx}.xyz
        var a4={atomIndex = rotor2idx}.xyz
        var curangle =  angle(a1, a2, a3, a4)
        rotateselected {a3} {a2} @{curangle-toangle}
    }
    catch {
    }
}

function countCollisions(rc) {
    var cAtoms = ({})
    for (var idx = {*}.min.atomIndex; idx <= {*}.max.atomIndex; idx++) {
        if ({atomIndex=idx}.size > 0) {
            var lcAtoms = (within(kCtolerance, FALSE, {atomIndex=idx})
                and {atomIndex > idx}
                and not {rc}
                and not connected({atomIndex=idx}))
            if (lcAtoms.size > 0) {
                cAtoms = cAtom or lcAtoms
            }
        }
    }
    return cAtoms.size
}

# Resolve collisions
function handleCollisions2( targetIdx) {

    # For all selected atoms
    for (var iNo = {selected}.min.atomno; iNo <= {selected}.max.atomno; iNo++) {
        var idx = {(atomno=iNo) and (chain=gchain)}.atomIndex
        if ({atomindex=idx}.selected) {

            # Collect local colliders
            var lcAtoms = (within(kCtolerance, FALSE, {atomIndex=idx})
                and not {atomIndex=idx}
                and not {gOkCollide}
                and not connected({atomIndex=idx}))
            if (lcAtoms.size > 0) {

                # Ignore kinked BB
                if (isBBidx(idx) and angle({atomIndex=@{getCwardBBidx(idx)}},
                    {atomIndex=idx} , {atomIndex=@{getNwardBBidx(idx)}}) < 100) {
                    continue
                }

                # For all local colliders
                for (var c = 1; c <= lcAtoms.size; c++ ) {
                    var cidx = lcAtoms[c].atomIndex

                    # If it is with water, delete it
                    if (lcAtoms[c].group = "HOH") {
                        delete {atomIndex=cidx}
                    }

                    # else if it is with side chain not proline, fix it
                    else if (isSCidx(cidx) and ({atomIndex=cidx}.group != "PRO")) {
                        fixSCcollision2(cidx)
                        recollect = TRUE

                        # If not fixed, exit fail
                        if (gOk2 == FALSE) {
                            return # early exit (break n jmol bug)
                        }
                    }

                    # else if it is itself a side chain not proline, fix it
                    else if (isSCidx(idx) and ({atomIndex=idx}.group != "PRO")) {
                        fixSCcollision2(idx)
                        recollect = TRUE

                        # If not fixed, exit fail
                        if (gOk2 == FALSE) {
                            return # early exit (break n jmol bug)
                        }
                    }

                    # Else if it is with O, counter-rotate
                    else if (lcAtoms[c].atomName = "O") {
                        counterRotate2(lcAtoms[c].atomIndex,
                            {atomIndex=idx}.xyz, targetIdx, FALSE)

                        # If not fixed, exit fail
                        if (gOk2 == FALSE) {
                            return # early exit (break n jmol bug)
                        }
                    }

                    # Else if it is itself O, counter-rotate
                    else if ({atomIndex=idx}.atomName = "O") {
                        counterRotate2(idx, lcAtoms[c].xyz, targetIdx, FALSE)

                        # If not fixed, exit fail
                        if (gOk2 == FALSE) {
                            return # early exit (break n jmol bug)
                        }
                    }

                    else {    # Else not fixed, exit fail
                        gOk2 = FALSE
                        return # early exit (break n jmol bug)
                    }
                } # endfor
            }
        }
    } # endfor iNo
}

# Rotate rotor set to move target atom to its proper place
function tugTrackIdx(targetIdx, targetPt, nWard, cDetect) {
    gOK = FALSE
    var pt = targetPt
    var dist = distance(pt, {atomIndex=targetIdx}.xyz)

    var rotors = (nWard ? gNrotors : gCrotors)

    # For a number of passes
    for (var pass1 = 0; pass1 < 20; pass1++) {
        var blocked = ({})
        for (var pass2 = 0; pass2 < (rotors.size/4); pass2++) {

            var v1 = {atomIndex=targetIdx}.xyz - pt

            # Find the most orthgonal unused rotor
            var imax = 0
            var smax = 0.5
            for (var i = 1; i < rotors.size; i += 4) {
                var i2 = rotors[i+1]
                var i3 = rotors[i+2]
                var i4 = rotors[i+3]
                if ((i2 != targetIdx) and (i3 != targetIdx) and (i4 != targetIdx)) {
                    if ({blocked and {atomIndex=i2}}.count == 0) {
                        var v2 = {atomIndex=i3}.xyz - {atomIndex=i2}.xyz

                        var s = sin(abs(angle(v1, {0 0 0}, v2)))
                        if (s > smax) {
                            smax = s
                            imax = i
                        }
                    }
                }
            }

            # If no more rotors, break to next full try
            if (imax == 0) {
               break
            }
            var i1 = rotors[imax+0]
            var i2 = rotors[imax+1]
            var i3 = rotors[imax+2]
            var i4 = rotors[imax+3]

            # Get dihedral of rotor with target point
            var dt = angle({atomIndex=targetIdx}, {atomIndex=i2}, {atomIndex=i3}, pt)
            var dh = angle({atomIndex=i1}, {atomIndex=i2}, {atomIndex=i3}, {atomIndex=i4})
            if (dh == "NaN") {
                dh = -50
            }
            var psi = dh + dt
            var phi = dh + dt

            # Compute resultant psi and phi
            #  and select from target atom to first half of rotor
            var movePt = FALSE
            if (nWard) {
                if ({atomIndex=i2}.atomName="CA") {
                    psi = angle({atomIndex=@{getCwardBBidx(i1)}}, {atomIndex=i1},
                        {atomIndex=i2}, {atomIndex=i3}) + dt
                }
                else {
                    phi = angle({atomIndex=i1}, {atomIndex=i2},
                        {atomIndex=i3}, {atomIndex=@{getNwardBBidx(i3)}}) + dt
                }

                if ({atomIndex=i2}.atomno > {atomIndex=targetIdx}.atomno) {
                    movePt = TRUE
                    selectNwardIdx(i3, getCwardBBidx(targetIdx))
                    {atomIndex=targetIdx}.selected = TRUE
                }
                else {
                    selectCwardIdx(i2, targetIdx)
                }
            }
            else {
                if (({atomIndex=i2}.atomName="CA")) {
                    phi = angle({atomIndex=@{getNwardBBidx(i1)}}, {atomIndex=i1},
                        {atomIndex=i2}, {atomIndex=i3}) + dt
                }
                else {
                    psi = angle({atomIndex=i2}, {atomIndex=i3},
                        {atomIndex=i4}, {atomIndex=@{getCwardBBidx(i4)}}) + dt
                }

                if ({atomIndex=i2}.atomno < {atomIndex=targetIdx}.atomno) {
                    movePt = TRUE
                    selectCwardIdx(i3, getNwardBBidx(targetIdx))
                    {atomIndex=targetIdx}.selected = TRUE
                }
                else {
                    selectNwardIdx(i2, targetIdx)
                }
            }

            # Relax rules if desperate
            if (pass1 > 10) {
                phi = -50
            }

            # If rotation within ramachandran limits
            if ((abs(dt) >= 0.1) and
                (({atomIndex=i2}.group=="GLY") or (phi < 0))) {

                # If moving target point, put the target atom there
                var cp = {atomIndex=targetIdx}.xyz
                if (movePt) {
                    dt = -dt
                    {atomIndex=targetIdx}.xyz = pt
                }

                # Rotate to minimize vector ====================
                rotateSelected {atomIndex=i2} {atomIndex=i3} @dt

                # If collision checking
                if (cDetect) {

                    # If collision, back off by eighths
                    var wasCollision = FALSE
                    for (var ci = 0; ci < 4; ci++) {
                        if (ci < 3) {
                            dt /= 2
                        }
                        handleCollisions2( nWard, targetIdx)
                        if (gOk2==FALSE) {
                            wasCollision = TRUE
                            rotateSelected {atomIndex=i2} {atomIndex=i3} @{-dt}
                        }
                        else if (wasCollision) {
                            if (ci <3) {
                                rotateSelected {atomIndex=i2} {atomIndex=i3} @{dt}
                            }
                        }
                        else {
                            break
                        }

                        if (dt < 0.01) {
                            break
                        }
                    } # endfor
                }

                # If moving target point, put the target atom back
                if (movePt) {
                    pt = {atomIndex=targetIdx}.xyz
                    {atomIndex=targetIdx}.xyz = cp
                }

            }

            # If close enough, stop
            if (distance(pt, {atomIndex=targetIdx}) < kDtolerance) {
                gOK = TRUE
                gTargetPt = pt
                break
            }

            # Block rotor
            blocked |= {atomIndex=i2}

        }   # endfor num rotors passes

        if (gOK) {
            break
        }
    }   # endfor 10 passes
}

# Counter rotate bonds on either side of a BB O
function docounterRotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, nWard) {

    # Rotate psi
    {atomIndex=nIdx}.selected = nWard
    {atomIndex=cIdx}.selected = nWard
    {atomIndex=oIdx}.selected = nward
    rotateSelected {atomIndex=caPsiIdx} {atomIndex=cIdx} @{dir}

    # Counter-rotate phi
    {atomIndex=nIdx}.selected = not nWard
    {atomIndex=cIdx}.selected = not nWard
    {atomIndex=oIdx}.selected = not nward
    rotateSelected {atomIndex=nIdx} {atomIndex=caPhiIdx} @{-dir}
}

function counterRotate(oIdx, dir, nWard) {

    var selsave = {selected}
    var iChain = {atomIndex=oIdx}.chain
    var cIdx = getScBBidx(oIdx)
    var nIdx = getCwardBBidx(cIdx)
    var caPhiIdx = getCwardBBidx(nIdx)
    var caPsiIdx = getNwardBBidx(cIdx)

    if (nWard) {
        nNo = {chain=iChain}.atomno.min
        selectNwardIdx(caPsiIdx, {(atomno=nNo) and (chain=iChain)}.atomIndex)
    }
    else {
        cNo = {chain=iChain}.atomno.max
        selectCwardIdx(caPhiIdx, {(atomno=cNo) and (chain=iChain)}.atomIndex)
    }

    # Counter-rotate
    docounterRotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, not nWard)
    select selsave
}

function counterRotate2(oIdx, toPt, terminalIdx, oDrag) {

    var selsave = {selected}
    var gOk2 = TRUE
    var cIdx = getScBBidx(oIdx)
    var nIdx = getCwardBBidx(cIdx)
    var caPhiIdx = getCwardBBidx(nIdx)
    var caPsiIdx = getNwardBBidx(cIdx)

    var nTward = ({atomIndex=oIdx}.atomno < {atomIndex=terminalIdx}.atomno)
    if (nTward) {
        selectCwardIdx(cIdx, terminalIdx)
    }
    else {
        selectNwardIdx(nIdx, terminalIdx)
    }

    # Until all collisions cancelled
    var dir = 5
    var ang = angle(toPt, {atomIndex=oIdx}, {atomIndex=cIdx})
    var tcount = 0
    while (oDrag or (within(kCtolerance, FALSE, {atomIndex=oIdx})
            and not {atomIndex=oIdx} and not connected({atomIndex=oIdx})
            and not {gOkCollide} > 0)) {

        # Counter-rotate
        docounterRotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, nTward)
        var newang = angle(toPt, {atomIndex=oIdx}, {atomIndex=cIdx})

        # If wrong direction once, undo and reverse
        if (newang > ang) {
            docounterRotate(caPhiIDx, nIdx, cIdx, oIdx, caPsiIdx, -dir, nTward)

            # If first time, continue in opposite direction
            dir *= -1
            if (dir < 0) {
                continue
            }
        }

        if (oDrag) {
            break
        }

        # If no go, undo and exit
        tcount++
        if (tcount > (360/abs(dir))) {
            gOk2 = FALSE
            break
        }

    } # endwhile
    select selsave
}

# Repair proline
function repairProline(BBidx) {
    var cbidx = getCBidx(BBidx)
    var cbno = {atomIndex=cbidx}.atomno
    var cgidx = {(atomno=@{cbno+1}) and (chain=gChain)}.atomIndex
    var cdidx = {(atomno=@{cbno+2}) and (chain=gChain)}.atomIndex
    var caidx = {(atomno=@{cbno-3}) and (chain=gChain)}.atomIndex
    var nidx = {(atomno=@{cbno-4}) and (chain=gChain)}.atomIndex

    select {atomIndex=cbidx}
    setAngleIdx(nidx, caidx, cbidx, 109.5)

    select {atomIndex=cdidx}
    setDistanceIdx(nidx, cdidx, 1.47)
    setAngleIdx(caidx, nidx, cdidx, 102.7)
    setDihedralIdx(cbidx, caidx, nidx, cdidx, 16.2)

    select {atomIndex=cgidx}
    setDistanceIdx(cdidx, cgidx, 1.51)
    setAngleIdx(nidx, cdidx, cgidx, 106.4)
    setDihedralIdx(caidx, nidx, cdidx, cgidx, 16.2)
}

# Repair side chain
function repairSideChain(targetIdx, nWard) {

    var idx = (nWard ? getCwardBBidx(targetIdx) : getNwardBBidx(targetIdx))

    if (({atomIndex=targetIdx}.atomName == "CA")
        and ({atomIndex=targetIdx}.group != "GLY")) {
        var cbidx = getCBidx(targetIdx)
        select none
        selectAddSideChain(cbidx)
        setAngleIdx(idx, targetIdx, cbidx, 110.0)
        setDistanceIdx(targetIdx, cbidx, 1.5)
        if ({atomIndex=targetIdx}.group != "PRO") {
            var colliders = (within(kCtolerance, FALSE, {selected})
                and not {atomIndex=targetIdx} and not {selected})
            if (colliders.size > 0) {
                if ({atomIndex=targetIdx}.group != "ALA") {
                    fixSCcollision2(cbidx)
                }
            }
        }
        else {
            if (nWard) {
            }
            else {
                setDihedralIdx(getNwardBBidx(idx), idx, targetIdx, cbidx, 174.2)
            }
        }
    }

    else if ({atomIndex=targetIdx}.atomName == "C") {
        var oidx = getOidx(targetIdx)
        select {atomIndex=oidx}
        setAngleIdx(idx, targetIdx, oidx, 120.0)
        setDistanceIdx(targetIdx, oidx, 1.21)
        if (nWard) {
            setDihedralIdx(getCwardBBidx(idx), idx, targetIdx, oidx, 0.0)
        }
        if ({atomIndex=idx}.group == "PRO") {
            repairProline(idx)
            var dNo = {atomIndex=targetIdx}.atomno + 4
            var dIdx = {(atomno=dNO) and (chain=gChain)}.atomIndex
            var colliders = (within(kCtolerance, FALSE, {atomIndex=dIdx})
                and not connected({atomIndex=dIdx})
                and not {atomIndex=dIdx})
            for (var i = 1; i <= colliders.size; i++) {
                if (colliders[i].atomName == "O") {
                    counterRotate2(colliders[i].atomIndex,
                        {atomIndex=dIdx}.xyz, targetIdx, FALSE)
                }
            }
        }
    }
}

# Rebuild Cward rotors set
function tugTrackC() {

    # For all bb atoms cWard of cargo
    var targetIdx = gCcargoIdx
    var okCount = 0

    # Allow collisions with cargo
    gOkCollide = gCargoSet
    var tcount = 0
    while (targetIdx != gCanchorIdx) {

        # Step to next atom
        targetIdx = getCwardBBidx(targetIdx)

        # No collision with cargo allowed after two atoms placed
        if (tcount == 2) {
           gOkCollide = ({})
        }
        tcount++

        # Compute targets desired coords
        var c1idx = getCwardBBidx(targetIdx)
        var n1idx = getNwardBBidx(targetIdx)
        var n2idx = getNwardBBidx(n1Idx)
        var n3idx = getNwardBBidx(n2Idx)
        var pt = {0 0 0}
        if ({atomIndex=targetIdx}.atomName == "N") {
            var oidx = getOidx(n1idx)
            select {atomIndex=oidx}

            # Desired target location is trigonal O
            setDistanceIdx(n1idx, oidx, 1.5)
            pt = getTrigonal(n2idx, n1idx, oidx, 1.37)
            setDistanceIdx(n1idx, oidx, 1.21)
        }
        else if (({atomIndex=targetIdx}.atomName == "C")
            and ({atomIndex=targetIdx}.group != "GLY")) {

            # Desired target location is tetragonal CB
            var cbidx = getCBidx(n1idx)
            pt = getTet(n2idx, n1idx, cbidx, 1.5)
        }
        else { # CA (or GLY C)

            # Save current target coords
            var cp = {atomIndex=targetIdx}.xyz

            # Set target atom at desired distance and angle
            select {atomIndex=targetIdx}
            setDistanceIdx(n1idx, targetIdx, 1.5)
            setAngleIdx(n2idx, n1idx, targetIdx, 120.0)
            if ({atomIndex=targetIdx}.atomName == "CA") {
                setDihedralIdx(n3idx, n2idx, n1idx, targetIdx, 180)
            }

            # Record and restore target
            pt = {atomIndex=targetIdx}.xyz
            {atomIndex=targetIdx}.xyz = cp
        }

        # If target not at desired location
        if (distance(pt, {atomIndex=targetIdx}) > kDtolerance) {
            okCount = 0
            gTargetPt = pt
            var xcount = 0
            gOK = FALSE
            while ((xcount < 20) and (gOK == FALSE)) {

                # Rotate on cWard rotor set to move it there
                tugTrackIdx(targetIdx, pt, FALSE, FALSE)
                    #(distance(pt, {atomIndex=targetIdx}) > kMtolerance))
                xcount++
            }
        }
        else {
            gOK = TRUE
            okCount++
        }

        # If successful
        if (gOK == TRUE) {

            # Adust any side atoms
            repairSideChain(targetIdx, FALSE)
        }

        # Else fail
        else {
            break
        }

        # If no movement in 4 tries, we are done
        if (okCount > 3) {
            break
        }
    } # endwhile (targetIdx != gCanchorIdx) {
}

# Rebuild Nward rotors set
function tugTrackN() {

    gOK = TRUE

    # For all bb atoms nWard of cargo
    var targetIdx = gNcargoIdx
    var okCount = 0

    # Allow collisions with cargo
    gOkCollide = gCargoSet
    var tcount = 0
    while (targetIdx != gNanchorIdx) {

        # Step to next atom
        targetIdx = getNwardBBidx(targetIdx)

        # No collision with cargo allowed after two atoms placed
        if (tcount == 2) {
           gOkCollide = ({})
        }
        tcount++

        # Compute targets desired coords
        var n1idx = getNwardBBidx(targetIdx)
        var c1idx = getCwardBBidx(targetIdx)
        var c2idx = getCwardBBidx(c1idx)
        var c3idx = getCwardBBidx(c2idx)
        var pt = {0 0 0}
        if ({atomIndex=targetIdx}.atomName == "CA") {

            # Desired target location is trigonal O
            var oidx = getOidx(c1idx)
            select {atomIndex=oidx}
            setDistanceIdx(c1idx, oidx, 1.39)
            pt = getTrigonal(c2idx, c1idx, oidx, 1.41)
            setDistanceIdx(c1idx, oidx, 1.21)
        }
        else if (({atomIndex=targetIdx}.atomName == "N")
            and ({atomIndex=targetIdx}.group != "GLY")) {

            # Desired target location is r-tetragonal CB
            var cbidx = getCBidx(c1idx)
            pt = getTet(cbidx, c1idx, c2idx, 1.5)
        }
        else { # C

            # Save current target coords
            var cp = {atomIndex=targetIdx}.xyz

            # Set target atom at desired distance and angle
            select {atomIndex=targetIdx}
            setDistanceIdx(c1idx, targetIdx, 1.37)
            setAngleIdx(c2idx, c1idx, targetIdx, 110.0)

            if ({atomIndex=targetIdx}.group == "PRO") {
                setDihedralIdx(c3idx, c2idx, c1idx, targetIdx, -57.0)
            }

            # Record and restore target
            pt = {atomIndex=targetIdx}.xyz
            {atomIndex=targetIdx}.xyz = cp
        }

        # If target not at desired location
        if (distance(pt, {atomIndex=targetIdx}) > kDtolerance) {
            var okCount = 0
            gTargetPt = pt
            var xcount = 0
            gOK = FALSE
            while ((xcount < 20) and (gOK == FALSE)) {

                # Rotate on cWard rotor set to move it there
                tugTrackIdx(targetIdx, pt, TRUE, FALSE)
                    #(distance(pt, {atomIndex=targetIdx}) > kMtolerance))
                xcount++
            }
        }
        else {
            gOK = TRUE
            okCount++
        }

        # If sucessful
        if (gOK == TRUE) {

            # Adust any side atoms
            repairSideChain(targetIdx, TRUE)
        }

        # Else fail
        else {
            break
        }

        # If no movement in 4 tries, we are done
        if (okCount > 3) {
            break
        }

    }   # endwhile (targetIdx != gNanchorIdx) {

}

# gPlicoRecord is maintained by the macro pilcoRecord
function plicoRecord(s) {
    var g = format("show file \"%s\"", gPlicoRecord)
    var ls = script(g)
    if (ls.find("FileNotFoundException")) {
        ls = ""
    }
    ls += s
    write var ls @gPlicoRecord
}

# gPlicoRecord is maintained by the macro pilcoRecord
function translateSelectedRecord(pt) {
    if (gPlicoRecord != "") {
        plicoRecord(format("select %s;translateSelected %s;", {selected}, pt))
    }
    translateSelected @pt
}

# gPlicoRecord is maintained by the macro pilcoRecord
function rotateSelectedRecord(g1pivotIdx, axis, a) {
    if (gPlicoRecord != "") {
        plicoRecord(format("select %s;", {selected}))
        plicoRecord(format("rotateSelected {atomIndex=%d} @%s @%s;",
            g1pivotIdx, axis, a))
    }
    rotateSelected {atomIndex=g1pivotIdx} @axis @a
}

function collectSCrotors(no, iChain) {
    var scBondIdxs = array()
    for (var iNo = no; iNo >= 0; iNo--) {
        var ile = 0
        switch ({(atomno=iNo) and (chain=iChain)}.atomName) {
        case "CA" :
            return scBondIdxs # Early exit since break 1 appears broken
        case "CZ" :
            if ({(atomno=iNo) and (chain=iChain)}.group == "TYR") {
                break
            }
        case "CE" :
            if ({(atomno=iNo) and (chain=iChain)}.group == "MET") {
                break
            }
        case "CG1" :
            if ({(atomno=iNo) and (chain=iChain)}.group == "VAL") {
                break
            }
            if ({(atomno=iNo) and (chain=iChain)}.group == "ILE") {
                ile = 1
            }
        case "NE" :
        case "CD" :
        case "SD" :
        case "CG" :
        case "CB" :
            scBondIdxs += {(atomno=@{iNo+1+ile}) and (chain=iChain)}.atomIndex
            scBondIdxs += {(atomno=@{iNo+0}) and (chain=iChain)}.atomIndex
            if ({(atomno=iNo) and (chain=iChain)}.atomName%2 == "CG") {
                scBondIdxs += {(atomno=@{iNo-1}) and (chain=iChain)}.atomIndex
                scBondIdxs += {(atomno=@{iNo-4}) and (chain=iChain)}.atomIndex
            }
            else if ({(atomno=iNo) and (chain=iChain)}.atomName == "CB") {
                scBondIdxs += {(atomno=@{iNo-3}) and (chain=iChain)}.atomIndex
                scBondIdxs += {(atomno=@{iNo-4}) and (chain=iChain)}.atomIndex
            }
            else {
                scBondIdxs += {(atomno=@{iNo-1}) and (chain=iChain)}.atomIndex
                scBondIdxs += {(atomno=@{iNo-2}) and (chain=iChain)}.atomIndex
            }
            break
        }

    }

    return scBondIdxs
}

# Drag Side Chain
function dragSC() {

    var iNo = {atomIndex=gSCidx}.atomno
    var iChain  = {atomIndex=gSCidx}.chain

    if ({atomIndex=gSCidx}.group != "PRO") {

        var scBondIdxs = collectSCrotors( iNo, iChain)
        var numChi = scBondIdxs.size / 4
        var dist = distance({atomIndex=gSCidx}.xyz, gSCpt)

        # For all rotor combinations
        var dh = array()
        for (var i = 0; i < numChi; i++) {
            dh += angle({atomIndex=@{scBondIdxs[4+(4*i)]}},
                {atomIndex=@{scBondIdxs[3+(4*i)]}},
                {atomIndex=@{scBondIdxs[2+(4*i)]}},
                {atomIndex=@{scBondIdxs[1+(4*i)]}})
        }
        for (var i = 0; i < numChi; i++) {
            var rot = -120
            for (var j = 0; j < 6; j++) {
                rot += 60*j
                selectAddSideChain(scBondIdxs[1+(4*i)])
                setDihedralIdx(scBondIdxs[4+(4*i)], scBondIdxs[3+(4*i)],
                    scBondIdxs[2+(4*i)], scBondIdxs[1+(4*i)], rot)
                var newDist = distance({atomIndex=gSCidx}.xyz, gSCpt)

                # Find the best
                if (newDist < dist) {
                    dist = newDist
                    for (var k = 0; k < numChi; k++) {
                        dh[k+1] = angle({atomIndex=@{scBondIdxs[4+(4*k)]}},
                        {atomIndex=@{scBondIdxs[3+(4*k)]}},
                        {atomIndex=@{scBondIdxs[2+(4*k)]}},
                        {atomIndex=@{scBondIdxs[1+(4*k)]}})
                    }
                }
            }
        }

        # Now set the best
        for (var i = 0; i < numChi; i++) {
            selectAddSideChain(scBondIdxs[1+(4*i)])
            setDihedralIdx(scBondIdxs[4+(4*i)], scBondIdxs[3+(4*i)],
                scBondIdxs[2+(4*i)], scBondIdxs[1+(4*i)], dh[i+1])
        }
    }
    else { # PRO - toggle between puckers up and down
        var icd = {(atomno=@{iNo+1}) and (chain=iChain)}.atomIndex
        var icb = {(atomno=@{iNo-1}) and (chain=iChain)}.atomIndex
        var ica = {(atomno=@{iNo-4}) and (chain=iChain)}.atomIndex
        var in = {(atomno=@{iNo-5}) and (chain=iChain)}.atomIndex
        select {atomIndex=gSCidx}

        if (angle({atomIndex=ica}, {atomIndex=in},
            {atomIndex=icd}, {atomIndex=gSCidx}) < -10.0) {
            setDihedralIdx(ica, in, icd, gSCidx, 8.7)
            setAngleIdx(in, icd, gSCidx, 110.0)
            setDistanceIdx(icd, gSCidx, 1.5)
        }
        else {
            setDihedralIdx(ica, in, icd, gSCidx, -29.5)
            setAngleIdx(in, icd, gSCidx, 108.8)
            setDistanceIdx(icd, gSCidx, 1.5)
        }
    }

    draw gSCcircle CIRCLE {atomIndex=gSCidx} MESH NOFILL
    gSCpt = {atomIndex=gSCidx}.xyz
}

# Fix side chain collisions
function fixSCcollision2(idx) {
    gOk2 = FALSE
    var iNo = {atomIndex=idx}.atomno
    var iChain = {atomIndex=idx}.chain
    var resno = {(atomno=iNo) and (chain=iChain)}.resno

    # Get SC terminus
    while (resno == {(atomno=iNo) and (chain=iChain)}.resno) {
        iNo++
    }
    iNo--

    var sc = array()
    var iBno = iNo
    while ({(atomno=iBno) and (chain=iChain)}.atomName != "CB") {
        sc += {(atomno=iBno) and (chain=iChain)}
        iBno--
    }
    var cbidx = {(atomno=iBno) and (chain=iChain)}.atomIndex

    var scBondIdxs = collectSCrotors( iNo, iChain)
    var numChi = scBondIdxs.size / 4

    # For all rotor combinations
    for (var i = 0; i < numChi; i++) {
        var rot = -120
        for (var j = 0; j < 6; j++) {
            rot += 60
            selectAddSideChain(scBondIdxs[1+(4*i)])
            setDihedralIdx(scBondIdxs[1+(4*i)], scBondIdxs[2+(4*i)],
                scBondIdxs[3+(4*i)], scBondIdxs[4+(4*i)], rot)
                
            # If no collision, exit
            colliders = (within(kCtolerance, FALSE, {sc})
                and not {atomIndex=cbidx} and not {sc})

            # If it is with water, delete the water
            for (var c = 1; c < colliders.size; c++ ) {
                if (colliders[c].group = "HOH") {
                    delete {atomIndex=@{colliders[c].atomIndex}}
                    colliders = {colliders and not @{colliders[c]}}
                }
            }

            if (colliders.size == 0) {
                gOk2 = TRUE
                return # Early exit since break 1 appears broken
            }

        }
    }
}

function isMovableSideChain(aIdx) {

    var ret = (({atomIndex=aIdx}.group != "PRO")
        or ({atomIndex=aIdx}.atomName == "CG"))
    switch({atomIndex=aIdx}.atomName) {
    case "N":
    case "CA":
    case "C":
    case "CB":
    case "O":
    case "O4\'":
        ret = FALSE
        break
    }
    return ret
}

# gFreeze is maintained by the script plicoFreeze that allows the user
# to inhibit rotation on selected rotors
function isRotorAvailable(idx) {
    return (gFreeze.find(idx) == 0)
}

function collectBBrotors(nWard) {
    var anchorNo = (nWard
        ? ((gNanchorIdx >= 0) ? {atomIndex=gNanchorIdx}.atomno : gMinNo)
        : ((gCanchorIdx >= 0) ? {atomIndex=gCanchorIdx}.atomno : gMaxNo))
    var cargoNo = (nWard
        ? ((gNcargoIdx >= 0) ? {atomIndex=gNcargoIdx}.atomno
        : {atomIndex=gCcargoIdx}.atomno)
        : {atomIndex=gCcargoIdx}.atomno)
    var rotors = array()
    if (cargoNo < anchorNo) {

        for (var iNo = cargoNo; iNo <= anchorNo; iNo++) {
            if ({(atomno=iNo) and (chain=gChain)}.atomName == "CA") {
                if (isRotorAvailable(iNo)) {
                    if (({(atomno=iNo) and (chain=gChain)}.group != "PRO") and (iNo > cargoNo)) { # phi
                        rotors += [{(atomno=@{getCmNo(iNo-1)}) and (chain=gChain)}.atomIndex,
                            {(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex]
                        rotors += [{(atomno=@{iNo}) and (chain=gChain)}.atomIndex,
                            {(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex]
                    }
                    if (iNo != (anchorNo-1)) { # psi
                        rotors += [{(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex,
                            {(atomno=@{iNo}) and (chain=gChain)}.atomIndex]
                        rotors += [{(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex,
                            {(atomno=@{getNpNo(iNo+2)}) and (chain=gChain)}.atomIndex]
                    }
                }
            }
        }
    }
    else {

        for (var iNo = cargoNo; iNo >= anchorNo; iNo--) {
            if ({(atomno=iNo) and (chain=gChain)}.atomName == "CA") {
                if (isRotorAvailable(iNo)) {
                    if ((iNo != (anchorNo-1)) and (iNo < cargoNo)) { # psi
                        rotors += [{(atomno=@{getNpNo(iNo+2)}) and (chain=gChain)}.atomIndex,
                            {(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex]
                        rotors += [{(atomno=@{iNo}) and (chain=gChain)}.atomIndex,
                            {(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex]
                    }
                    if ({(atomno=iNo) and (chain=gChain)}.group != "PRO") { # phi
                        rotors += [{(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex,
                            {(atomno=@{iNo}) and (chain=gChain)}.atomIndex]
                        rotors += [{(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex,
                            {(atomno=@{getCmNo(iNo-1)}) and (chain=gChain)}.atomIndex]
                    }
                }
            }
        }
    }

    if (nWard) {
        gNrotors = rotors
    }
    else {
        gCrotors = rotors
    }
}

function collectRotors() {
    collectBBrotors(FALSE)
    collectBBrotors(TRUE)
}

function tugSideChain(pt) {

    # If destination atom defined
    if (gDestAtomIdx >= 0) {
        var v = {atomIndex=gDestAtomIdx}.xyz - {atomIndex=gSCidx}.xyz
        if (abs(angle({atomIndex=gDestAtomIdx}.xyz, {0 0 0}, pt)) < 90) {
            pt = -v/20.0
        }
        else {
            pt = v/20.0
        }
    }
    gSCpt += pt
    draw arrow {atomIndex=gSCidx} @gSCpt
}

function setColors() {
    select all
    color {selected} @gScheme
    color {atomIndex=g1pivotIdx} green
    color {atomIndex=g2pivotIdx} green
    color @gCargoSet @gAltScheme
    select {(atomIndex=gCcargoIdx) or (atomIndex=gNcargoIdx)
        or (atomIndex=gCanchorIdx) or (atomIndex=gNanchorIdx)}
    halo on
    select {atomIndex=gDestAtomIdx}
    star on
    select none
}

function clearAtomIdxs() {
    gCcargoIdx = -1
    gNcargoIdx = -1
    gCanchorIdx = -1
    gNanchorIdx = -1
    g1pivotIdx = -1
    g2pivotIdx = -1
    gDestAtomIdx = -1
    gSCidx = -1
}

function timedOut (s) {
    timeout ID"tug" OFF
    prompt(s)
    gBusy = FALSE
    background ECHO yellow
    restore state gState
    select gCargoSet
    refresh
    quit
}

function recordDrag() {
    var ls = format("select %s;", {selected})
    ls += format("gCanchorIdx = %d;", gCanchorIdx)
    ls += format("gCanchorNo = %d;", gCanchorNo)
    ls += format("gNanchorIdx = %d;", gNanchorIdx)
    ls += format("gNanchorNo = %d;", gNanchorNo)
    ls += format("gCcargoIdx = %d;", gCcargoIdx)
    ls += format("gNcargoIdx = %d;", gNcargoIdx)
    ls += format("gCcargoNo = %d;", gCcargoNo)
    ls += format("gNcargoNo = %d;", gNcargoNo)
    ls += format("gDestAtomIdx = %d;", gDestAtomIdx)
    ls += format("g1pivotIdx = %d;", g1pivotIdx)
    ls += format("g2pivotIdx = %d;", g2pivotIdx)
    ls += format("gOkCollide = %s;", gOkCollide)
    ls += format("gChain = \"%s\";", gChain)
    ls += format("gMinNo = %d;", gMinNo)
    ls += format("gMaxNo = %d;", gMaxNo)
    ls += format("gCargoSet = %s;", gCargoSet)
    ls += format("gSCidx = %d;", gSCidx)
    ls += format("gSCcircle = %d;", gSCcircle)
    ls += format("gSCpt = %s;", gSCpt)
    ls += "collectRotors();"
    ls += "tugDragDoneMB();"
    plicoRecord(ls)
}

# Bound to LEFT-UP by tugEnableDrag
function tugDragDoneMB() {
    if (gBusy == FALSE) {
        if (gPlicoRecord != "") {
            recordDrag()
        }

        # Move by rotation on rotor sets, smallest first
        gBusy = TRUE
        background ECHO pink
        refresh

        # If side chain mode
        if (gSCidx >= 0) {
            dragSC()
        }

        # Else
        else if (not gTow) {
            gOK = TRUE
            timeout ID"tug" 20.0 "timedOut(\"Tug timed out\")"
            if ((gCrotors.size < gNrotors.size) or (gNanchorIdx < 0)) {
                if (gCrotors.size > 4) {
                    tugTrackC()  # PULLSTRING MODEL
                }
                if (gOK and (gNrotors.size > 4)) {
                    tugTrackN()  # PULLSTRING MODEL
                }
            }
            else {
                if (gNrotors.size > 4) {
                    tugTrackN()  # PULLSTRING MODEL
                }
                if (gOK and (gCrotors.size > 4)) {
                    tugTrackC()  # PULLSTRING MODEL
                }
            }
            timeout ID"tug" OFF

            # If anchor angles acute, fail
            if (gOK == TRUE) {
                if (gCanchorIdx >= 0) {
                    var ic = getCwardBBidx(gCanchorIdx)
                    var in = getNwardBBidx(gCanchorIdx)
                    if ((ic >= 0) and
                        angle({atomIndex=ic}, {atomIndex=gCanchorIdx}, {atomIndex=in})
                        < 100.0) {
                        gOK = FALSE
                    }
                }
                if (gNanchorIdx >= 0) {
                    var ic = getCwardBBidx(gNanchorIdx)
                    var in = getNwardBBidx(gNanchorIdx)
                    if ((in >= 0) and
                        angle({atomIndex=ic}, {atomIndex=gNanchorIdx}, {atomIndex=in})
                        < 100.0) {
                        gOK = FALSE
                    }
                }
            }

            # If too far
            if (gOK == FALSE) {
                timedOut("TUG TOO FAR!")
            }

            # Else OK
            else {

                select ((atomno >= gNanchorNo) and (atomno <= gCanchorNo)
                    and (chain = gChain))
                var idx = {(atomno=@{{chain=gChain}.atomno.min})
                    and (chain=gChain)}.atomIndex


                var ihc = 0
                for (ihc = 0; ihc < 10; ihc++) {
                    handleCollisions2(FALSE, idx)
                    if (countCollisions(({})) == 0) {
                        break
                    }
                }
                if (ihc == 10) {
                    var p = prompt("Unable to handle all collisions!")
                    restore state gState
                }
            }
        }
        select {gCargoSet}
        gBusy = FALSE
        background ECHO yellow
        refresh
    }
}

# Bound to ALT-SHIFT-LEFT-DRAG by tugEnableDrag
function tugDrag2MB() {
    tugDragMB(TRUE)
}

# Bound to ALT-LEFT-DRAG by tugEnableDrag
function tugDragMB(alt) {
    if (gBusy == FALSE) {
        gBusy = TRUE
        var dx = (40.0 * (_mouseX - gMouseX))/_width
        var dy = (40.0 * (_mouseY - gMouseY))/_height
        var q = quaternion()
        var ptd = {@dx @dy 0}
        var pt = (!q)%ptd
        var axis = {0 0 0}
        if (distance(pt,  {0 0 0}) > 0.004) {
            # If sidechain mode
            if (gSCidx >= 0) {
                if ({atomIndex=gSCidx}.atomName == "O") {
                    dir = ((abs(dx) > abs(dy))
                        ? ((dx < 0) ? 2 : -2)
                        : ((dy < 0) ? 2 : -2))
                    counterRotate(gSCidx, dir, not alt)
                }
                else {
                    tugSideChain(pt)
                }
            }

            # Else
            else {

                # If new drag
                if (gNewDrag) {
                    gNewDrag = FALSE
                    save state gState
                }

                # If destination atom defined
                if (gDestAtomIdx >= 0) {
                    var v = {atomIndex=gDestAtomIdx}.xyz - {selected}.xyz
                    if (abs(angle({atomIndex=gDestAtomIdx}.xyz, {0 0 0}, pt)) < 90) {
                        pt = -v/20.0
                    }
                    else {
                        pt = v/20.0
                    }
                }

                # Move the cargo
                select {gCargoSet}

                # If pivots defined, rotate it
                if (g1pivotIdx >= 0) {

                    # If two pivots
                    if (g2pivotIdx >= 0) {
                        axis = {atomIndex=g2pivotIdx}
                    }

                    # Else
                    else {
                        axis = cross(pt, {0 0 0}) + {atomIndex=g1pivotIdx}.xyz
                    }

                    dir = ((abs(dx) > abs(dy))
                        ? ((dx < 0) ? 2 : -2)
                        : ((dy < 0) ? 2 : -2))
                    rotateSelectedRecord(g1pivotIdx, axis, dir)

                }

                # Else translate it
                else {
                    translateSelectedRecord(pt)
                }

                # If collisions
                var cNotSels = (within(kCtolerance, FALSE, {selected})
                    and not {gMovingSet})
                if (cNotSels.size > 0) {
                    gOk2 = TRUE
                    for (var i = 1; i <= cNotSels.size;  i++) {

                        # If net collision vector same as move vector
                        cSels = (within(kCtolerance, FALSE, cNotSels[i]) and {selected})
                        for (var j = 1; j <= cSels.size;  j++) {
                            var v1 = cNotSels[i].xyz - cSels[j].xyz
                            if (abs(angle(v1, {0 0 0}, pt)) < 90) {

                                # If tow mode
                                if (gTow) {

                                    # Make a dynamic pivot
                                    if (g1pivotIdx < 0) {
                                        g1pivotIdx = cSels[j].atomIndex
                                        g1dynamicIdx = cNotSels[i].atomIndex
                                        color {atomIndex=g1pivotIdx} lightgreen
                                        setDistanceIdx(cNotSels[i].atomIndex,
                                            cSels[j].atomIndex,
                                            kCtolerance + kDtolerance)
                                    }
                                    else if (g2pivotIdx < 0) {
                                        g2pivotIdx = cSels[j].atomIndex
                                        g2dynamicIdx = cNotSels[i].atomIndex
                                        color {atomIndex=g2pivotIdx} lightgreen
                                        setDistanceIdx(cNotSels[i].atomIndex,
                                            cSels[j].atomIndex,
                                            kCtolerance + kDtolerance)
                                    }
                                    else {
                                        gOk2 = FALSE
                                    }
                                }
                                else {

                                    # Try to resolve
                                    select cSels[j]
                                    var idx = {(atomno=@{{chain=gChain}.atomno.min})
                                        and (chain=gChain)}.atomIndex
                                    handleCollisions2(FALSE, idx)
                                }
                            }
                        } # endfor
                        if (gOk2 == FALSE) {
                            break
                        }
                    } # endfor

                    # If unable and not alt
                    if ((gOk2 == FALSE) and (not alt)) {

                        # Back off
                        background ECHO pink
                        delay 1
                        if (g1pivotIdx >= 0) {
                            rotateSelectedRecord(g1pivotIdx, axis, -a)
                        }
                        else {
                            translateSelectedRecord(-pt)
                        }
                        background ECHO yellow
                    }
                }
            }

            # If dynamic pivotsTBD
            if (g1dynamicIdx >= 0) {
                var v1 = {atomIndex=g1dynamicIdx}.xyz - {atomIndex=g1pivotIdx}.xyz
                if (abs(angle(v1, {0 0 0}, pt)) > 90) {
                    color {atomIndex=g1pivotIdx} @gAltScheme
                    g1pivotIdx = -1
                    g1dynamicIdx = -1

                }
            }
            if (g2dynamicIdx >= 0) {
                var v1 = {atomIndex=g2dynamicIdx}.xyz - {atomIndex=g2pivotIdx}.xyz
                if (abs(angle(v1, {0 0 0}, pt)) > 90) {
                    color {atomIndex=g2pivotIdx} @gAltScheme
                    g2pivotIdx = -1
                    g2dynamicIdx = -1
                }
            }

            gMouseX = _mouseX
            gMouseY = _mouseY
        }
        select {gCargoSet}
        gBusy = FALSE
    }
}

# Bound to ALT-LEFT-DOWN by tugEnableDrag
function tugMarkMB() {
    gMouseX = _mouseX
    gMouseY = _mouseY
    gNewDrag = TRUE
}

# Called by tugCargoMB
function tugEnableDrag() {
    gEcho = "__________TUG__________|ALT-CLICK=mark block|SHIFT-CLICK=anchors" +
        "|ALT-CTRL-CLICK=pivots|ALT-SHIFT-CLICK=dest atom|ALT-DRAG=move" +
        "|SHIFT-ALT-DRAG=alt move|DOUBLE-CLICK=exit"
    echo @gEcho

    # Allow atoms to be dragged
    bind "ALT-LEFT-DOWN" "tugMarkMB";
    bind "ALT-LEFT-UP" "tugDragDoneMB";
    bind "ALT-LEFT-DRAG" "tugDragMB";
    bind "ALT-SHIFT-LEFT-DRAG" "tugDrag2MB";

    unbind "SHIFT-LEFT-CLICK"
    bind "SHIFT-LEFT-CLICK" "_pickAtom";
    bind "SHIFT-LEFT-CLICK" "+:tugAnchorMB";

    bind "ALT-CTRL-LEFT-CLICK" "_pickAtom";
    bind "ALT-CTRL-LEFT-CLICK" "+:tugPivotMB";

    bind "ALT-SHIFT-LEFT-CLICK" "_pickAtom";
    bind "ALT-SHIFT-LEFT-CLICK" "+:tugDestAtomMB";
}

# Bound to SHIFT-LEFT-CLICK by tugCargoMB
function tugAnchorMB() {
    if ({atomIndex=_atomPicked}.chain == gChain) {
        var aPidx = getScBBidx( _atomPicked)

        var pno = {atomIndex=aPidx}.atomno
        if (pno > {atomIndex=gCcargoIdx}.atomno) {
            select {atomIndex=gCanchorIdx}
            halo off
            if (gCanchorIdx == aPidx) {
                gCanchorIdx = -1
                gCanchorNo = gMaxNo + 1
            }
            else {
                gCanchorIdx = aPidx
                gCanchorNo = {atomIndex=gCanchorIdx}.atomno
                select {atomIndex=gCanchorIdx}
                halo on
            }
            collectBBrotors(FALSE)
        }
        else if (pno < {atomIndex=gNcargoIdx}.atomno) {
            select {atomIndex=gNanchorIdx}
            halo off
            if (gNanchorIdx == aPidx) {
                gNanchorIdx = -1
                gNanchorNo = gMinNo - 1
            }
            else {
                gNanchorIdx = aPidx
                gNanchorNo = {atomIndex=gNanchorIdx}.atomno
                select {atomIndex=gNanchorIdx}
                halo on
            }
            collectBBrotors(TRUE)
        }
        else {
            towCargoMB()
        }

        # Get moving atoms set
        gMovingSet = {((atomno < gCanchorNo) and (atomno > gNanchorNo)
            and (chain=gChain))}
    }
    select {gCargoSet}
}

# Bound to ALT-SHIFT-LEFT-CLICK by tugCargoMB
function tugDestAtomMB() {
    var aOk = TRUE
    if ({atomIndex=_atomPicked}.chain == gChain) {

        var pno = {atomIndex=_atomPicked}.atomno
        if ((pno <= {atomIndex=gCcargoIdx}.atomno) and (pno >= {atomIndex=gNcargoIdx}.atomno)) {
            aOk = FALSE
        }
    }
    if (aOk) {
        select {atomIndex=gDestAtomIdx}
        star off
        if (gDestAtomIdx == _atomPicked) {
            gDestAtomIdx = -1
        }
        else {
            gDestAtomIdx = _atomPicked
            select {atomIndex=gDestAtomIdx}
            star on
        }
        select {gCargoSet}
    }
}

# Bound to CTRL-LEFT-CLICK by tugCargoMB
function tugPivotMB() {
    if (g1pivotIdx == _atomPicked) {
        color {atomIndex=g1pivotIdx} @gScheme
        if (g2pivotIdx >= 0) {
            g1pivotIdx = g2pivotIdx
            g2pivotIdx = -1
        }
        else {
            g1pivotIdx = -1
        }
    }
    else if (g2pivotIdx == _atomPicked) {
        color {atomIndex=g2pivotIdx} @gScheme
        g2pivotIdx = -1
    }
    else if (g1pivotIdx >= 0) {
        if (g2pivotIdx >= 0) {
            color {atomIndex=g2pivotIdx} @gScheme
        }

        g2pivotIdx = _atomPicked
        color {atomIndex=g2pivotIdx} green
    }
    else {
        g1pivotIdx = _atomPicked
        color {atomIndex=g1pivotIdx} green
    }
    select {gCargoSet}
}

# Bound to SHIFT-LEFT-CLICK by plicoTug
function towCargoMB() {
    gTow = TRUE
    gChain = {atomIndex=_atomPicked}.chain
    gMinNo = {chain=gChain}.atomno.min
    gMaxNo = {chain=gChain}.atomno.max
    gCcargoIdx = -1
    gNcargoIdx = -1
    gCanchorIdx = -1
    gCanchorNo = gMaxNo + 1
    gNanchorIdx = -1
    gNanchorNo = gMinNo - 1

    # Highlight cargo cluster
    select {chain=gChain}
    gCargoSet = {selected}
    gMovingSet = {selected}
    setColors()

    # Enable dragging
    tugEnableDrag()
    select {gCargoSet}
    halo off
    var es = gEcho.replace("anchors","mark chain")
    echo @es
    unbind "SHIFT-LEFT-CLICK"
    bind "SHIFT-LEFT-CLICK" "_pickAtom";
    bind "SHIFT-LEFT-CLICK" "+:towCargoMB";
}

# Bound to ALT-LEFT-CLICK by plicoTug
function tugCargoMB() {

    # If O or movable side chain atom picked
    if (({atomIndex=_atomPicked}.atomName = "O")
        or (isMovableSideChain( _atomPicked))) {
        if (gSCidx >= 0) {
            draw gSCcircle DELETE
        }
        if (gSCidx == _atomPicked) {
            gSCidx = -1
        }
        else {
            gSCidx = _atomPicked
            gSCpt = {atomIndex=gSCidx}.xyz
            draw gSCcircle CIRCLE {atomIndex=gSCidx} MESH NOFILL
        }
    }
    else {
        if ({atomIndex=_atomPicked}.chain != gChain) {
            if (gTow) {
                select {chain=gChain}
                color {selected} @gScheme
            }
            gChain = {atomIndex=_atomPicked}.chain
            select ({atomIndex=gCcargoIdx} or {atomIndex=gNcargoIdx}
                or {atomIndex=gCanchorIdx} or {atomIndex=gNanchorIdx})
            halo off
            gCcargoIdx = -1
            gNcargoIdx = -1
            gCanchorIdx = -1
            gNanchorIdx = -1
        }
        gTow = FALSE
        gMinNo = {chain=gChain}.atomno.min
        gMaxNo = {chain=gChain}.atomno.max
        if (gNanchorIdx < 0) {
            gNanchorNo = gMinNo - 1
        }
        if (gCanchorIdx < 0) {
            gCanchorNo = gMaxNo + 1
        }
        var aPidx = getScBBidx( _atomPicked)

        gSCidx = -1
        draw gSCcircle DELETE

        # If existing cWard cargo picked
        if (gCcargoIdx == aPidx) {

            # Clear the highlight
            select {atomIndex=gCcargoIdx}
            halo off

            # If nWard cargo exists, mark it as the cWard cargo
            if (gNcargoIdx != gCcargoIdx) {
                gCcargoIdx = getCpIdx(gNcargoIdx)
                gCcargoNo = {atomIndex=gCcargoIdx}.atomno
            }
            else {
                gCcargoIdx = -1
                gNcargoIdx = -1
            }
        }
        else if (gNcargoIdx == aPidx) {
            select {atomIndex=gNcargoIdx}
            halo off
            gNcargoIdx = getNmIdx(gCcargoIdx)
            gNcargoNo = {atomIndex=gNcargoIdx}.atomno
        }
        else if (gCcargoIdx >= 0) {

            var no = {atomIndex=aPidx}.atomno

            # If pick is nWard of it
            if (no < {atomIndex=gCcargoIdx}.atomno) {

                # If exists, clear its highlight
                if (gNcargoIdx != gCcargoIdx) {
                    select {atomIndex=gNcargoIdx}
                    halo off
                }

                # Set new nWard cargo and highlight it
                gNcargoIdx = getNmIdx(aPidx)
                gNcargoNo = {atomIndex=gNcargoIdx}.atomno
            }

            # Else cWard
            else {

                # Clear its old highlight
                select {atomIndex=gCcargoIdx}
                if (gNcargoIdx != gCcargoIdx) {
                    halo off
                }

                # Set new cWard cargo and highlight
                gCcargoIdx = getCpIdx(aPidx)
                gCcargoNo = {atomIndex=gCcargoIdx}.atomno
            }
        }

        # Else no cWard cargo
        else {

            # Set new cWard cargo and highlight
            gCcargoIdx = getCpIdx(aPidx)
            gCcargoNo = {atomIndex=gCcargoIdx}.atomno
            gNcargoIdx = getNmIdx(gCcargoIdx)
            gNcargoNo = {atomIndex=gNcargoIdx}.atomno

            # Set default cWard anchor at cWard N
            var iNo = gMaxNo
            for (; iNo > 0; iNo--) {
                if ({(atomno=iNo) and (chain=gChain)}.atomName == "N") {
                    break;
                }
            }
            gCanchorIdx = {(atomno=iNo) and (chain=gChain)}.atomIndex
            gCanchorNo = {atomIndex=gCanchorIdx}.atomno
        }

        # If any anchor now inside cargo cluster, kill it
        if ({atomIndex=gCanchorIdx}.atomno <= {atomIndex=gCcargoIdx}.atomno) {
            gCanchorIdx = -1
            gCanchorNo = gMaxNo + 1
        }
        if ({atomIndex=gNanchorIdx}.atomno >= {atomIndex=gNcargoIdx}.atomno) {
            gNanchorIdx = -1
            gNanchorNo = gMinNo - 1
        }

        # Highlight cargo cluster
        selectNwardIdx(gCcargoIdx, gNcargoIdx)
        gCargoSet = {selected}
        setColors()

        # Collect the rotor sets
        collectRotors()

        # Get moving atoms set
        gMovingSet = {((atomno < gCanchorNo) and (atomno > gNanchorNo)
            and (chain=gChain))}
    }

    # Enable dragging
    tugEnableDrag()

    select {gCargoSet}
}

# Top level of Tug
function plicoTug() {

    # Bad idea to proceed when collisions present
    var cc = countCollisions(({}))
    if (cc > 0) {
        var p = prompt(format("%d collision%s present! Proceed anyway?",
            cc, ((cc > 1) ? "s" : "")), "OK|Cancel", TRUE)
        if (p == "Cancel") {
            quit
        }
    }

    # Push selected
    gSelSaves = {selected}
    select all
    gZoom = script("show zoom")
    gRotate = script("show rotation")
    write tugsave.pdb
    select none

    gScheme = defaultColorScheme
    gAltScheme = ((gScheme == "Jmol") ? "Rasmol" : "Jmol")
    set echo TOP LEFT
    background ECHO yellow
    gEcho = ("_________TUG_________|ALT-CLICK=mark block|SHIFT-CLICK=mark chain" +
        "|DOUBLE-CLICK=exit")
    echo @gEcho
    gCrotors = array()
    gNrotors = array()
    clearAtomIdxs()
    gChain = ""
    unbind

    bind "ALT-LEFT-CLICK" "_pickAtom";
    bind "ALT-LEFT-CLICK" "+:tugCargoMB";
    bind "SHIFT-LEFT-CLICK" "_pickAtom";
    bind "SHIFT-LEFT-CLICK" "+:towCargoMB";
    bind "DOUBLE" "tugExit";
}

# Bound to DOUBLE by plicoTug
function tugExit() {
    var p = prompt("Exit tug?", "Yes|No|Undo", TRUE)
    if (p == "Undo") {
        load tugsave.pdb
        script inline gZoom
        rotate @gRotate
        echo Tug session undone
        if (gPlicoRecord != "") {
            plicoRecord("load tugsave.pdb;")
        }
    }
    if (p != "No") {
        unbind
        halo off
        set allowMoveAtoms FALSE
        echo
        select all
        halo off
        star off
        color {selected} @gScheme
        draw gSCcircle DELETE
        gBusy = FALSE
        background ECHO yellow

        # Pop selected
        select gSelSaves
    }
}
# End of TUG.SPT

Contributors

Remig