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

From Jmol
Jump to navigation Jump to search
m (Add description)
(Avoid "axis," a newly reserved word)
 
(16 intermediate revisions by the same user not shown)
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. A full description of its capabilities can be found [[User:Remig/plico/tugManual|here]].
  
 
'''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:
 
<pre>Title=PLICO Tug
 
<pre>Title=PLICO Tug
Script=script <path to your scripts folder>/tug.spt;plicotug</pre>
+
Script=script <path to your scripts directory>/tug.spt;plico_tug</pre>
saved as plicotug.macro in your .jmol/macros folder as described in [[Macro]].
+
saved as plicotug.macro in your .jmol/macros directory as described in [[Macro]].
  
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 directory 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.20 beta    4/12/2016 -axis is now a reserved word
 
#
 
#
These functions support protein manipulation in Jmol
+
Translate or rotate a stretch of a polypeptide against itself
var kTug = 1
+
#    or against other chains by mouse actions
var kDtolerance = 0.2
+
#
var kAtolerance = 5.0
+
kTug = 3
var kCtolerance = 1.85
+
gCanchorIdx = -1
var kMtolerance = 0.8
+
gCanchorNo = -1
var kMinRama = 2
+
gPlico = "TUG"
var kCollisionLimit = 200
+
gNanchorIdx = -1
var gCanchorIdx = -1
+
gNanchorNo = -1
var gCanchorNo = -1
+
gCcargoIdx = -1
var gNanchorIdx = -1
+
gNcargoIdx = -1
var gNanchorNo = -1
+
gCcargoNo = -1
var gNanchorPidx = -1
+
gNcargoNo = -1
var gCanchorXyz = {0 0 0}
+
gDestAtomIdx = -1
var gNanchorXyz = {0 0 0}
+
g1pivotIdx = -1
var gNanchorPxyz = {0 0 0}
+
g2pivotIdx = -1
var gCcargoIdx = -1
+
gSelSaves = ({})
var gNcargoIdx = -1
+
gCrotors = array()
var gCcargoXyz = {0 0 0}
+
gNrotors = array()
var gNcargoXyz = {0 0 0}
+
gOkCollide = ({})
var gCcargoNo = -1
+
gChain = ""
var gNcargoNo = -1
+
gMinNo = 1
var gDestAtomIdx = -1
+
gMaxNo = 9999
var g1pivotIdx = -1
+
gCargoSet = ({})
var g2pivotIdx = -1
+
gMovingSet = ({})
var gSelSaves = ({})  
+
gBusy = false
var gCrotors = array()
+
gSCidx = -1
var gNrotors = array()  
+
gSCcircle = -1
var gMouseX = 0
+
gSCpt = {0 0 0}
var gMouseY = 0
+
gTargetPt = {0 0 0}
var gAc = ({})
+
gNewDrag = false
var gChain = ""
+
gTow = false
var gMinNo = 1  
+
g1dynamicIdx = -1
var gMaxNo = 9999  
+
g2dynamicIdx = -1
var gScheme = "Jmol"
+
gSCcheck = true
var gAltScheme = "Rasmol"
+
gBondPicking = false
var gCargoAtoms = ({})
+
gFreeze = array()
var gSeed = 23423.52
+
gToab = false
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 gCountdown = 0
 
var gZoom = ""
 
var gRotate = ""
 
  
# 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
+
function get_cp_idx (idx) {
    var cdist = distance(pt, {atomIndex=i2})
+
     var no = {atomIndex=idx}.atomno
    var factor = (dist/cdist)
+
     while ((no < gMaxNo) and ({(atomno=no) and (chain=gChain)
    var lpt = v * factor
+
        and thisModel}.atomName != "C")) {
 
+
         no++
    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
+
     return ({(atomno=no) and (chain=gChain) and thisModel}.atomIndex)
 
}
 
}
  
function getCAmNo (iNo) {
+
function get_cm_no (iNo) {
     while ((iNo > 0) and ({(atomno=iNo) and (chain=gChain)}.atomName != "CA")) {
+
     while ((iNo > 0) and ({(atomno=iNo) and (chain=gChain)
 +
        and thisModel}.atomName != "C")) {
 
         iNo--
 
         iNo--
 
     }
 
     }
Line 105: Line 67:
 
}
 
}
  
function getCAmIdx (idx) {
+
function get_nm_idx (idx) {
     var no = {atomIndex=idx and (chain=gChain)}.atomno
+
     var no = {atomIndex=idx}.atomno
     no = getCAmNo( no)
+
     while ((no > 0) and ({(atomno=no) and (chain=gChain)
    return ({(atomno=no) and (chain=gChain)}.atomIndex)
+
         and thisModel}.atomName != "N")) {
}
+
         no--
 
 
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
+
     return ({(atomno=no) and (chain=gChain) and thisModel}.atomIndex)
}
 
 
 
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) {
+
function get_np_no (iNo) {
     while ((iNo < gMaxNo) and ({(atomno=iNo) and (chain=gChain)}.atomName != "N")) {
+
     while ((iNo < gMaxNo) and ({(atomno=iNo) and (chain=gChain)
 +
        and thisModel}.atomName != "N")) {
 
         iNo++
 
         iNo++
 
     }
 
     }
Line 170: Line 84:
 
}
 
}
  
function getNpIdx (idx) {
+
function get_cb_idx (BBidx) {
    var no = {atomIndex=idx}.atomno
 
    no = getNpNo( no)
 
    return ({(atomno=no) and (chain=gChain)}.atomIndex)
 
}
 
 
 
function getCBidx (BBidx) {
 
 
     var no = {atomIndex=BBidx}.atomno
 
     var no = {atomIndex=BBidx}.atomno
 
     var i = 1
 
     var i = 1
 
     for (; i < 5; i++) {
 
     for (; i < 5; i++) {
         if ({(atomno=@{no+i}) and (chain=gChain)}.atomName == "CB") {
+
         if ({(atomno=@{no+i}) and (chain=gChain)
 +
            and thisModel}.atomName == "CB") {
 
             break
 
             break
 
         }
 
         }
 
     }
 
     }
     return {(atomno=@{no+i}) and (chain=gChain)}.atomIndex
+
     return {(atomno=@{no+i}) and (chain=gChain) and thisModel}.atomIndex
 
}
 
}
  
function getOidx (BBidx) {
+
function get_o_idx (BBidx) {
 
     var no = {atomIndex=BBidx}.atomno
 
     var no = {atomIndex=BBidx}.atomno
 
     var i = 1
 
     var i = 1
 
     for (; i < 4; i++) {
 
     for (; i < 4; i++) {
         if ({(atomno=@{no+i}) and (chain=gChain)}.atomName == "O") {
+
         if ({(atomno=@{no+i}) and (chain=gChain)
 +
            and thisModel}.atomName == "O") {
 
             break
 
             break
 
         }
 
         }
 
     }
 
     }
     return {(atomno=@{no+i}) and (chain=gChain)}.atomIndex
+
     return {(atomno=@{no+i}) and (chain=gChain) and thisModel}.atomIndex
 
}
 
}
  
function getNwardBBno (iNo) {
+
function get_nward_bb_idx (idx, iChain) {
     while ((iNo >= 0) and (
+
     var no = {atomIndex=idx}.atomno - 1
         ({(atomno=iNo) and (chain=gChain)}.atomName != "N")
+
    for (; no >= 0; no--) {
         and ({(atomno=iNo) and (chain=gChain)}.atomName != "C")
+
         var atomName = {(atomno=no) and (chain=iChain)
        and ({(atomno=iNo) and (chain=gChain)}.atomName != "CA"))) {
+
            and thisModel}.atomName
         iNo--
+
         if ((atomName = "N") or (atomName = "C") or (atomName = "CA")) {
 +
            break
 +
         }
 
     }
 
     }
     return iNo
+
     return ((no >= 0) ? ({(atomno=no) and (chain=iChain)
 +
        and thisModel}.atomIndex) : -1)
 
}
 
}
  
function getNwardBBidx (idx) {
+
function get_cward_bb_idx (idx, iChain) {
     var no = {atomIndex=idx}.atomno - 1
+
     var no = {atomIndex=idx}.atomno + 1
     no = getNwardBBno( no)
+
     for (; no < gMaxNo; no++) {
     return ((no >= 0) ? ({(atomno=no) and (chain=gChain)}.atomIndex) : -1)
+
        var atomName = {(atomno=no) and (chain=iChain)
 +
            and thisModel}.atomName
 +
        if ((atomName = "N") or (atomName = "C") or (atomName = "CA")) {
 +
            break
 +
        }
 +
    }
 +
     return ((no >= 0) ? ({(atomno=no) and (chain=iChain)
 +
        and thisModel}.atomIndex) : -1)
 
}
 
}
  
function getCwardBBno (iNo) {
+
function get_sc_set (scIdx, iChain) {
     while ((iNo < gMaxNo) and (
+
     var scSet = ({})
        ({(atomno=iNo) and (chain=gChain)}.atomName != "N")
+
    var idx = get_sc_bb_idx(scIdx, iChain)
         and ({(atomno=iNo) and (chain=gChain)}.atomName != "C")
+
    var iNo = {atomIndex=idx}.atomno + 3
         and ({(atomno=iNo) and (chain=gChain)}.atomName != "CA"))) {
+
 
         iNo++
+
    for (var i = 1; i < 20; i++) {
 +
         idx = {(atomno=@{iNo+i}) and (chain=iChain)
 +
            and thisModel}.atomIndex
 +
         if (is_bb_idx(idx)) {
 +
            break
 +
        }
 +
         scSet = scSet or {atomIndex=idx}
 
     }
 
     }
     return iNo
+
     return scSet
 
}
 
}
  
function getCwardBBidx (idx) {
+
function get_sc_bb_idx (idx, iChain) {
    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
 
     var no = {atomIndex=idx}.atomno
 
     for (; no > 0; no--) {
 
     for (; no > 0; no--) {
         if ({(atomno=no) and (chain=gChain)}.atomName == "CA") {
+
         if ({(atomno=no) and (chain=iChain)
 +
            and thisModel}.atomName == "CA") {
 
             break
 
             break
 
         }
 
         }
         else if ({(atomno=no) and (chain=gChain)}.atomName == "C") {
+
         else if ({(atomno=no) and (chain=iChain)
 +
            and thisModel}.atomName == "C") {
 
             break
 
             break
 
         }
 
         }
         else if ({(atomno=no) and (chain=gChain)}.atomName == "N") {
+
         else if ({(atomno=no) and (chain=iChain)
 +
            and thisModel}.atomName == "N") {
 
             break
 
             break
 
         }
 
         }
         else if ({(atomno=no) and (chain=gChain)}.atomName == "CB") {
+
         else if ({(atomno=no) and (chain=iChain)
 +
            and thisModel}.atomName == "CB") {
 
             no -= 3
 
             no -= 3
 
             break
 
             break
 
         }
 
         }
 
     }
 
     }
     return {(atomno=no) and (chain=gChain)}.atomIndex
+
     return {(atomno=no) and (chain=iChain) and thisModel}.atomIndex
 
}
 
}
  
function isBBidx(aIdx) {
+
function is_bb_idx(aIdx) {
     var ret = FALSE
+
     var ret = false
 
     switch({atomIndex=aIdx}.atomName) {
 
     switch({atomIndex=aIdx}.atomName) {
 
     case "N":
 
     case "N":
 
     case "CA":
 
     case "CA":
 
     case "C":
 
     case "C":
         ret = TRUE
+
         ret = true
 
         break
 
         break
 
     }
 
     }
Line 262: Line 186:
 
}
 
}
  
function isSCidx(aIdx) {
+
function is_sc_idx(aIdx) {
  
     var ret = FALSE
+
     var ret = false
     if (isBBidx(aIDx) == FALSE) {
+
     if (not is_bb_idx(aIDx)) {
       
+
 
         ret = TRUE
+
         ret = true
 
         switch({atomIndex=aIdx}.atomName) {
 
         switch({atomIndex=aIdx}.atomName) {
 
         case "O":
 
         case "O":
 
         case "CB":
 
         case "CB":
             ret = FALSE
+
             ret = false
 
             break
 
             break
 
         }
 
         }
Line 278: Line 202:
 
}
 
}
  
function addSideChainToSelection(CAno, isAdd, addOXT) {
+
function select_add_sc(fromIdx) {
    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 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)
         {(atomno=iNo) and (chain=gChain)}.selected = TRUE
+
        and thisModel and sidechain}) {
         iNo++
+
         var a = {(atomno=iNo) and (chain=iChain)and thisModel}
        if (iNo > gMaxNo) {
+
        a.selected = true
            break
+
         iNo++
        } 
 
 
     }
 
     }
 
}
 
}
  
# First and last are BB atoms
+
# Resolve collisions on selection
# Any side atoms in the range are also selected
+
function handle_collisions( targetIdx) {
function selectNward (firstIdx, lastIdx) {
 
    var firstno = ((firstIdx < 0) ? {atomIndex=lastIdx}.atomno : {atomIndex=firstIdx}.atomno)
 
    var lastno = ((lastIdx < 0) ? firstno : {atomIndex=lastIdx}.atomno)
 
 
 
    select (atomno <= firstno) and (atomno >= lastno)
 
   
 
    if ({(atomno=firstno) and (chain=gChain)}.atomName == "C") { # if psi
 
        addSideChainToSelection(firstno-1, TRUE, TRUE)
 
        {(atomno=@{firstno+1}) and (chain=gChain)}.selected = TRUE # add O
 
    }
 
    if ({(atomno=firstno) and (chain=gChain)}.atomName == "CA") {
 
        addSideChainToSelection(firstno, TRUE, FALSE)
 
    }
 
    if ({(atomno=lastno) and (chain=gChain)}.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 selectCward (firstIdx, lastIdx) {
 
    var firstno = ((firstIdx < 0) ? gMaxNo : {atomIndex=firstIdx}.atomno)
 
    var lastno = ((lastIdx < 0) ? 1 : {atomIndex=lastIdx}.atomno)
 
   
 
    # If nWard anchor in range, begin selection with it
 
    if (gNanchorIdx >= 0) {
 
        var aNo = {atomIndex=gNanchorIdx}.atomno
 
        if (aNo > firstNo) {
 
            firstno = aNo
 
        }
 
    }
 
   
 
    # If cWard anchor in range, end selection with it
 
    if (gCanchorIdx >= 0) {
 
        var aNo = {atomIndex=gCanchorIdx}.atomno
 
        if (aNo < lastNo) {
 
            lastno = aNo
 
        }
 
    }
 
   
 
    select (atomno >= firstno) and (atomno <= lastno)
 
   
 
    if ({(atomno=firstno) and (chain=gChain)}.atomName == "C") { # if psi
 
        addSideChainToSelection(firstno-1, FALSE, FALSE)
 
    }
 
    if ({(atomno=lastno) and (chain=gChain)}.atomName == "CA") {
 
        addSideChainToSelection(lastno, TRUE, FALSE)
 
    }
 
    if ({(atomno=lastno) and (chain=gChain)}.atomName == "C") { # if psi
 
        addSideChainToSelection(lastno-1, TRUE, TRUE)
 
        {(atomno=@{lastno+1}) and (chain=gChain)}.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 lcAtoms = ({})
 
    for (var idx = {*}.min.atomIndex; idx <= {*}.max.atomIndex; idx++) {
 
        lcAtoms = lcAtoms or (within(kCtolerance, FALSE, {atomIndex=idx})
 
            and {atomIndex > idx}
 
            and not {rc}
 
            and not connected({atomIndex=idx}))
 
    }
 
    return lcAtoms.size
 
}
 
 
 
# Resolve collisions
 
function handleCollisions( nWard, targetIdx) {
 
  
 
     # For all selected atoms
 
     # For all selected atoms
 
     for (var iNo = {selected}.min.atomno; iNo <= {selected}.max.atomno; iNo++) {
 
     for (var iNo = {selected}.min.atomno; iNo <= {selected}.max.atomno; iNo++) {
         var idx = {(atomno=iNo) and (chain=gchain)}.atomIndex
+
         var idx = {(atomno=iNo) and (chain=gchain)
 +
            and thisModel}.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})
 
                 and not {atomIndex=idx}
 
                 and not {atomIndex=idx}
 
                 and not {gOkCollide}
 
                 and not {gOkCollide}
 
                 and not connected({atomIndex=idx}))
 
                 and not connected({atomIndex=idx}))
           
+
             if (lcAtoms) {
             if (lcAtoms.size > 0) {
 
           
 
 
                 # Ignore kinked BB
 
                 # Ignore kinked BB
                 if (isBBidx(idx) and angle({atomIndex=@{getCwardBBidx(idx)}},
+
                 try {
                    {atomIndex=idx} , {atomIndex=@{getNwardBBidx(idx)}}) < 100) {
+
                    if (is_bb_idx(idx) and (angle(
                    continue
+
                        {atomIndex=@{get_cward_bb_idx(idx, gChain)}}, {atomIndex=idx},
                }
+
                        {atomIndex=@{get_nward_bb_idx(idx, gChain)}}) < 100.0)) {
           
+
                         continue
                #gCountdown--
 
                if (gCountdown < 0) {
 
                    timedOut("Too many collsions")
 
                }   
 
               
 
                # For all local water colliders, delete
 
                var recollect = FALSE
 
                for (var c = 1; c <= lcAtoms.size; c++ ) {
 
                    if (lcAtoms[c].group = "HOH") {
 
                         delete {atomIndex=@{lcAtoms[c].atomIndex}}
 
                        recollect = TRUE
 
 
                     }
 
                     }
 
                 }
 
                 }
                  
+
                 catch {
                # Recollect local colliders if needed
 
                if (recollect) {
 
                    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 all local colliders
 
                 for (var c = 1; c <= lcAtoms.size; c++ ) {
 
                 for (var c = 1; c <= lcAtoms.size; c++ ) {
               
 
                    # If it is with side chain not proline, fix it
 
 
                     var cidx = lcAtoms[c].atomIndex
 
                     var cidx = lcAtoms[c].atomIndex
                     if (isSCidx(cidx) and ({atomIndex=cidx}.group != "PRO")) {
+
 
                         fixSCcollision2(cidx)
+
                    # If it is with water, delete it
                        recollect = TRUE
+
                     if (lcAtoms[c].group = "HOH") {
 +
                        delete {atomIndex=cidx}
 +
                    }
 +
 
 +
                    # else if it is with side chain not proline, fix it
 +
                    else if (is_sc_idx(cidx) and ({atomIndex=cidx}.group != "PRO")) {
 +
                         fix_sc_collision_2(cidx)
 
                          
 
                          
 
                         # If not fixed, exit fail
 
                         # If not fixed, exit fail
                         if (gOk2 == FALSE) {
+
                         if (not gOk2) {
 +
                            return # early exit (break n jmol bug)
 +
                        }
 +
                    }
 +
 
 +
                    # else if it is itself a side chain not proline, fix it
 +
                    else if (is_sc_idx(idx) and ({atomIndex=idx}.group != "PRO")) {
 +
                        fix_sc_collision_2(idx)
 +
 
 +
                        # If not fixed, exit fail
 +
                        if (not gOk2) {
 
                             return # early exit (break n jmol bug)
 
                             return # early exit (break n jmol bug)
 
                         }
 
                         }
 
                     }
 
                     }
                }
+
 
               
+
                    # Else if it is with O, counter-rotate
                # Recollect local colliders if needed
+
                    else if (lcAtoms[c].atomName = "O") {
                if (recollect) {
+
                        counter_rotate_2(lcAtoms[c].atomIndex,
                    lcAtoms = (within(kCtolerance, FALSE, {atomIndex=idx})
+
                            {atomIndex=idx}.xyz, targetIdx, false)
                         and not {atomIndex=idx}
+
 
                         and not {gOkCollide}
+
                         # If not fixed, exit fail
                        and not connected({atomIndex=idx}))
+
                         if (not gOk2) {
                }
+
                            return # early exit (break n jmol bug)
                      
+
                        }
                # For all local colliders
+
                     }
                for (var c = 1; c <= lcAtoms.size; c++ ) {
+
 
               
+
                     # Else if it is itself O, counter-rotate
                     # If it is with O, counter-rotate
+
                     else if ({atomIndex=idx}.atomName = "O") {
                     if ({atomIndex=@{lcAtoms[c].atomIndex}}.atomName = "O") {
+
                         counter_rotate_2(idx, lcAtoms[c].xyz, targetIdx, false)
                         counterRotate2(idx, lcAtoms[c].atomIndex, targetIdx)
+
 
                       
 
 
                         # If not fixed, exit fail
 
                         # If not fixed, exit fail
                         if (gOk2 == FALSE) {
+
                         if (not gOk2) {
 
                             return # early exit (break n jmol bug)
 
                             return # early exit (break n jmol bug)
 
                         }
 
                         }
 
                     }
 
                     }
                 }
+
 
 +
                    else {    # Else not fixed, exit fail
 +
                        gOk2 = false
 +
                        return # early exit (break n jmol bug)
 +
                    }
 +
                 } # endfor
 
             }
 
             }
 
         }
 
         }
Line 496: Line 301:
  
 
# Rotate rotor set to move target atom to its proper place
 
# Rotate rotor set to move target atom to its proper place
function tugTrackIdx(targetIdx, targetPt, nWard, cDetect) {
+
function tug_track_idx(targetIdx, targetPt, nWard, cDetect) {
     gOK = FALSE
+
     gOK = false
 
     var pt = targetPt
 
     var pt = targetPt
 
     var dist = distance(pt, {atomIndex=targetIdx}.xyz)
 
     var dist = distance(pt, {atomIndex=targetIdx}.xyz)
  
 
     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 325:
 
                     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 334:
 
                 }
 
                 }
 
             }
 
             }
           
+
 
 
             # 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 343:
 
             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)
 
             var dh = angle({atomIndex=i1}, {atomIndex=i2}, {atomIndex=i3}, {atomIndex=i4})
 
             var dh = angle({atomIndex=i1}, {atomIndex=i2}, {atomIndex=i3}, {atomIndex=i4})
             if (dh == "NaN") {
+
             if (abs(dh).type == "string") {
 
                 dh = -50
 
                 dh = -50
 
             }
 
             }
 
             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
             var movePt = FALSE
+
             var movePt = false
 
             if (nWard) {
 
             if (nWard) {
 
                 if ({atomIndex=i2}.atomName="CA") {
 
                 if ({atomIndex=i2}.atomName="CA") {
                     psi = angle({atomIndex=@{getCwardBBidx(i1)}}, {atomIndex=i1},
+
                     psi = angle({atomIndex=@{get_cward_bb_idx(i1, gChain)}}, {atomIndex=i1},
 
                         {atomIndex=i2}, {atomIndex=i3}) + dt
 
                         {atomIndex=i2}, {atomIndex=i3}) + dt
 
                 }
 
                 }
 
                 else {
 
                 else {
 
                     phi = angle({atomIndex=i1}, {atomIndex=i2},
 
                     phi = angle({atomIndex=i1}, {atomIndex=i2},
                         {atomIndex=i3}, {atomIndex=@{getNwardBBidx(i3)}}) + dt
+
                         {atomIndex=i3}, {atomIndex=@{get_nward_bb_idx(i3, gChain)}}) + dt
 
                 }
 
                 }
               
+
 
 
                 if ({atomIndex=i2}.atomno > {atomIndex=targetIdx}.atomno) {
 
                 if ({atomIndex=i2}.atomno > {atomIndex=targetIdx}.atomno) {
                     movePt = TRUE
+
                     movePt = true
                     selectNward(i3, getCwardBBidx(targetIdx))
+
                     select_nward_idx(i3, get_cward_bb_idx(targetIdx, gChain))
                     {atomIndex=targetIdx}.selected = TRUE
+
                     {atomIndex=targetIdx}.selected = true
 
                 }
 
                 }
 
                 else {
 
                 else {
                     selectCward(i2, targetIdx)
+
                     select_cward_idx(i2, targetIdx)
 
                 }
 
                 }
 
             }
 
             }
 
             else {
 
             else {
 
                 if (({atomIndex=i2}.atomName="CA")) {
 
                 if (({atomIndex=i2}.atomName="CA")) {
                     phi = angle({atomIndex=@{getNwardBBidx(i1)}}, {atomIndex=i1},
+
                     phi = angle({atomIndex=@{get_nward_bb_idx(i1, gChain)}}, {atomIndex=i1},
 
                         {atomIndex=i2}, {atomIndex=i3}) + dt
 
                         {atomIndex=i2}, {atomIndex=i3}) + dt
 
                 }
 
                 }
 
                 else {
 
                 else {
 
                     psi = angle({atomIndex=i2}, {atomIndex=i3},
 
                     psi = angle({atomIndex=i2}, {atomIndex=i3},
                         {atomIndex=i4}, {atomIndex=@{getCwardBBidx(i4)}}) + dt
+
                         {atomIndex=i4}, {atomIndex=@{get_cward_bb_idx(i4, gChain)}}) + dt
 
                 }
 
                 }
               
+
 
 
                 if ({atomIndex=i2}.atomno < {atomIndex=targetIdx}.atomno) {
 
                 if ({atomIndex=i2}.atomno < {atomIndex=targetIdx}.atomno) {
                     movePt = TRUE
+
                     movePt = true
                     selectCward(i3, getNwardBBidx(targetIdx))
+
                     select_cward_idx(i3, get_nward_bb_idx(targetIdx, gChain))
                     {atomIndex=targetIdx}.selected = TRUE
+
                     {atomIndex=targetIdx}.selected = true
 
                 }
 
                 }
 
                 else {
 
                 else {
                     selectNward(i2, targetIdx)
+
                     select_nward_idx(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 410:
 
                     {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 416:
 
                 # 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
 
                     for (var ci = 0; ci < 4; ci++) {
 
                     for (var ci = 0; ci < 4; ci++) {
 
                         if (ci < 3) {
 
                         if (ci < 3) {
 
                             dt /= 2
 
                             dt /= 2
                         }                      
+
                         }
                         handleCollisions( nWard, targetIdx)
+
                         handle_collisions( targetIdx)
                         if (gOk2==FALSE) {
+
                         if (not gOk2) {
                             wasCollision = TRUE
+
                             wasCollision = true
 
                             rotateSelected {atomIndex=i2} {atomIndex=i3} @{-dt}
 
                             rotateSelected {atomIndex=i2} {atomIndex=i3} @{-dt}
 
                         }
 
                         }
Line 626: Line 436:
 
                             break
 
                             break
 
                         }
 
                         }
                       
+
 
 
                         if (dt < 0.01) {
 
                         if (dt < 0.01) {
 
                             break
 
                             break
Line 632: Line 442:
 
                     } # 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 448:
 
                     {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) {
                 gOK = TRUE
+
                 gOK = true
 
                 gTargetPt = pt
 
                 gTargetPt = pt
 
                 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
 
         }
 
         }
     }  # endfor 10 passes
+
     }  # endfor 20 passes
 
}
 
}
  
 
# 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 do_counter_rotate(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 counter_rotate(oIdx, dir, nWard) {
 +
 
 +
    var iChain = {atomIndex=oIdx}.chain
 
     var selsave = {selected}
 
     var selsave = {selected}
     gOk2 = TRUE
+
     var cIdx = get_sc_bb_idx(oIdx, iChain)
     var oIdx = aIdx
+
     var nIdx = get_cward_bb_idx(cIdx, iChain)
     var xIdx = bIdx
+
    var caPhiIdx = get_cward_bb_idx(nIdx, iChain)
     if ({atomIndex=bIdx}.atomName=="O") {
+
     var caPsiIdx = get_nward_bb_idx(cIdx, iChain)
        oIdx = bIdx
+
 
        xIdx = aIdx
+
     if (nWard) {
 +
        var nNo = {(chain=iChain) and thisModel}.atomno.min
 +
        select_nward_idx(caPsiIdx, {(atomno=nNo) and (chain=iChain)
 +
            and thisModel}.atomIndex)
 
     }
 
     }
     var cIdx = getScBBidx(oIdx)
+
    else {
     var nIdx = getCwardBBidx(cIdx)
+
        var cNo = {(chain=iChain) and thisModel}.atomno.max
     var caPhiIdx = getCwardBBidx(nIdx)
+
        select_cward_idx(caPhiIdx, {(atomno=cNo) and (chain=iChain)
     var caPsiIdx = getNwardBBidx(cIdx)
+
            and thisModel}.atomIndex)
   
+
    }
     var nWard = ({atomIndex=oIdx}.atomno < {atomIndex=targetIdx}.atomno)
+
 
     if (nWard) {
+
    # Counter-rotate
         selectCward(cIdx, targetIdx)
+
    do_counter_rotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, not nWard)
 +
    select selsave
 +
}
 +
 
 +
function counter_rotate_2(oIdx, toPt, terminalIdx, oDrag) {
 +
 
 +
    var iChain = {atomIndex=oIdx}.chain
 +
    var selsave = {selected}
 +
    var gOk2 = true
 +
     var cIdx = get_sc_bb_idx(oIdx, iChain)
 +
     var nIdx = get_cward_bb_idx(cIdx, iChain)
 +
     var caPhiIdx = get_cward_bb_idx(nIdx, iChain)
 +
     var caPsiIdx = get_nward_bb_idx(cIdx, iChain)
 +
 
 +
     var nTward = ({atomIndex=oIdx}.atomno < {atomIndex=terminalIdx}.atomno)
 +
     if (nTward) {
 +
         select_cward_idx(cIdx, terminalIdx)
 
     }
 
     }
 
     else {
 
     else {
         selectNward(nIdx, targetIdx)
+
         select_nward_idx(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)
+
         do_counter_rotate(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)
+
             do_counter_rotate(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 549:
 
                 continue
 
                 continue
 
             }
 
             }
 +
        }
 +
 +
        if (oDrag) {
 +
            break
 
         }
 
         }
  
Line 723: Line 558:
 
         tcount++
 
         tcount++
 
         if (tcount > (360/abs(dir))) {
 
         if (tcount > (360/abs(dir))) {
             gOk2 = FALSE
+
             gOk2 = false
 
             break
 
             break
 
         }
 
         }
       
+
 
 
     } # endwhile
 
     } # endwhile
     select selsave  
+
     select selsave
 
}
 
}
  
 
# Repair proline
 
# Repair proline
function repairProline(idx) {
+
function repair_proline(BBidx) {
     var cbidx = getCBidx(idx)
+
     var cbidx = get_cb_idx(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)
     var cdidx = {(atomno=@{cbno+2}) and (chain=gChain)}.atomIndex
+
        and thisModel}.atomIndex
     var caidx = {(atomno=@{cbno-3}) and (chain=gChain)}.atomIndex
+
     var cdidx = {(atomno=@{cbno+2}) and (chain=gChain)
     var nidx = {(atomno=@{cbno-4}) and (chain=gChain)}.atomIndex
+
        and thisModel}.atomIndex
   
+
     var caidx = {(atomno=@{cbno-3}) and (chain=gChain)
 +
        and thisModel}.atomIndex
 +
     var nidx = {(atomno=@{cbno-4}) and (chain=gChain)
 +
        and thisModel}.atomIndex
 +
 
 
     select {atomIndex=cbidx}
 
     select {atomIndex=cbidx}
     setAngleIdx(nidx, caidx, cbidx, 109.5)
+
     set_angle_idx(nidx, caidx, cbidx, 109.5)
   
+
 
 
     select {atomIndex=cdidx}
 
     select {atomIndex=cdidx}
     setDistanceIdx(nidx, cdidx, 1.47)
+
     set_distance_idx(nidx, cdidx, 1.47)
     setAngleIdx(caidx, nidx, cdidx, 102.7)
+
     set_angle_idx(caidx, nidx, cdidx, 102.7)
     setDihedralIdx(cbidx, caidx, nidx, cdidx, 16.2)
+
     set_dihedral_idx(cbidx, caidx, nidx, cdidx, 16.2)
   
+
 
 
     select {atomIndex=cgidx}
 
     select {atomIndex=cgidx}
     setDistanceIdx(cdidx, cgidx, 1.51)
+
     set_distance_idx(cdidx, cgidx, 1.51)
     setAngleIdx(nidx, cdidx, cgidx, 106.4)
+
     set_angle_idx(nidx, cdidx, cgidx, 106.4)
     setDihedralIdx(caidx, nidx, cdidx, cgidx, 16.2)
+
     set_dihedral_idx(caidx, nidx, cdidx, cgidx, 16.2)
 
}
 
}
  
 
# Repair side chain
 
# Repair side chain
function plicoRepairSideChain(targetIdx, nWard) {
+
function repair_sc(targetIdx, nWard) {
  
     var idx = (nWard ? getCwardBBidx(targetIdx) : getNwardBBidx(targetIdx))
+
     var idx = (nWard ? get_cward_bb_idx(targetIdx, gChain) : get_nward_bb_idx(targetIdx, gChain))
  
 
     if (({atomIndex=targetIdx}.atomName == "CA")
 
     if (({atomIndex=targetIdx}.atomName == "CA")
 
         and ({atomIndex=targetIdx}.group != "GLY")) {
 
         and ({atomIndex=targetIdx}.group != "GLY")) {
         var cbidx = getCBidx(targetIdx)
+
         var cbidx = get_cb_idx(targetIdx)
 
         select none
 
         select none
         selectAddSideChain(cbidx)
+
         select_add_sc(cbidx)
         setAngleIdx(idx, targetIdx, cbidx, 110.0)
+
         set_angle_idx(idx, targetIdx, cbidx, 110.0)
         setDistanceIdx(targetIdx, cbidx, 1.5)
+
         set_distance_idx(targetIdx, cbidx, 1.5)
 
         if ({atomIndex=targetIdx}.group != "PRO") {
 
         if ({atomIndex=targetIdx}.group != "PRO") {
             var colliders = (within(kCtolerance, FALSE, {selected})
+
             var colliders = (within(kCtolerance, false, {selected})
 
                 and not {atomIndex=targetIdx} and not {selected})
 
                 and not {atomIndex=targetIdx} and not {selected})
             if (colliders.size > 0) {
+
             if (colliders) {
 
                 if ({atomIndex=targetIdx}.group != "ALA") {
 
                 if ({atomIndex=targetIdx}.group != "ALA") {
                     fixSCcollision2(cbidx)
+
                     fix_sc_collision_2(cbidx)
 
                 }
 
                 }
 
             }
 
             }
Line 779: Line 618:
 
             }
 
             }
 
             else {
 
             else {
                 setDihedralIdx(getNwardBBidx(idx), idx, targetIdx, cbidx, 174.2)
+
                 set_dihedral_idx(get_nward_bb_idx(idx, gChain), idx, targetIdx, cbidx, 174.2)
 
             }
 
             }
 
         }
 
         }
 
     }
 
     }
   
+
 
 
     else if ({atomIndex=targetIdx}.atomName == "C") {
 
     else if ({atomIndex=targetIdx}.atomName == "C") {
         var oidx = getOidx(targetIdx)
+
         var oidx = get_o_idx(targetIdx)
 
         select {atomIndex=oidx}
 
         select {atomIndex=oidx}
         setAngleIdx(idx, targetIdx, oidx, 120.0)
+
         set_angle_idx(idx, targetIdx, oidx, 120.0)
         setDistanceIdx(targetIdx, oidx, 1.21)
+
         set_distance_idx(targetIdx, oidx, 1.21)
 
         if (nWard) {
 
         if (nWard) {
             setDihedralIdx(getCwardBBidx(idx), idx, targetIdx, oidx, 0.0)
+
             set_dihedral_idx(get_cward_bb_idx(idx, gChain), idx, targetIdx, oidx, 0.0)
 
         }
 
         }
 
         if ({atomIndex=idx}.group == "PRO") {
 
         if ({atomIndex=idx}.group == "PRO") {
             repairProline(idx)
+
             repair_proline(idx)
 
             var dNo = {atomIndex=targetIdx}.atomno + 4
 
             var dNo = {atomIndex=targetIdx}.atomno + 4
             var dIdx = {(atomno=dNO) and (chain=gChain)}.atomIndex
+
             var dIdx = {(atomno=dNO) and (chain=gChain)
             var colliders = (within(kCtolerance, FALSE, {atomIndex=dIdx})
+
                and thisModel}.atomIndex
 +
             var colliders = (within(kCtolerance, false, {atomIndex=dIdx})
 
                 and not connected({atomIndex=dIdx})
 
                 and not connected({atomIndex=dIdx})
 
                 and not {atomIndex=dIdx})
 
                 and not {atomIndex=dIdx})
 
             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)
+
                     counter_rotate_2(colliders[i].atomIndex,
 +
                        {atomIndex=dIdx}.xyz, targetIdx, false)
 
                 }
 
                 }
 
             }
 
             }
Line 809: Line 650:
  
 
# Rebuild Cward rotors set
 
# Rebuild Cward rotors set
function tugTrackC() {
+
function tug_track_c() {
  
 
     # For all bb atoms cWard of cargo
 
     # For all bb atoms cWard of cargo
 
     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 != gNanchorIdx) {
   
+
 
 
         # Step to next atom
 
         # Step to next atom
         targetIdx = getCwardBBidx(targetIdx)
+
         targetIdx = get_cward_bb_idx(targetIdx, gChain)
          
+
         if (targetIdx < 0) {
 +
            break
 +
        }
 +
 
 
         # 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 672:
 
         }
 
         }
 
         tcount++
 
         tcount++
       
+
 
 
         # Compute targets desired coords
 
         # Compute targets desired coords
         var c1idx = getCwardBBidx(targetIdx)
+
         try {
        var n1idx = getNwardBBidx(targetIdx)
+
            var c1idx = get_cward_bb_idx(targetIdx, gChain)
        var n2idx = getNwardBBidx(n1Idx)
+
            var n1idx = get_nward_bb_idx(targetIdx, gChain )
        var n3idx = getNwardBBidx(n2Idx)
+
            var n2idx = get_nward_bb_idx(n1Idx, gChain)
        var pt = {0 0 0}
+
            var n3idx = get_nward_bb_idx(n2Idx, gChain)
        if ({atomIndex=targetIdx}.atomName == "N") {
+
            var pt = {0 0 0}
            var oidx = getOidx(n1idx)
+
            if ({atomIndex=targetIdx}.atomName == "N") {
            select {atomIndex=oidx}
+
                var oidx = get_o_idx(n1idx)
           
+
                select {atomIndex=oidx}
            # Desired target location is trigonal O      
+
   
            setDistanceIdx(n1idx, oidx, 1.5)
+
                # Desired target location is trigonal O
            pt = getTrigonal(n2idx, n1idx, oidx, 1.37)
+
                set_distance_idx(n1idx, oidx, 1.5)
            setDistanceIdx(n1idx, oidx, 1.21)
+
                pt = get_trigonal_idx(n2idx, n1idx, oidx, 1.37)
        }
+
                set_distance_idx(n1idx, oidx, 1.21)
        else if (({atomIndex=targetIdx}.atomName == "C")
+
            }
            and ({atomIndex=targetIdx}.group != "GLY")) {
+
            else if (({atomIndex=targetIdx}.atomName == "C")
           
+
                and ({atomIndex=targetIdx}.group != "GLY")) {
            # Desired target location is tetragonal CB
+
   
            var cbidx = getCBidx(n1idx)
+
                # Desired target location is tetragonal CB
            pt = getTet(n2idx, n1idx, cbidx, 1.5)
+
                var cbidx = get_cb_idx(n1idx)
        }
+
                pt = get_tet_idx(n2idx, n1idx, cbidx, 1.5)
        else { # CA (or GLY C)
+
            }
           
+
            else { # CA (or GLY C)
            # Save current target coords
+
   
            var cp = {atomIndex=targetIdx}.xyz
+
                # Save current target coords
 +
                var cp = {atomIndex=targetIdx}.xyz
 +
   
 +
                # Set target atom at desired distance and angle
 +
                select {atomIndex=targetIdx}
 +
                set_distance_idx(n1idx, targetIdx, 1.5)
 +
                set_angle_idx(n2idx, n1idx, targetIdx, 120.0)
 +
                if ({atomIndex=targetIdx}.atomName == "CA") {
 +
                    set_dihedral_idx(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 (not gOK)) {
 
      
 
      
            # Set target atom at desired distance and angle
+
                    # Rotate on cWard rotor set to move it there
            select {atomIndex=targetIdx}
+
                    tug_track_idx(targetIdx, pt, false, false)
            setDistanceIdx(n1idx, targetIdx, 1.5)
+
                    xcount++
             setAngleIdx(n2idx, n1idx, targetIdx, 110.0)
+
                }
             if ({atomIndex=targetIdx}.atomName == "CA") {
+
             }
                 setDihedralIdx(n3idx, n2idx, n1idx, targetIdx, 180)
+
             else {
 +
                gOK = true
 +
                 okCount++
 
             }
 
             }
           
+
        }
            # Record and restore target
+
        catch {
            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 successful
         if (gOK == TRUE) {
+
         if (gOK == true) {
       
+
 
 
             # Adust any side atoms
 
             # Adust any side atoms
             plicoRepairSideChain(targetIdx, FALSE)
+
             repair_sc(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 908: Line 755:
  
 
# Rebuild Nward rotors set
 
# Rebuild Nward rotors set
function tugTrackN() {
+
function tug_track_n() {
 +
 
 +
    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 = get_nward_bb_idx(targetIdx, gChain)
 +
        if (targetIdx < 0) {
 +
            break
 +
        }
 
          
 
          
 
         # No collision with cargo allowed after two atoms placed
 
         # No collision with cargo allowed after two atoms placed
Line 929: Line 779:
 
         }
 
         }
 
         tcount++
 
         tcount++
       
+
 
 
         # Compute targets desired coords
 
         # Compute targets desired coords
         var n1idx = getNwardBBidx(targetIdx)
+
         var n1idx = get_nward_bb_idx(targetIdx, gChain)
         var c1idx = getCwardBBidx(targetIdx)
+
         var c1idx = get_cward_bb_idx(targetIdx, gChain)
         var c2idx = getCwardBBidx(c1idx)
+
         var c2idx = get_cward_bb_idx(c1idx, gChain)
         var c3idx = getCwardBBidx(c2idx)
+
         var c3idx = get_cward_bb_idx(c2idx, gChain)
 
         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 = get_o_idx(c1idx)
 
             select {atomIndex=oidx}
 
             select {atomIndex=oidx}
             setDistanceIdx(c1idx, oidx, 1.39)
+
             set_distance_idx(c1idx, oidx, 1.39)
             pt = getTrigonal(c2idx, c1idx, oidx, 1.41)
+
             pt = get_trigonal_idx(c2idx, c1idx, oidx, 1.41)
             setDistanceIdx(c1idx, oidx, 1.21)
+
             set_distance_idx(c1idx, oidx, 1.21)
 
         }
 
         }
 
         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 = get_cb_idx(c1idx)
             pt = getTet(cbidx, c1idx, c2idx, 1.5)
+
             pt = get_tet_idx(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)
+
             set_distance_idx(c1idx, targetIdx, 1.37)
             setAngleIdx(c2idx, c1idx, targetIdx, 110.0)
+
             set_angle_idx(c2idx, c1idx, targetIdx, 110.0)
               
+
 
 
             if ({atomIndex=targetIdx}.group == "PRO") {
 
             if ({atomIndex=targetIdx}.group == "PRO") {
                 setDihedralIdx(c3idx, c2idx, c1idx, targetIdx, -57.0)
+
                 set_dihedral_idx(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
 
             gTargetPt = pt
 
             gTargetPt = pt
 
             var xcount = 0
 
             var xcount = 0
             gOK = FALSE
+
             gOK = false
             while ((xcount < 20) and (gOK == FALSE)) {
+
             while ((xcount < 20) and (not gOK)) {
           
+
 
                 # Rotate on cWard rotor set to move it there
+
                 # Rotate on nWard rotor set to move it there
                 tugTrackIdx(targetIdx, pt, TRUE, FALSE)
+
                 tug_track_idx(targetIdx, pt, true, false)
                    #(distance(pt, {atomIndex=targetIdx}) > kMtolerance))
 
 
                 xcount++
 
                 xcount++
 
             }
 
             }
 
         }
 
         }
 
         else {
 
         else {
             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)
+
             repair_sc(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) {
 
 
}
 
}
  
 
# gPlicoRecord is maintained by the macro pilcoRecord
 
# gPlicoRecord is maintained by the macro pilcoRecord
function plicoRecord(s) {
+
function translate_selected_record(pt) {
    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 != "") {
 
     if (gPlicoRecord != "") {
         plicoRecord(format("select %s;translateSelected %s;", {selected}, pt))
+
         plico_record(format("select %s;translateSelected %s;", {selected}, pt))
 
     }
 
     }
 
     translateSelected @pt
 
     translateSelected @pt
Line 1,031: Line 868:
  
 
# gPlicoRecord is maintained by the macro pilcoRecord
 
# gPlicoRecord is maintained by the macro pilcoRecord
function rotateSelectedRecord(g1pivotIdx, axis, a) {
+
function rotate_selected_record(pivotIdx, caxis, a) {
 
     if (gPlicoRecord != "") {
 
     if (gPlicoRecord != "") {
         plicoRecord(format("select %s;", {selected}))
+
         plico_record(format("select %s;", {selected}))
         plicoRecord(format("rotateSelected {atomIndex=%d} @%s @%s;",
+
         plico_record(format("rotateSelected {atomIndex=%d} @%s @%s;",
             g1pivotIdx, axis, a))
+
             pivotIdx, caxis, a))
 
     }
 
     }
     rotateSelected {atomIndex=g1pivotIdx} @axis @a
+
     rotateSelected {atomIndex=pivotIdx} @caxis @a
 
}
 
}
  
function collectSCrotors(no) {
+
function collect_sc_rotors(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)
 +
            and thisModel}.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)
 +
                and thisModel}.group == "TYR") {
 
                 break
 
                 break
 
             }
 
             }
 
         case "CE" :
 
         case "CE" :
             if ({(atomno=iNo) and (chain=gChain)}.group == "MET") {
+
             if ({(atomno=iNo) and (chain=iChain)
 +
                and thisModel}.group == "MET") {
 
                 break
 
                 break
 
             }
 
             }
 
         case "CG1" :
 
         case "CG1" :
             if ({(atomno=iNo) and (chain=gChain)}.group == "VAL") {
+
             if ({(atomno=iNo) and (chain=iChain)
 +
                and thisModel}.group == "VAL") {
 
                 break
 
                 break
 
             }
 
             }
             if ({(atomno=iNo) and (chain=gChain)}.group == "ILE") {
+
             if ({(atomno=iNo) and (chain=iChain)
 +
                and thisModel}.group == "ILE") {
 
                 ile = 1
 
                 ile = 1
 
             }
 
             }
Line 1,068: Line 909:
 
         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)
             scBondIdxs += {(atomno=@{iNo+0}) and (chain=gChain)}.atomIndex
+
                and thisModel}.atomIndex
             if ({(atomno=iNo) and (chain=gChain)}.atomName%2 == "CG") {
+
             scBondIdxs += {(atomno=@{iNo+0}) and (chain=iChain)
                 scBondIdxs += {(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex
+
                and thisModel}.atomIndex
                 scBondIdxs += {(atomno=@{iNo-4}) and (chain=gChain)}.atomIndex
+
             if ({(atomno=iNo) and (chain=iChain)
 +
                and thisModel}.atomName%2 == "CG") {
 +
                 scBondIdxs += {(atomno=@{iNo-1}) and (chain=iChain)
 +
                    and thisModel}.atomIndex
 +
                 scBondIdxs += {(atomno=@{iNo-4}) and (chain=iChain)
 +
                    and thisModel}.atomIndex
 
             }
 
             }
             else if ({(atomno=iNo) and (chain=gChain)}.atomName == "CB") {
+
             else if ({(atomno=iNo) and (chain=iChain)
                 scBondIdxs += {(atomno=@{iNo-3}) and (chain=gChain)}.atomIndex
+
                and thisModel}.atomName == "CB") {
                 scBondIdxs += {(atomno=@{iNo-4}) and (chain=gChain)}.atomIndex
+
                 scBondIdxs += {(atomno=@{iNo-3}) and (chain=iChain)
 +
                    and thisModel}.atomIndex
 +
                 scBondIdxs += {(atomno=@{iNo-4}) and (chain=iChain)
 +
                    and thisModel}.atomIndex
 
             }
 
             }
 
             else {
 
             else {
                 scBondIdxs += {(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex
+
                 scBondIdxs += {(atomno=@{iNo-1}) and (chain=iChain)
                 scBondIdxs += {(atomno=@{iNo-2}) and (chain=gChain)}.atomIndex
+
                    and thisModel}.atomIndex
 +
                 scBondIdxs += {(atomno=@{iNo-2}) and (chain=iChain)
 +
                    and thisModel}.atomIndex
 
             }
 
             }
 
             break
 
             break
 
         }
 
         }
   
+
 
 
     }
 
     }
   
+
 
     return scBondIdxs  
+
     return scBondIdxs
 
}
 
}
  
 
# Drag Side Chain
 
# Drag Side Chain
function dragSC() {
+
function drag_sc() {
 
 
 
     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 = collect_sc_rotors( 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)
 +
        var scSet = get_sc_set(gSCidx, iChain)
 +
 
         # For all rotor combinations
 
         # For all rotor combinations
 
         var dh = array()
 
         var dh = array()
Line 1,112: Line 964:
 
             for (var j = 0; j < 6; j++) {
 
             for (var j = 0; j < 6; j++) {
 
                 rot += 60*j
 
                 rot += 60*j
                 selectAddSideChain(scBondIdxs[1+(4*i)])
+
                 select_add_sc(scBondIdxs[1+(4*i)])
                 setDihedralIdx(scBondIdxs[4+(4*i)], scBondIdxs[3+(4*i)],
+
                 set_dihedral_idx(scBondIdxs[4+(4*i)], scBondIdxs[3+(4*i)],
 
                     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)
                  
+
                 if (gSCcheck) {
 +
                    var colliders = (within(kCtolerance, false, scSet)
 +
                        and not connected(scSet) and not {scSet})
 +
                    if (colliders) {
 +
                        continue
 +
                    }
 +
                }
 +
 
 
                 # Find the best
 
                 # Find the best
 
                 if (newDist < dist) {
 
                 if (newDist < dist) {
Line 1,129: Line 988:
 
             }
 
             }
 
         }
 
         }
       
+
 
 
         # Now set the best
 
         # Now set the best
 
         for (var i = 0; i < numChi; i++) {
 
         for (var i = 0; i < numChi; i++) {
             selectAddSideChain(scBondIdxs[1+(4*i)])
+
             select_add_sc(scBondIdxs[1+(4*i)])
             setDihedralIdx(scBondIdxs[4+(4*i)], scBondIdxs[3+(4*i)],
+
             set_dihedral_idx(scBondIdxs[4+(4*i)], scBondIdxs[3+(4*i)],
 
                 scBondIdxs[2+(4*i)], scBondIdxs[1+(4*i)], dh[i+1])
 
                 scBondIdxs[2+(4*i)], scBondIdxs[1+(4*i)], dh[i+1])
 +
        }
 +
       
 +
        if (gSCcheck = false) {
 +
            plico_minimize( scSet)
 
         }
 
         }
 
     }
 
     }
 
     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)
         var icb = {(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex
+
            and thisModel}.atomIndex
         var ica = {(atomno=@{iNo-4}) and (chain=gChain)}.atomIndex
+
         var icb = {(atomno=@{iNo-1}) and (chain=iChain)
         var in = {(atomno=@{iNo-5}) and (chain=gChain)}.atomIndex
+
            and thisModel}.atomIndex
 +
         var ica = {(atomno=@{iNo-4}) and (chain=iChain)
 +
            and thisModel}.atomIndex
 +
         var in = {(atomno=@{iNo-5}) and (chain=iChain)
 +
            and thisModel}.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) {
             setDihedralIdx(ica, in, icd, gSCidx, 8.7)
+
             set_dihedral_idx(ica, in, icd, gSCidx, 8.7)
             setAngleIdx(in, icd, gSCidx, 110.0)
+
             set_angle_idx(in, icd, gSCidx, 110.0)
             setDistanceIdx(icd, gSCidx, 1.5)
+
             set_distance_idx(icd, gSCidx, 1.5)
 
         }
 
         }
 
         else {
 
         else {
             setDihedralIdx(ica, in, icd, gSCidx, -29.5)
+
             set_dihedral_idx(ica, in, icd, gSCidx, -29.5)
             setAngleIdx(in, icd, gSCidx, 108.8)
+
             set_angle_idx(in, icd, gSCidx, 108.8)
             setDistanceIdx(icd, gSCidx, 1.5)
+
             set_distance_idx(icd, gSCidx, 1.5)
 
         }
 
         }
 
     }
 
     }
   
+
 
 
     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 fix_sc_collision_2(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)
 +
        and thisModel}.resno
 +
 
 
     # Get SC terminus
 
     # Get SC terminus
     while (resno == {(atomno=iNo) and (chain=gChain)}.resno) {
+
     while (resno == {(atomno=iNo) and (chain=iChain)
 +
        and thisModel}.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)
         sc += {(atomno=iBno) and (chain=gChain)}
+
        and thisModel}.atomName != "CB") {
 +
         sc += {(atomno=iBno) and (chain=iChain)
 +
            and thisModel}
 
         iBno--
 
         iBno--
 
     }
 
     }
     var cbidx = {(atomno=iBno) and (chain=gChain)}.atomIndex
+
     var cbidx = {(atomno=iBno) and (chain=iChain)
   
+
        and thisModel}.atomIndex
     var scBondIdxs = collectSCrotors( iNo)
+
 
 +
     var scBondIdxs = collect_sc_rotors( 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,188: Line 1,062:
 
         for (var j = 0; j < 6; j++) {
 
         for (var j = 0; j < 6; j++) {
 
             rot += 60
 
             rot += 60
             selectAddSideChain(scBondIdxs[1+(4*i)])
+
             select_add_sc(scBondIdxs[1+(4*i)])
             #setDihedralIdx(scBondIdxs[4+(4*i)], scBondIdxs[3+(4*i)],
+
             set_dihedral_idx(scBondIdxs[1+(4*i)], scBondIdxs[2+(4*i)],
            #    scBondIdxs[2+(4*i)], scBondIdxs[1+(4*i)], rot)
 
            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})
+
             var 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,077:
 
                 }
 
                 }
 
             }
 
             }
               
+
 
 
             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
 
 
             }
 
             }
           
+
 
 
         }
 
         }
 
     }
 
     }
 
}
 
}
  
function isMovableSideChain(aIdx) {
+
function is_moveable_sc(aIdx) {
  
 
     var ret = (({atomIndex=aIdx}.group != "PRO")
 
     var ret = (({atomIndex=aIdx}.group != "PRO")
Line 1,226: Line 1,098:
 
     case "O":
 
     case "O":
 
     case "O4\'":
 
     case "O4\'":
         ret = FALSE
+
         ret = false
 
         break
 
         break
 
     }
 
     }
Line 1,232: Line 1,104:
 
}
 
}
  
# gFreeze is maintained by the script plicoFreeze that allows the user
+
function is_rotor_avail(i1idx, i2idx) {
# to inhibit rotation on selected rotors
+
    var ret = true
function isRotorAvailable(idx) {
+
    if (i1idx > i2idx) { # frozen bonds are kept as lo-hi
     return (gFreeze.find(idx) == 0)
+
        var idx = @i1idx
 +
        i1idx = @i2idx
 +
        i2idx = @idx
 +
    }
 +
 
 +
     for (var i = 1; i <= gFreeze.size; i += 2) {
 +
        if ((gFreeze[i] == i1idx) and (gFreeze[i+1] == i2idx)) {
 +
            ret = false
 +
            break
 +
        }
 +
    }
 +
 
 +
    return ret
 
}
 
}
  
function collectBBrotors(nWard) {
+
function xcollect_bb_rotors(nWard) {
 
     var anchorNo = (nWard
 
     var anchorNo = (nWard
 
         ? ((gNanchorIdx >= 0) ? {atomIndex=gNanchorIdx}.atomno : gMinNo)
 
         ? ((gNanchorIdx >= 0) ? {atomIndex=gNanchorIdx}.atomno : gMinNo)
Line 1,248: Line 1,132:
 
     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)
                 if (isRotorAvailable(iNo)) {
+
                and thisModel}.atomName == "CA") {
                     if (({(atomno=iNo) and (chain=gChain)}.group != "PRO") and (iNo > cargoNo)) { # phi
+
                 if (is_rotor_avail(iNo)) {# xxx
                         rotors += [{(atomno=@{getCmNo(iNo-1)}) and (chain=gChain)}.atomIndex,
+
                     if (({(atomno=iNo) and (chain=gChain)
                             {(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex]
+
                        and thisModel}.group != "PRO") and (iNo > cargoNo)) { # phi
                         rotors += [{(atomno=@{iNo}) and (chain=gChain)}.atomIndex,
+
                         rotors += [{(atomno=@{get_cm_no(iNo-1)}) and (chain=gChain)
                             {(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex]
+
                            and thisModel}.atomIndex,
 +
                             {(atomno=@{iNo-1}) and (chain=gChain)
 +
                                and thisModel}.atomIndex]
 +
                         rotors += [{(atomno=@{iNo}) and (chain=gChain)
 +
                            and thisModel}.atomIndex,
 +
                             {(atomno=@{iNo+1}) and (chain=gChain)
 +
                                and thisModel}.atomIndex]
 
                     }
 
                     }
 
                     if (iNo != (anchorNo-1)) { # psi
 
                     if (iNo != (anchorNo-1)) { # psi
                         rotors += [{(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex,
+
                         rotors += [{(atomno=@{iNo-1}) and (chain=gChain)
                             {(atomno=@{iNo}) and (chain=gChain)}.atomIndex]
+
                            and thisModel}.atomIndex,
                         rotors += [{(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex,
+
                             {(atomno=@{iNo}) and (chain=gChain)
                             {(atomno=@{getNpNo(iNo+2)}) and (chain=gChain)}.atomIndex]
+
                                and thisModel}.atomIndex]
 +
                         rotors += [{(atomno=@{iNo+1}) and (chain=gChain)
 +
                            and thisModel}.atomIndex,
 +
                             {(atomno=@{get_np_no(iNo+2)}) and (chain=gChain)
 +
                                and thisModel}.atomIndex]
 
                     }
 
                     }
 
                 }
 
                 }
Line 1,274: Line 1,163:
 
     }
 
     }
 
     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)
                 if (isRotorAvailable(iNo)) {
+
                and thisModel}.atomName == "CA") {
 +
                 if (is_rotor_avail(iNo)) {
 
                     if ((iNo != (anchorNo-1)) and (iNo < cargoNo)) { # psi
 
                     if ((iNo != (anchorNo-1)) and (iNo < cargoNo)) { # psi
                         rotors += [{(atomno=@{getNpNo(iNo+2)}) and (chain=gChain)}.atomIndex,
+
                         rotors += [{(atomno=@{get_np_no(iNo+2)}) and (chain=gChain)
                             {(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex]
+
                            and thisModel}.atomIndex,
                         rotors += [{(atomno=@{iNo}) and (chain=gChain)}.atomIndex,
+
                             {(atomno=@{iNo+1}) and (chain=gChain)
                             {(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex]
+
                                and thisModel}.atomIndex]
 +
                         rotors += [{(atomno=@{iNo}) and (chain=gChain)
 +
                            and thisModel}.atomIndex,
 +
                             {(atomno=@{iNo-1}) and (chain=gChain)
 +
                                and thisModel}.atomIndex]
 
                     }
 
                     }
                     if ({(atomno=iNo) and (chain=gChain)}.group != "PRO") { # phi
+
                     if ({(atomno=iNo) and (chain=gChain)
                         rotors += [{(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex,
+
                        and thisModel}.group != "PRO") { # phi
                             {(atomno=@{iNo}) and (chain=gChain)}.atomIndex]
+
                         rotors += [{(atomno=@{iNo+1}) and (chain=gChain)
                         rotors += [{(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex,
+
                            and thisModel}.atomIndex,
                             {(atomno=@{getCmNo(iNo-1)}) and (chain=gChain)}.atomIndex]
+
                             {(atomno=@{iNo}) and (chain=gChain)
 +
                                and thisModel}.atomIndex]
 +
                         rotors += [{(atomno=@{iNo-1}) and (chain=gChain)
 +
                            and thisModel}.atomIndex,
 +
                             {(atomno=@{get_cm_no(iNo-1)}) and (chain=gChain)
 +
                                and thisModel}.atomIndex]
 
                     }
 
                     }
 
                 }
 
                 }
Line 1,299: Line 1,193:
 
         }
 
         }
 
     }
 
     }
   
+
 
 
     if (nWard) {
 
     if (nWard) {
 
         gNrotors = rotors
 
         gNrotors = rotors
         if (FALSE) {#DEBUG
+
    }
            print ("gNrotors atomnos")#DEBUG
+
    else {
            for (var i = 1; i < gNrotors.size; i += 4) {#DEBUG
+
         gCrotors = rotors
                print format("%d %d %s", {atomIndex=@{gNrotors[i]}}.atomno,
+
    }
                    {atomIndex=@{gNrotors[i+1]}}.atomno, format("%d %d",
+
}
                    {atomIndex=@{gNrotors[i+2]}}.atomno,
+
function collect_bb_rotors(nWard) {
                    {atomIndex=@{gNrotors[i+3]}}.atomno))
+
    if (nWard) {
 +
        gNrotors = array()
 +
        var nres = {atomIndex=gNcargoIdx}.resno
 +
        var nname = {atomIndex=gNcargoIdx}.atomName
 +
        var ares = get_resno_min(gChain)
 +
        var aA = get_atom_rcn( ares, gChain, "N")
 +
        if (gNanchorIdx >= 0) {
 +
            aA = {atomIndex=gNanchorIdx}
 +
        }
 +
        var aname = aA.atomName
 +
        for (var r = nres; r >= ares; r--) {
 +
            var aCp = get_atom_rcn( r-1, gChain, "C")
 +
            var aN = get_atom_rcn( r, gChain, "N")
 +
            var aCa = get_atom_rcn( r, gChain, "CA")
 +
            var aC = get_atom_rcn( r, gChain, "C")
 +
            var aNn = get_atom_rcn( r+1, gChain, "N")
 +
            if (aCp.size < 1) {
 +
                aCp = aNn
 
             }
 
             }
         }#DEBUG
+
            # psi
 +
            if (((r < nres) and (r > ares))
 +
                or ((r == nres) and (nname == "C"))
 +
                or ((r == ares) and (aname != "C"))) {
 +
                if (is_rotor_avail(aCa.atomIndex, aC.atomIndex)) {
 +
                    gNrotors += aNn.atomIndex
 +
                    gNrotors += aC.atomIndex
 +
                    gNrotors += aCa.atomIndex
 +
                    gNrotors += aN.atomIndex
 +
                }
 +
            }
 +
                       
 +
            # phi
 +
            if (aCa.group != "PRO") {
 +
                if (((r <= nres) and (r > ares))
 +
                    or ((r == ares) and (aname == "N"))) {
 +
                    if (is_rotor_avail(aCa.atomIndex, aN.atomIndex)) {
 +
                        gNrotors += aC.atomIndex
 +
                        gNrotors += aCa.atomIndex
 +
                        gNrotors += aN.atomIndex
 +
                        gNrotors += aCp.atomIndex
 +
                    }
 +
                }
 +
            }
 +
         } # endfor
 
     }
 
     }
 
     else {
 
     else {
         gCrotors = rotors
+
         gCrotors = array()
         if (FALSE) {#DEBUG
+
        var cres = {atomIndex=gCcargoIdx}.resno
             print ("gCrotors atomnos")#DEBUG
+
        var cname = {atomIndex=gCcargoIdx}.atomName
             for (var i = 1; i < gCrotors.size; i += 4) {#DEBUG
+
        var ares = get_resno_max(gChain)
                 print format("%d %d %s", {atomIndex=@{gCrotors[i]}}.atomno,
+
        var aA = get_atom_rcn( ares, gChain, "C")
                    {atomIndex=@{gCrotors[i+1]}}.atomno, format("%d %d",
+
         if (gCanchorIdx >= 0) {
                    {atomIndex=@{gCrotors[i+2]}}.atomno,
+
             aA = {atomIndex=gCanchorIdx}
                     {atomIndex=@{gCrotors[i+3]}}.atomno))
+
        }
 +
        var aname = aA.atomName
 +
        for (var r = cres; r <= ares; r++) {
 +
            var aCp = get_atom_rcn( r-1, gChain, "C")
 +
            var aN = get_atom_rcn( r, gChain, "N")
 +
             var aCa = get_atom_rcn( r, gChain, "CA")
 +
            var aC = get_atom_rcn( r, gChain, "C")
 +
            var aNn = get_atom_rcn( r+1, gChain, "N")
 +
            if (aNn.size < 1) {
 +
                aNn = aCp
 +
            }
 +
           
 +
            # phi
 +
            if (aCa.group != "PRO") {
 +
                 if (((r > cres) and (r < ares))
 +
                    or ((r == cres) and (aname == "N"))
 +
                    or ((r == ares) and (aname != "N"))) {
 +
                    if (is_rotor_avail(aN.atomIndex, aCa.atomIndex)) {
 +
                        gCrotors += aCp.atomIndex
 +
                        gCrotors += aN.atomIndex
 +
                        gCrotors += aCa.atomIndex
 +
                        gCrotors += aC.atomIndex
 +
                    }
 +
                }
 +
            }
 +
           
 +
            # psi
 +
            if (((r >= cres) and (r < ares))
 +
                or ((r == cres) and (aname != "C"))
 +
                or ((r == ares) and (aname == "C"))) {
 +
                if (is_rotor_avail(aCa.atomIndex, aC.atomIndex)) {
 +
                    gCrotors += aN.atomIndex
 +
                     gCrotors += aCa.atomIndex
 +
                    gCrotors += aC.atomIndex
 +
                    gCrotors += aNn.atomIndex
 +
                }
 
             }
 
             }
         }#DEBUG
+
         } # endfor
 
     }
 
     }
 
}
 
}
  
function collectRotors() {
+
function collect_rotors() {
     collectBBrotors(FALSE)
+
     collect_bb_rotors(false)
     collectBBrotors(TRUE)
+
     collect_bb_rotors(true)
 
}
 
}
  
function tugSideChain(pt) {
+
function tug_sc(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,314:
 
     }
 
     }
 
     gSCpt += pt
 
     gSCpt += pt
     draw arrow {atomIndex=gSCidx} @gSCpt  
+
     draw arrow {atomIndex=gSCidx} @gSCpt
 
}
 
}
  
function setColors() {
+
function set_colors() {
     select all
+
     select (thisModel)
 
     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)}
+
    color {gCargoSet and oxygen} pink
 +
     select {(atomIndex=gCcargoIdx) or (atomIndex=gNcargoIdx)
 +
        or (atomIndex=gCanchorIdx) or (atomIndex=gNanchorIdx)}
 
     halo on
 
     halo on
 
     select {atomIndex=gDestAtomIdx}
 
     select {atomIndex=gDestAtomIdx}
Line 1,362: Line 1,332:
 
}
 
}
  
function clearAtomIdxs() {
+
function clear_atom_idxs() {
 
     gCcargoIdx = -1
 
     gCcargoIdx = -1
 
     gNcargoIdx = -1
 
     gNcargoIdx = -1
Line 1,369: Line 1,339:
 
     g1pivotIdx = -1
 
     g1pivotIdx = -1
 
     g2pivotIdx = -1
 
     g2pivotIdx = -1
 +
    g1dynamicIdx = -1
 +
    g2dynamicIdx = -1
 
     gDestAtomIdx = -1
 
     gDestAtomIdx = -1
 
     gSCidx = -1
 
     gSCidx = -1
 
}
 
}
  
function timedOut (s) {
+
function timed_out (s) {
 
     timeout ID"tug" OFF
 
     timeout ID"tug" OFF
    prompt(s)
 
    gBusy = FALSE
 
    background ECHO yellow
 
    restore state gState
 
    select gCargoAtoms
 
 
     refresh
 
     refresh
     quit
+
     if (prompt(format("%s - Undo?", s), "Yes|No", true) == "Yes") {
 +
        gBusy = false
 +
        restore state gState
 +
        connect
 +
        select gCargoSet
 +
        set bondPicking true
 +
        refresh
 +
        for (var i = 1; i <= gFreeze.size; i+=2) {
 +
            select {atomIndex=@{gFreeze[i]}} or {atomIndex=@{gFreeze[i+1]}}
 +
            color bonds lightblue
 +
        }
 +
        background ECHO yellow
 +
        echo @gEcho
 +
        select all
 +
        quit
 +
    }
 
}
 
}
  
function recordDrag() {
+
function record_drag() {
 
     var ls = format("select %s;", {selected})
 
     var ls = format("select %s;", {selected})
 
     ls += format("gCanchorIdx = %d;", gCanchorIdx)
 
     ls += format("gCanchorIdx = %d;", gCanchorIdx)
Line 1,390: Line 1,372:
 
     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,379:
 
     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)
 
     ls += format("gSCpt = %s;", gSCpt)
 
     ls += format("gSCpt = %s;", gSCpt)
     ls += "collectRotors();"
+
     ls += "collect_rotors();"
     ls += "tugDragDoneMB();"
+
     ls += "tug_drag_done_mb();"
     plicoRecord(ls)
+
     plico_record(ls)
 
}
 
}
  
# Bound to LEFT-UP by tugEnableDrag
+
# Pick call-back for freeze
function tugDragDoneMB() {
+
function tug_pick_cb() {
     if (gPlicoRecord != "") {
+
     if (_pickInfo[3][6] == "bond") {
        recordDrag()
+
        var sel = {selected}
 +
        var i = _pickInfo.find(":")
 +
        var iChain = _pickInfo[i+1]
 +
        i = _pickInfo.find("#")
 +
        var a1no = 0 + _pickInfo[i+1][i+3]
 +
        var j = _pickInfo[i+1][9999].find("#")
 +
        var a2no = 0 + _pickInfo[i+j+1][i+j+3]
 +
        var i1idx = {(atomno=a1no) and (chain=iChain)
 +
            and thisModel}.atomIndex
 +
        var i2idx = {(atomno=a2no) and (chain=iChain)
 +
            and thisModel}.atomIndex
 +
        if (({atomIndex=i1idx}.atomName == "CA")
 +
            or ({atomIndex=i2idx}.atomName == "CA")) {
 +
            if (({atomIndex=i1idx}.atomName != "CB")
 +
                and ({atomIndex=i2idx}.atomName != "CB")) {
 +
                if (i1idx > i2idx) {
 +
                    var idx = 0 + i1idx
 +
                    i1idx = 0 + i2idx
 +
                    i2idx = 0 + idx
 +
                }
 +
                select {atomIndex=i1idx} or {atomIndex=i2idx}
 +
 
 +
                for (i = 1; i <= gFreeze.size; i += 2) {
 +
                    if ((gFreeze[i] == i1idx) and (gFreeze[i+1] == i2idx)) {
 +
                        if (i > 1) {
 +
                            if (gFreeze.size = (i+1)) {
 +
                                gFreeze = gFreeze[1][i-1]
 +
                            }
 +
                            else {
 +
                                gFreeze = gFreeze[1][i-1] + gFreeze[i+2][0]
 +
                            }
 +
                        }
 +
                        else {
 +
                            gFreeze = gFreeze[i+2][0]
 +
                        }
 +
                        color bonds NONE
 +
                        i = 0
 +
                        break
 +
                    }
 +
                }
 +
                if (i > gFreeze.size) {
 +
                    gFreeze += i1idx
 +
                    gFreeze += i2idx
 +
                    color bonds lightblue
 +
                }
 +
            }
 +
        }
 +
        select {sel}
 
     }
 
     }
+
}
     # Move by rotation on rotor sets, smallest first
+
 
    gBusy = TRUE
+
# Bound to LEFT-UP by tug_enable_drag
    background ECHO pink
+
function tug_drag_done_mb() {
    refresh
+
     if (not gBusy) {
 +
        if (gPlicoRecord != "") {
 +
            record_drag()
 +
        }
 +
 
 +
        # Move by rotation on rotor sets, smallest first
 +
        gBusy = true
 +
        background ECHO pink
 +
        refresh
  
    # If side chain mode
+
        # If side chain mode
    if (gSCidx >= 0) {
+
        if (gSCidx >= 0) {
        dragSC()
+
             drag_sc()
    }
 
   
 
    # 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
 +
            timeout ID"tug" 20.0 "timed_out(\"Tug timed out\")"
 +
             if ((gCrotors.size < gNrotors.size) or (gNanchorIdx < 0)) {
 +
                if (gCrotors.size > 4) {
 +
                    tug_track_c()
 +
                }
 +
                 if (gOK and (gNrotors.size > 4)) {
 +
                    tug_track_n()
 +
                }
 
             }
 
             }
             if (gOK and (gCrotors.size > 4)) {
+
             else {
                tugTrackC() # PULLSTRING MODEL
+
                if (gNrotors.size > 4) {
 +
                    tug_track_n()
 +
                }
 +
                if (gOK and (gCrotors.size > 4)) {
 +
                    tug_track_c()
 +
                }
 
             }
 
             }
        }
+
            timeout ID"tug" OFF
        timeout ID"tug" OFF
+
 
       
+
            # If anchor angles acute, fail
        # If anchor angles acute, fail
+
            if (gOK == true) {
        if (gOK == TRUE) {
+
                if (gCanchorIdx >= 0) {
            if (gCanchorIdx >= 0) {
+
                    var ic = get_cward_bb_idx(gCanchorIdx, gChain)
                var ic = getCwardBBidx(gCanchorIdx)
+
                    var in = get_nward_bb_idx(gCanchorIdx, gChain)
                var in = getNwardBBidx(gCanchorIdx)
+
                    if ((ic >= 0) and
                if ((ic >= 0) and
+
                        angle({atomIndex=ic}, {atomIndex=gCanchorIdx}, {atomIndex=in})
                    angle({atomIndex=ic}, {atomIndex=gCanchorIdx}, {atomIndex=in})
+
                        < 100.0) {
                     < 100.0) {
+
                        gOK = false
                    gOK = FALSE
+
                    }
 +
                }
 +
                if (gNanchorIdx >= 0) {
 +
                     var ic = get_cward_bb_idx(gNanchorIdx, gChain)
 +
                    var in = get_nward_bb_idx(gNanchorIdx, gChain)
 +
                    if ((in >= 0) and
 +
                        angle({atomIndex=ic}, {atomIndex=gNanchorIdx}, {atomIndex=in})
 +
                        < 100.0) {
 +
                        gOK = false
 +
                    }
 
                 }
 
                 }
 
             }
 
             }
             if (gNanchorIdx >= 0) {
+
 
                 var ic = getCwardBBidx(gNanchorIdx)
+
            # If too far
                var in = getNwardBBidx(gNanchorIdx)
+
             if (not gOK) {
                if ((in >= 0) and
+
                 timed_out("TUG TOO FAR!")
                    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)
+
             # Else OK
                 and (chain = gChain))
+
            else {
            var idx = {atomno=@{{chain=gChain}.min.atomno}}.atomIndex
+
 
 +
                 var idx = {
 +
                    (atomno=@{{(chain=gChain) and thisModel}.atomno.min})
 +
                    and (chain=gChain) and thisModel}.atomIndex
 +
 
  
           
+
                var ihc = 0
            var i = 0
+
                for (ihc = 0; ihc < 10; ihc++) {
            for (; i < 3; i++) {
+
                    select ((atomno >= gNanchorNo) and (atomno <= gCanchorNo)
                handleCollisions(FALSE, idx)
+
                        and (chain = gChain) and thisModel)
                if (countCollisions() == 0) {
+
                    handle_collisions( idx)
                     break
+
                    if (count_collisions(({})).size == 0) {
 +
                        break
 +
                    }
 +
                }
 +
                if (ihc == 10) {
 +
                     timed_out("Unable to handle all collisions!")
 
                 }
 
                 }
 
             }
 
             }
            if (i == 3) {
 
                var p = prompt("Unable to handle all collisions!")
 
                restore state gState
 
            }                   
 
 
         }
 
         }
 +
        select {gCargoSet}
 +
        gBusy = false
 +
        background ECHO yellow
 +
        set bondPicking true
 +
        refresh
 
     }
 
     }
    select {gCargoAtoms}
 
    gBusy = FALSE
 
    background ECHO yellow
 
    refresh
 
 
}
 
}
  
# Bound to ALT-LEFT-DRAG by tugEnableDrag
+
# Bound to ALT-SHIFT-LEFT-DRAG by tug_enable_drag
function tugDragMB() {
+
function tug_drag_2_mb() {
     if (gBusy == FALSE) {
+
    tug_drag_mb(true)
         var dx = (20.0 * (_mouseX - gMouseX))/_width
+
}
         var dy = (20.0 * (_mouseY - gMouseY))/_height
+
 
         var q = quaternion(script("show rotation"))
+
# Bound to ALT-LEFT-DRAG by tug_enable_drag
 +
function tug_drag_mb(alt) {
 +
     if (not gBusy) {
 +
        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 ptd = {@dx @dy 0}
 
         var pt = (!q)%ptd
 
         var pt = (!q)%ptd
   
+
        var caxis = {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") {
 +
                    if ({atomIndex=gSCidx}.group != "PRO") {
 +
                        var dir = ((abs(dx) > abs(dy))
 +
                            ? ((dx < 0) ? 10 : -10)
 +
                            : ((dy < 0) ? 1 : -1))
 +
                        counter_rotate(gSCidx, dir, not alt)
 +
                    }
 +
                }
 +
                else {
 +
                    gSCcheck = not alt
 +
                    tug_sc(pt)
 +
                }
 
             }
 
             }
  
             # Else          
+
             # Else
 
             else {
 
             else {
           
+
 
 
                 # If new drag
 
                 # If new drag
 
                 if (gNewDrag) {
 
                 if (gNewDrag) {
                     gNewDrag = FALSE
+
                     gNewDrag = false
 
                     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,593:
 
                     }
 
                     }
 
                 }
 
                 }
               
+
 
 
                 # 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}
+
                         caxis = {atomIndex=g2pivotIdx}
 
                     }
 
                     }
                   
+
 
 
                     # Else
 
                     # Else
 
                     else {
 
                     else {
                         axis = cross(pt, {0 0 0}) + {atomIndex=g1pivotIdx}.xyz
+
                         caxis = cross(pt, {0 0 0}) + {atomIndex=g1pivotIdx}.xyz
 
                     }
 
                     }
                   
+
 
                     var a = ((abs(angle(pt + {atomIndex=g1pivotIdx}.xyz,
+
                     var 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)
+
                     rotate_selected_record(g1pivotIdx, caxis, dir)
       
+
 
 
                 }
 
                 }
               
+
 
 
                 # Else translate it
 
                 # Else translate it
 
                 else {
 
                 else {
                     #translateSelected @pt
+
                     translate_selected_record(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) and (not alt)) {
                    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]
+
                        var 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 tow mode
 +
                                if (gTow) {
 +
 
 +
                                    # Make a dynamic pivot
 +
                                    if (g1pivotIdx < 0) {
 +
                                        g1pivotIdx = cSels[j].atomIndex
 +
                                        g1dynamicIdx = cNotSels[i].atomIndex
 +
                                        color {atomIndex=g1pivotIdx} lightgreen
 +
                                        set_distance_idx(cNotSels[i].atomIndex,
 +
                                            cSels[j].atomIndex,
 +
                                            kCtolerance + kDtolerance)
 +
                                    }
 +
                                    else if (g2pivotIdx < 0) {
 +
                                        g2pivotIdx = cSels[j].atomIndex
 +
                                        g2dynamicIdx = cNotSels[i].atomIndex
 +
                                        color {atomIndex=g2pivotIdx} lightgreen
 +
                                        set_distance_idx(cNotSels[i].atomIndex,
 +
                                            cSels[j].atomIndex,
 +
                                            kCtolerance + kDtolerance)
 +
                                    }
 +
                                    else {
 +
                                        gOk2 = false
 +
                                    }
 +
                                }
 +
                                else {
 +
 
 +
                                    # Try to resolve
 +
                                    select @{cSels[j]}
 +
                                    var idx = {(atomno=@{{(chain=gChain)
 +
                                        and thisModel}.atomno.min})
 +
                                        and (chain=gChain) and thisModel}.atomIndex
 +
                                    handle_collisions( idx)
 +
                                }
 +
                            }
 +
                        } # endfor
 +
                         if (not gOk2) {
 +
                            break
 +
                        }
 +
                     } # endfor
 +
 
 
                     # If unable
 
                     # If unable
                     if (gOk2 == FALSE) {
+
                     if (not gOk2) {
                   
+
 
 
                         # Back off
 
                         # Back off
 
                         background ECHO pink
 
                         background ECHO pink
 
                         delay 1
 
                         delay 1
 
                         if (g1pivotIdx >= 0) {
 
                         if (g1pivotIdx >= 0) {
                             rotateSelectedRecord(g1pivotIdx, axis, -a)
+
                             rotate_selected_record(g1pivotIdx, caxis, -a)
 
                         }
 
                         }
 
                         else {
 
                         else {
                             translateSelectedRecord(-pt)
+
                             translate_selected_record(-pt)
 
                         }
 
                         }
 
                         background ECHO yellow
 
                         background ECHO yellow
 +
                        set bondPicking true
 
                     }
 
                     }
 
                 }
 
                 }
                #tugDragDoneMB()
 
 
             }
 
             }
              
+
 
 +
             # If dynamic pivots
 +
            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
 
     }
 
     }
 
}
 
}
  
# Bound to ALT-LEFT-DOWN by tugEnableDrag
+
# Bound to ALT-LEFT-DOWN by tug_enable_drag
function tugMarkMB() {
+
function tug_mark_mb() {
 
     gMouseX = _mouseX
 
     gMouseX = _mouseX
 
     gMouseY = _mouseY
 
     gMouseY = _mouseY
    gNcargoXyz = {atomIndex=gNcargoIdx}.xyz
+
     gNewDrag = true
    gCcargoXyz = {atomIndex=gCcargoIdx}.xyz
 
     gNewDrag = TRUE
 
 
}
 
}
  
# Called by tugCargoMB and by tugAnchorMB or tugPivotMB when cargo exists
+
# Called by tug_cargo_mb
function tugEnableDrag() {
+
function tug_enable_drag() {
     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" +
 +
        "|ALT-SHIFT-DRAG=alt move|CLICK bond=freeze|ALT-DOUBLE-CLICK=undo" +
 +
        "|SHIFT-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" "tug_mark_mb";
     bind "ALT-LEFT-UP" "tugDragDoneMB";#ONCEMODE
+
     bind "ALT-LEFT-UP" "tug_drag_done_mb";
     bind "ALT-LEFT-DRAG" "tugDragMB";
+
     bind "ALT-SHIFT-LEFT-DOWN" "tug_mark_mb";
 +
    bind "ALT-LEFT-DRAG" "tug_drag_mb";
 +
    bind "ALT-SHIFT-LEFT-DRAG" "tug_drag_2_mb";
 +
 
 +
    unbind "SHIFT-LEFT-CLICK"
 +
    bind "SHIFT-LEFT-CLICK" "_pickAtom";
 +
    bind "SHIFT-LEFT-CLICK" "+:tug_anchor_mb";
 +
 
 +
    bind "ALT-CTRL-LEFT-CLICK" "_pickAtom";
 +
    bind "ALT-CTRL-LEFT-CLICK" "+:tug_pivot_mb";
 +
 
 +
    bind "ALT-SHIFT-LEFT-CLICK" "_pickAtom";
 +
    bind "ALT-SHIFT-LEFT-CLICK" "+:tug_dest_atom_mb";
 
}
 
}
  
# Bound to SHIFT-LEFT-CLICK by tugCargoMB
+
# Bound to SHIFT-LEFT-CLICK by tug_cargo_mb
function tugAnchorMB() {
+
function tug_anchor_mb() {
 
     if ({atomIndex=_atomPicked}.chain == gChain) {
 
     if ({atomIndex=_atomPicked}.chain == gChain) {
         var aPidx = getScBBidx( _atomPicked)
+
         var aPidx = get_sc_bb_idx( _atomPicked, gChain)
       
+
 
 
         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,769:
 
                 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)
+
             collect_bb_rotors(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,784:
 
                 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)
+
             collect_bb_rotors(true)
 +
        }
 +
        else {
 +
            tow_cargo_mb()
 
         }
 
         }
 +
 +
        # Get moving atoms set
 +
        gMovingSet = {((atomno < gCanchorNo) and (atomno > gNanchorNo)
 +
            and (chain=gChain) and thisModel)}
 
     }
 
     }
   
+
     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}
 
 
}
 
}
  
# Bound to ALT-SHIFT-LEFT-CLICK by tugCargoMB
+
# Bound to ALT-SHIFT-LEFT-CLICK by tug_cargo_mb
function tugDestAtomMB() {
+
function tug_dest_atom_mb() {
     var aOk = TRUE
+
     var aOk = true
 
     if ({atomIndex=_atomPicked}.chain == gChain) {
 
     if ({atomIndex=_atomPicked}.chain == gChain) {
  
 
         var pno = {atomIndex=_atomPicked}.atomno
 
         var pno = {atomIndex=_atomPicked}.atomno
         if ((pno <= {atomIndex=gCcargoIdx}.atomno) and (pno >= {atomIndex=gNcargoIdx}.atomno)) {
+
         if ((pno <= {atomIndex=gCcargoIdx}.atomno)
             aOk = FALSE
+
            and (pno >= {atomIndex=gNcargoIdx}.atomno)) {
 +
             aOk = false
 
         }
 
         }
 
     }
 
     }
Line 1,700: Line 1,822:
 
             star on
 
             star on
 
         }
 
         }
         select {gCargoAtoms}
+
         select {gCargoSet}
 
     }
 
     }
 
}
 
}
  
# Bound to CTRL-LEFT-CLICK by tugCargoMB
+
# Bound to CTRL-LEFT-CLICK by tug_cargo_mb
function tugPivotMB() {
+
function tug_pivot_mb() {
 
     if (g1pivotIdx == _atomPicked) {
 
     if (g1pivotIdx == _atomPicked) {
 
         color {atomIndex=g1pivotIdx} @gScheme
 
         color {atomIndex=g1pivotIdx} @gScheme
Line 1,711: Line 1,833:
 
             g1pivotIdx = g2pivotIdx
 
             g1pivotIdx = g2pivotIdx
 
             g2pivotIdx = -1
 
             g2pivotIdx = -1
 +
            g2dynamicIdx = -1
 
         }
 
         }
 
         else {
 
         else {
 
             g1pivotIdx = -1
 
             g1pivotIdx = -1
 +
            g1dynamicIdx = -1
 
         }
 
         }
 
     }
 
     }
Line 1,719: Line 1,843:
 
         color {atomIndex=g2pivotIdx} @gScheme
 
         color {atomIndex=g2pivotIdx} @gScheme
 
         g2pivotIdx = -1
 
         g2pivotIdx = -1
 +
        g2dynamicIdx = -1
 
     }
 
     }
 
     else if (g1pivotIdx >= 0) {
 
     else if (g1pivotIdx >= 0) {
Line 1,724: Line 1,849:
 
             color {atomIndex=g2pivotIdx} @gScheme
 
             color {atomIndex=g2pivotIdx} @gScheme
 
         }
 
         }
           
+
 
 
         g2pivotIdx = _atomPicked
 
         g2pivotIdx = _atomPicked
 +
        g2dynamicIdx = -1
 
         color {atomIndex=g2pivotIdx} green
 
         color {atomIndex=g2pivotIdx} green
 
     }
 
     }
 
     else {
 
     else {
 
         g1pivotIdx = _atomPicked
 
         g1pivotIdx = _atomPicked
 +
        g1dynamicIdx = -1
 
         color {atomIndex=g1pivotIdx} green
 
         color {atomIndex=g1pivotIdx} green
 
     }
 
     }
     select {gCargoAtoms}
+
     select {gCargoSet}
 
}
 
}
  
# Bound to ALT-LEFT-CLICK by plicoTug
+
# Bound to SHIFT-LEFT-CLICK by plico_tug
function tugCargoMB() {
+
function tow_cargo_mb() {
      
+
     gTow = true
     if (gChain != {atomIndex=_atomPicked}.chain) {
+
     gChain = {atomIndex=_atomPicked}.chain
        clearAtomIdxs()
+
    gMinNo = {(chain=gChain) and thisModel}.atomno.min
        setColors()
+
    gMaxNo = {(chain=gChain) and thisModel}.atomno.max
        gChain = {atomIndex=_atomPicked}.chain
+
    gCcargoIdx = -1
     }
+
    gNcargoIdx = -1
   
+
    gCanchorIdx = -1
     # If movable side chain atom picked
+
    gCanchorNo = gMaxNo + 1
     if (isMovableSideChain( _atomPicked)) {
+
    gNanchorIdx = -1
 +
    gNanchorNo = gMinNo - 1
 +
 
 +
    # Highlight cargo cluster
 +
    select {(chain=gChain) and thisModel}
 +
    gCargoSet = {selected}
 +
    gMovingSet = {selected}
 +
    set_colors()
 +
 
 +
    # Enable dragging
 +
    tug_enable_drag()
 +
    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" "+:tow_cargo_mb";
 +
}
 +
 
 +
# Bound to ALT-LEFT-CLICK by plico_tug or called by plicotoab.toabCargoMB
 +
function tug_cargo_mb() {
 +
 
 +
     # If O or movable side chain atom picked
 +
     if ((({atomIndex=_atomPicked}.atomName = "O")
 +
        and ({atomIndex=_atomPicked}.group != "PRO"))
 +
        or (is_moveable_sc( _atomPicked))) {
 
         if (gSCidx >= 0) {
 
         if (gSCidx >= 0) {
 
             draw gSCcircle DELETE
 
             draw gSCcircle DELETE
Line 1,751: Line 1,904:
 
         if (gSCidx == _atomPicked) {
 
         if (gSCidx == _atomPicked) {
 
             gSCidx = -1
 
             gSCidx = -1
         }      
+
         }
 
         else {
 
         else {
 
             gSCidx = _atomPicked
 
             gSCidx = _atomPicked
Line 1,758: Line 1,911:
 
         }
 
         }
 
     }
 
     }
     else if ((gChain == "") or ({atomIndex=_atomPicked}.chain == gChain)) {
+
     else {
         gMinNo = {chain=gChain}.atomno.min  
+
        if ({atomIndex=_atomPicked}.chain != gChain) {
         gMaxNo = {chain=gChain}.atomno.max
+
            if (gTow) {
 +
                select {(chain=gChain) and thisModel}
 +
                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) and thisModel}.atomno.min
 +
         gMaxNo = {(chain=gChain) and thisModel}.atomno.max
 
         if (gNanchorIdx < 0) {
 
         if (gNanchorIdx < 0) {
 
             gNanchorNo = gMinNo - 1
 
             gNanchorNo = gMinNo - 1
 
         }
 
         }
 
         if (gCanchorIdx < 0) {
 
         if (gCanchorIdx < 0) {
             gCanchorNo = gMaxNo + 1  
+
             gCanchorNo = gMaxNo + 1
 
         }
 
         }
         var aPidx = getScBBidx( _atomPicked)
+
         var aPidx = get_sc_bb_idx( _atomPicked, gChain)
       
+
 
 
         gSCidx = -1
 
         gSCidx = -1
 
         draw gSCcircle DELETE
 
         draw gSCcircle DELETE
Line 1,774: Line 1,942:
 
         # 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) {
                 gCcargoIdx = getCpIdx(gNcargoIdx)
+
                 gCcargoIdx = get_cp_idx(gNcargoIdx)
 
                 gCcargoNo = {atomIndex=gCcargoIdx}.atomno
 
                 gCcargoNo = {atomIndex=gCcargoIdx}.atomno
 
             }
 
             }
Line 1,792: Line 1,960:
 
             select {atomIndex=gNcargoIdx}
 
             select {atomIndex=gNcargoIdx}
 
             halo off
 
             halo off
             gNcargoIdx = getNmIdx(gCcargoIdx)
+
             gNcargoIdx = get_nm_idx(gCcargoIdx)
 
             gNcargoNo = {atomIndex=gNcargoIdx}.atomno
 
             gNcargoNo = {atomIndex=gNcargoIdx}.atomno
 
         }
 
         }
 
         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,975:
 
                     halo off
 
                     halo off
 
                 }
 
                 }
               
+
 
 
                 # Set new nWard cargo and highlight it
 
                 # Set new nWard cargo and highlight it
                 gNcargoIdx = getNmIdx(aPidx)
+
                 gNcargoIdx = get_nm_idx(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,989:
 
                     halo off
 
                     halo off
 
                 }
 
                 }
           
+
 
 
                 # Set new cWard cargo and highlight
 
                 # Set new cWard cargo and highlight
                 gCcargoIdx = getCpIdx(aPidx)
+
                 gCcargoIdx = get_cp_idx(aPidx)
 
                 gCcargoNo = {atomIndex=gCcargoIdx}.atomno
 
                 gCcargoNo = {atomIndex=gCcargoIdx}.atomno
 
             }
 
             }
 
         }
 
         }
       
+
 
 
         # 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 = get_cp_idx(aPidx)
 
             gCcargoNo = {atomIndex=gCcargoIdx}.atomno
 
             gCcargoNo = {atomIndex=gCcargoIdx}.atomno
             gNcargoIdx = getNmIdx(gCcargoIdx)
+
             gNcargoIdx = get_nm_idx(gCcargoIdx)
 
             gNcargoNo = {atomIndex=gNcargoIdx}.atomno
 
             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
 
            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 2,015:
 
             gNanchorNo = gMinNo - 1
 
             gNanchorNo = gMinNo - 1
 
         }
 
         }
       
+
 
 
         # Highlight cargo cluster
 
         # Highlight cargo cluster
         selectNward(gCcargoIdx, gNcargoIdx)
+
         select_nward_idx(gCcargoIdx, gNcargoIdx)
         gCargoAtoms = {selected}
+
         gCargoSet = {selected}
         setColors()
+
         set_colors()
                           
+
 
 
         # Collect the rotor sets
 
         # Collect the rotor sets
         collectRotors()
+
         collect_rotors()
   
+
 
         # 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) and thisModel)}
        gAc = (within(kCtolerance, FALSE, {aset}) and not {aset})
 
 
     }
 
     }
   
+
 
 
     # Enable dragging
 
     # Enable dragging
     tugEnableDrag()
+
     if (gToab) {
 +
        to_ab_enable_drag()
 +
    }
 +
    else {
 +
        tug_enable_drag()
 +
    }
 +
 
 +
    select {gCargoSet}
 +
}
  
    # Bind other keys
+
function tug_undo_mb() {
     bind "SHIFT-LEFT-CLICK" "_pickAtom";
+
     if (prompt("Undo", "Yes|No", true) == "Yes") {
    bind "SHIFT-LEFT-CLICK" "+:tugAnchorMB";
+
        restore state gState
   
+
     }
     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 plico_tug() {
     set allowModelKit TRUE
+
 
     set allowRotateSelected TRUE
+
     # Load common functions if not already
     set allowMoveAtoms TRUE
+
     if (kCommon < 7) {
 +
        script $SCRIPT_PATH$plicoCommon.spt
 +
        if (kCommon < 7) {
 +
            prompt ("A newer version of plicoCommon.SPT is required")
 +
            quit
 +
        }
 +
    }
 +
 
 +
     gPlico = "TUG"
 +
    plico_prelim(true, true)
 
      
 
      
     # Push selected
+
     gBondPicking = bondPicking
    gSelSaves = {selected}
+
     set bondPicking true
     select all
+
     set PickCallback "jmolscript:tug_pick_cb"
     gZoom = script("show zoom")
+
 
     gRotate = script("show rotation")
+
     gEcho = ("_________TUG_________|ALT-CLICK=mark block|SHIFT-CLICK=mark chain" +
    write tugsave.pdb
+
        "|CLICK bond=freeze|ALT-DOUBLE-CLICK=undo|SHIFT-DOUBLE-CLICK=exit")
    select none
 
   
 
    gScheme = defaultColorScheme
 
    gAltScheme = ((gScheme == "Jmol") ? "Rasmol" : "Jmol")
 
    set echo TOP LEFT
 
    background ECHO yellow
 
    gEcho = "ALT-CLICK=set cargo range"
 
 
     echo @gEcho
 
     echo @gEcho
 
     gCrotors = array()
 
     gCrotors = array()
 
     gNrotors = array()
 
     gNrotors = array()
     clearAtomIdxs()
+
     clear_atom_idxs()
     gChain = ""
+
     gToab = false
    unbind
 
  
    set picking ON
 
 
     bind "ALT-LEFT-CLICK" "_pickAtom";
 
     bind "ALT-LEFT-CLICK" "_pickAtom";
     bind "ALT-LEFT-CLICK" "+:tugCargoMB";
+
     bind "ALT-LEFT-CLICK" "+:tug_cargo_mb";
     bind "DOUBLE" "tugExit";
+
     bind "SHIFT-LEFT-CLICK" "_pickAtom";
 +
    bind "SHIFT-LEFT-CLICK" "+:tow_cargo_mb";
 +
    bind "ALT-LEFT-DOUBLE" "tug_undo_mb";
 +
    bind "SHIFT-DOUBLE" "tug_exit(true)";
 +
    bind "LEFT-CLICK" "+:plico_menu_toggle";
 
}
 
}
  
# Bound to DOUBLE by plicoTug
+
# Bound to DOUBLE by plico_tug
function tugExit() {
+
function tug_exit() {
     var p = prompt("Exit tug?", "Yes|No|Undo", TRUE)
+
     if (plico_exit()) {
    if (p == "Undo") {
+
         set bondPicking gBondPicking
        load tugsave.pdb
+
         set PickCallback NONE
        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
 
         select all
        halo off
+
         color bonds none
        star off
 
         color {selected} @gScheme
 
        draw gSCcircle DELETE
 
        gBusy = FALSE
 
        background ECHO yellow
 
 
          
 
          
         # Pop selected
+
         reset kTug
         select gSelSaves
+
         reset gCanchorIdx 
 +
        reset gCanchorNo 
 +
        reset gPlico     
 +
        reset gNanchorIdx 
 +
        reset gNanchorNo 
 +
        reset gCcargoIdx 
 +
        reset gNcargoIdx 
 +
        reset gCcargoNo   
 +
        reset gNcargoNo   
 +
        reset gDestAtomIdx
 +
        reset g1pivotIdx 
 +
        reset g2pivotIdx 
 +
        reset gSelSaves  
 +
        reset gCrotors   
 +
        reset gNrotors   
 +
        reset gOkCollide 
 +
        reset gChain     
 +
        reset gMinNo     
 +
        reset gMaxNo     
 +
        reset gCargoSet   
 +
        reset gMovingSet 
 +
        reset gSCidx     
 +
        reset gSCcircle   
 +
        reset gSCpt       
 +
        reset gTargetPt   
 +
        reset gNewDrag   
 +
        reset gTow       
 +
        reset g1dynamicIdx
 +
        reset g2dynamicIdx
 +
        reset gSCcheck   
 +
        reset gBondPicking
 +
        reset gFreeze     
 +
        reset gToab       
 +
        reset function "get_cp_idx"
 +
        reset function "get_cm_no"
 +
        reset function "get_nm_idx"
 +
        reset function "get_np_no"
 +
        reset function "get_cb_idx"
 +
        reset function "get_o_idx"
 +
        reset function "get_nward_bb_idx"
 +
        reset function "get_cward_bb_idx"
 +
        reset function "get_sc_set"
 +
        reset function "get_sc_bb_idx"
 +
        reset function "is_bb_idx"
 +
        reset function "is_sc_idx"
 +
        reset function "select_add_sc"
 +
        reset function "handle_collisions"
 +
        reset function "tug_track_idx"
 +
        reset function "do_counter_rotate"
 +
        reset function "counter_rotate"
 +
        reset function "counter_rotate_2"
 +
        reset function "repair_proline"
 +
        reset function "repair_sc"
 +
        reset function "tug_track_c"
 +
        reset function "tug_track_n"
 +
        reset function "translate_selected_record"
 +
        reset function "rotate_selected_record"
 +
        reset function "collect_sc_rotors"
 +
        reset function "drag_sc"
 +
        reset function "fix_sc_collision_2"
 +
        reset function "is_moveable_sc"
 +
        reset function "is_rotor_avail"
 +
        reset function "xcollect_bb_rotors"
 +
        reset function "collect_bb_rotors"
 +
        reset function "collect_rotors"
 +
        reset function "tug_sc"
 +
        reset function "set_colors"
 +
        reset function "clear_atom_idxs"
 +
        reset function "timed_out"
 +
        reset function "record_drag"
 +
        reset function "tug_pick_cb"
 +
        reset function "tug_drag_done_mb"
 +
        reset function "tug_drag_2_mb"
 +
        reset function "tug_drag_mb"
 +
        reset function "tug_mark_mb"
 +
        reset function "tug_anchor_mb"
 +
        reset function "tug_enable_drag"
 +
        reset function "tug_dest_atom_mb"
 +
        reset function "tug_pivot_mb"
 +
        reset function "tow_cargo_mb"
 +
        reset function "tug_cargo_mb"
 +
        reset function "tug_undo_mb"
 +
        reset function "plico_tug"
 +
 
 +
 
 
     }
 
     }
 
}
 
}
# End of TUG.SPT</pre>
+
 
 +
# End of TUG.SPT
 +
</pre>

Latest revision as of 17:15, 12 April 2016

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. A full description of its capabilities can be found here.

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 directory>/tug.spt;plico_tug

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

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

#   tug - Jmol script by Ron Mignery
#   v1.20 beta    4/12/2016 -axis is now a reserved word
#
#   Translate or rotate a stretch of a polypeptide against itself
#    or against other chains by mouse actions
#
kTug = 3
gCanchorIdx = -1
gCanchorNo = -1
gPlico = "TUG"
gNanchorIdx = -1
gNanchorNo = -1
gCcargoIdx = -1
gNcargoIdx = -1
gCcargoNo = -1
gNcargoNo = -1
gDestAtomIdx = -1
g1pivotIdx = -1
g2pivotIdx = -1
gSelSaves = ({})
gCrotors = array()
gNrotors = array()
gOkCollide = ({})
gChain = ""
gMinNo = 1
gMaxNo = 9999
gCargoSet = ({})
gMovingSet = ({})
gBusy = false
gSCidx = -1
gSCcircle = -1
gSCpt = {0 0 0}
gTargetPt = {0 0 0}
gNewDrag = false
gTow = false
g1dynamicIdx = -1
g2dynamicIdx = -1
gSCcheck = true
gBondPicking = false
gFreeze = array()
gToab = false


function get_cp_idx (idx) {
    var no = {atomIndex=idx}.atomno
    while ((no < gMaxNo) and ({(atomno=no) and (chain=gChain)
        and thisModel}.atomName != "C")) {
        no++
    }
    return ({(atomno=no) and (chain=gChain) and thisModel}.atomIndex)
}

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

function get_nm_idx (idx) {
    var no = {atomIndex=idx}.atomno
    while ((no > 0) and ({(atomno=no) and (chain=gChain)
        and thisModel}.atomName != "N")) {
        no--
    }
    return ({(atomno=no) and (chain=gChain) and thisModel}.atomIndex)
}

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

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

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

function get_nward_bb_idx (idx, iChain) {
    var no = {atomIndex=idx}.atomno - 1
    for (; no >= 0; no--) {
        var atomName = {(atomno=no) and (chain=iChain)
            and thisModel}.atomName
        if ((atomName = "N") or (atomName = "C") or (atomName = "CA")) {
            break
        }
    }
    return ((no >= 0) ? ({(atomno=no) and (chain=iChain)
        and thisModel}.atomIndex) : -1)
}

function get_cward_bb_idx (idx, iChain) {
    var no = {atomIndex=idx}.atomno + 1
    for (; no < gMaxNo; no++) {
        var atomName = {(atomno=no) and (chain=iChain)
            and thisModel}.atomName
        if ((atomName = "N") or (atomName = "C") or (atomName = "CA")) {
            break
        }
    }
    return ((no >= 0) ? ({(atomno=no) and (chain=iChain)
        and thisModel}.atomIndex) : -1)
}

function get_sc_set (scIdx, iChain) {
    var scSet = ({})
    var idx = get_sc_bb_idx(scIdx, iChain)
    var iNo = {atomIndex=idx}.atomno + 3

    for (var i = 1; i < 20; i++) {
        idx = {(atomno=@{iNo+i}) and (chain=iChain)
            and thisModel}.atomIndex
        if (is_bb_idx(idx)) {
            break
        }
        scSet = scSet or {atomIndex=idx}
    }
    return scSet
}

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

function is_bb_idx(aIdx) {
    var ret = false
    switch({atomIndex=aIdx}.atomName) {
    case "N":
    case "CA":
    case "C":
        ret = true
        break
    }
    return ret
}

function is_sc_idx(aIdx) {

    var ret = false
    if (not is_bb_idx(aIDx)) {

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

function select_add_sc(fromIdx) {
    var iNo = {atomIndex=fromIdx}.atomno
    var iChain = {atomIndex=fromIdx}.chain
    select none
    while ({(atomno=iNo) and (chain=iChain)
        and thisModel and sidechain}) {
        var a = {(atomno=iNo) and (chain=iChain)and thisModel}
        a.selected = true
        iNo++
    }
}

# Resolve collisions on selection
function handle_collisions( targetIdx) {

    # For all selected atoms
    for (var iNo = {selected}.min.atomno; iNo <= {selected}.max.atomno; iNo++) {
        var idx = {(atomno=iNo) and (chain=gchain)
            and thisModel}.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) {
                # Ignore kinked BB
                try {
                    if (is_bb_idx(idx) and (angle(
                        {atomIndex=@{get_cward_bb_idx(idx, gChain)}}, {atomIndex=idx},
                        {atomIndex=@{get_nward_bb_idx(idx, gChain)}}) < 100.0)) {
                        continue
                    }
                }
                catch {
                }

                # 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 (is_sc_idx(cidx) and ({atomIndex=cidx}.group != "PRO")) {
                        fix_sc_collision_2(cidx)
                        
                        # If not fixed, exit fail
                        if (not gOk2) {
                            return # early exit (break n jmol bug)
                        }
                    }

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

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

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

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

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

                        # If not fixed, exit fail
                        if (not gOk2) {
                            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 tug_track_idx(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 (abs(dh).type == "string") {
                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=@{get_cward_bb_idx(i1, gChain)}}, {atomIndex=i1},
                        {atomIndex=i2}, {atomIndex=i3}) + dt
                }
                else {
                    phi = angle({atomIndex=i1}, {atomIndex=i2},
                        {atomIndex=i3}, {atomIndex=@{get_nward_bb_idx(i3, gChain)}}) + dt
                }

                if ({atomIndex=i2}.atomno > {atomIndex=targetIdx}.atomno) {
                    movePt = true
                    select_nward_idx(i3, get_cward_bb_idx(targetIdx, gChain))
                    {atomIndex=targetIdx}.selected = true
                }
                else {
                    select_cward_idx(i2, targetIdx)
                }
            }
            else {
                if (({atomIndex=i2}.atomName="CA")) {
                    phi = angle({atomIndex=@{get_nward_bb_idx(i1, gChain)}}, {atomIndex=i1},
                        {atomIndex=i2}, {atomIndex=i3}) + dt
                }
                else {
                    psi = angle({atomIndex=i2}, {atomIndex=i3},
                        {atomIndex=i4}, {atomIndex=@{get_cward_bb_idx(i4, gChain)}}) + dt
                }

                if ({atomIndex=i2}.atomno < {atomIndex=targetIdx}.atomno) {
                    movePt = true
                    select_cward_idx(i3, get_nward_bb_idx(targetIdx, gChain))
                    {atomIndex=targetIdx}.selected = true
                }
                else {
                    select_nward_idx(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
                        }
                        handle_collisions( targetIdx)
                        if (not gOk2) {
                            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 20 passes
}

# Counter rotate bonds on either side of a BB O
function do_counter_rotate(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 counter_rotate(oIdx, dir, nWard) {

    var iChain = {atomIndex=oIdx}.chain
    var selsave = {selected}
    var cIdx = get_sc_bb_idx(oIdx, iChain)
    var nIdx = get_cward_bb_idx(cIdx, iChain)
    var caPhiIdx = get_cward_bb_idx(nIdx, iChain)
    var caPsiIdx = get_nward_bb_idx(cIdx, iChain)

    if (nWard) {
        var nNo = {(chain=iChain) and thisModel}.atomno.min
        select_nward_idx(caPsiIdx, {(atomno=nNo) and (chain=iChain)
            and thisModel}.atomIndex)
    }
    else {
        var cNo = {(chain=iChain) and thisModel}.atomno.max
        select_cward_idx(caPhiIdx, {(atomno=cNo) and (chain=iChain)
            and thisModel}.atomIndex)
    }

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

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

    var iChain = {atomIndex=oIdx}.chain
    var selsave = {selected}
    var gOk2 = true
    var cIdx = get_sc_bb_idx(oIdx, iChain)
    var nIdx = get_cward_bb_idx(cIdx, iChain)
    var caPhiIdx = get_cward_bb_idx(nIdx, iChain)
    var caPsiIdx = get_nward_bb_idx(cIdx, iChain)

    var nTward = ({atomIndex=oIdx}.atomno < {atomIndex=terminalIdx}.atomno)
    if (nTward) {
        select_cward_idx(cIdx, terminalIdx)
    }
    else {
        select_nward_idx(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
        do_counter_rotate(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) {
            do_counter_rotate(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 repair_proline(BBidx) {
    var cbidx = get_cb_idx(BBidx)
    var cbno = {atomIndex=cbidx}.atomno
    var cgidx = {(atomno=@{cbno+1}) and (chain=gChain)
        and thisModel}.atomIndex
    var cdidx = {(atomno=@{cbno+2}) and (chain=gChain)
        and thisModel}.atomIndex
    var caidx = {(atomno=@{cbno-3}) and (chain=gChain)
        and thisModel}.atomIndex
    var nidx = {(atomno=@{cbno-4}) and (chain=gChain)
        and thisModel}.atomIndex

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

    select {atomIndex=cdidx}
    set_distance_idx(nidx, cdidx, 1.47)
    set_angle_idx(caidx, nidx, cdidx, 102.7)
    set_dihedral_idx(cbidx, caidx, nidx, cdidx, 16.2)

    select {atomIndex=cgidx}
    set_distance_idx(cdidx, cgidx, 1.51)
    set_angle_idx(nidx, cdidx, cgidx, 106.4)
    set_dihedral_idx(caidx, nidx, cdidx, cgidx, 16.2)
}

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

    var idx = (nWard ? get_cward_bb_idx(targetIdx, gChain) : get_nward_bb_idx(targetIdx, gChain))

    if (({atomIndex=targetIdx}.atomName == "CA")
        and ({atomIndex=targetIdx}.group != "GLY")) {
        var cbidx = get_cb_idx(targetIdx)
        select none
        select_add_sc(cbidx)
        set_angle_idx(idx, targetIdx, cbidx, 110.0)
        set_distance_idx(targetIdx, cbidx, 1.5)
        if ({atomIndex=targetIdx}.group != "PRO") {
            var colliders = (within(kCtolerance, false, {selected})
                and not {atomIndex=targetIdx} and not {selected})
            if (colliders) {
                if ({atomIndex=targetIdx}.group != "ALA") {
                    fix_sc_collision_2(cbidx)
                }
            }
        }
        else {
            if (nWard) {
            }
            else {
                set_dihedral_idx(get_nward_bb_idx(idx, gChain), idx, targetIdx, cbidx, 174.2)
            }
        }
    }

    else if ({atomIndex=targetIdx}.atomName == "C") {
        var oidx = get_o_idx(targetIdx)
        select {atomIndex=oidx}
        set_angle_idx(idx, targetIdx, oidx, 120.0)
        set_distance_idx(targetIdx, oidx, 1.21)
        if (nWard) {
            set_dihedral_idx(get_cward_bb_idx(idx, gChain), idx, targetIdx, oidx, 0.0)
        }
        if ({atomIndex=idx}.group == "PRO") {
            repair_proline(idx)
            var dNo = {atomIndex=targetIdx}.atomno + 4
            var dIdx = {(atomno=dNO) and (chain=gChain)
                and thisModel}.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") {
                    counter_rotate_2(colliders[i].atomIndex,
                        {atomIndex=dIdx}.xyz, targetIdx, false)
                }
            }
        }
    }
}

# Rebuild Cward rotors set
function tug_track_c() {

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

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

        # Step to next atom
        targetIdx = get_cward_bb_idx(targetIdx, gChain)
        if (targetIdx < 0) {
            break
        }

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

        # Compute targets desired coords
        try {
            var c1idx = get_cward_bb_idx(targetIdx, gChain)
            var n1idx = get_nward_bb_idx(targetIdx, gChain )
            var n2idx = get_nward_bb_idx(n1Idx, gChain)
            var n3idx = get_nward_bb_idx(n2Idx, gChain)
            var pt = {0 0 0}
            if ({atomIndex=targetIdx}.atomName == "N") {
                var oidx = get_o_idx(n1idx)
                select {atomIndex=oidx}
    
                # Desired target location is trigonal O
                set_distance_idx(n1idx, oidx, 1.5)
                pt = get_trigonal_idx(n2idx, n1idx, oidx, 1.37)
                set_distance_idx(n1idx, oidx, 1.21)
            }
            else if (({atomIndex=targetIdx}.atomName == "C")
                and ({atomIndex=targetIdx}.group != "GLY")) {
    
                # Desired target location is tetragonal CB
                var cbidx = get_cb_idx(n1idx)
                pt = get_tet_idx(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}
                set_distance_idx(n1idx, targetIdx, 1.5)
                set_angle_idx(n2idx, n1idx, targetIdx, 120.0)
                if ({atomIndex=targetIdx}.atomName == "CA") {
                    set_dihedral_idx(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 (not gOK)) {
    
                    # Rotate on cWard rotor set to move it there
                    tug_track_idx(targetIdx, pt, false, false)
                    xcount++
                }
            }
            else {
                gOK = true
                okCount++
            }
        }
        catch {
        }

        # If successful
        if (gOK == true) {

            # Adust any side atoms
            repair_sc(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 tug_track_n() {

    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 = get_nward_bb_idx(targetIdx, gChain)
        if (targetIdx < 0) {
            break
        }
        
        # No collision with cargo allowed after two atoms placed
        if (tcount == 2) {
           gOkCollide = ({})
        }
        tcount++

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

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

            # Desired target location is r-tetragonal CB
            var cbidx = get_cb_idx(c1idx)
            pt = get_tet_idx(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}
            set_distance_idx(c1idx, targetIdx, 1.37)
            set_angle_idx(c2idx, c1idx, targetIdx, 110.0)

            if ({atomIndex=targetIdx}.group == "PRO") {
                set_dihedral_idx(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 (not gOK)) {

                # Rotate on nWard rotor set to move it there
                tug_track_idx(targetIdx, pt, true, false)
                xcount++
            }
        }
        else {
            gOK = true
            okCount++
        }

        # If sucessful
        if (gOK == true) {

            # Adust any side atoms
            repair_sc(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 translate_selected_record(pt) {
    if (gPlicoRecord != "") {
        plico_record(format("select %s;translateSelected %s;", {selected}, pt))
    }
    translateSelected @pt
}

# gPlicoRecord is maintained by the macro pilcoRecord
function rotate_selected_record(pivotIdx, caxis, a) {
    if (gPlicoRecord != "") {
        plico_record(format("select %s;", {selected}))
        plico_record(format("rotateSelected {atomIndex=%d} @%s @%s;",
            pivotIdx, caxis, a))
    }
    rotateSelected {atomIndex=pivotIdx} @caxis @a
}

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

    }

    return scBondIdxs
}

# Drag Side Chain
function drag_sc() {
    var iNo = {atomIndex=gSCidx}.atomno
    var iChain  = {atomIndex=gSCidx}.chain
    if ({atomIndex=gSCidx}.group != "PRO") {

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

        # 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
                select_add_sc(scBondIdxs[1+(4*i)])
                set_dihedral_idx(scBondIdxs[4+(4*i)], scBondIdxs[3+(4*i)],
                    scBondIdxs[2+(4*i)], scBondIdxs[1+(4*i)], rot)
                var newDist = distance({atomIndex=gSCidx}.xyz, gSCpt)
                if (gSCcheck) {
                    var colliders = (within(kCtolerance, false, scSet)
                        and not connected(scSet) and not {scSet})
                    if (colliders) {
                        continue
                    }
                }

                # 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++) {
            select_add_sc(scBondIdxs[1+(4*i)])
            set_dihedral_idx(scBondIdxs[4+(4*i)], scBondIdxs[3+(4*i)],
                scBondIdxs[2+(4*i)], scBondIdxs[1+(4*i)], dh[i+1])
        }
        
        if (gSCcheck = false) {
            plico_minimize( scSet)
        }
    }
    else { # PRO - toggle between puckers up and down
        var icd = {(atomno=@{iNo+1}) and (chain=iChain)
            and thisModel}.atomIndex
        var icb = {(atomno=@{iNo-1}) and (chain=iChain)
            and thisModel}.atomIndex
        var ica = {(atomno=@{iNo-4}) and (chain=iChain)
            and thisModel}.atomIndex
        var in = {(atomno=@{iNo-5}) and (chain=iChain)
            and thisModel}.atomIndex
        select {atomIndex=gSCidx}

        if (angle({atomIndex=ica}, {atomIndex=in},
            {atomIndex=icd}, {atomIndex=gSCidx}) < -10.0) {
            set_dihedral_idx(ica, in, icd, gSCidx, 8.7)
            set_angle_idx(in, icd, gSCidx, 110.0)
            set_distance_idx(icd, gSCidx, 1.5)
        }
        else {
            set_dihedral_idx(ica, in, icd, gSCidx, -29.5)
            set_angle_idx(in, icd, gSCidx, 108.8)
            set_distance_idx(icd, gSCidx, 1.5)
        }
    }

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

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

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

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

    var scBondIdxs = collect_sc_rotors( 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
            select_add_sc(scBondIdxs[1+(4*i)])
            set_dihedral_idx(scBondIdxs[1+(4*i)], scBondIdxs[2+(4*i)],
                scBondIdxs[3+(4*i)], scBondIdxs[4+(4*i)], rot)

            # If no collision, exit
            var 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 is_moveable_sc(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
}

function is_rotor_avail(i1idx, i2idx) {
    var ret = true
    if (i1idx > i2idx) { # frozen bonds are kept as lo-hi
        var idx = @i1idx
        i1idx = @i2idx
        i2idx = @idx
    }

    for (var i = 1; i <= gFreeze.size; i += 2) {
        if ((gFreeze[i] == i1idx) and (gFreeze[i+1] == i2idx)) {
            ret = false
            break
        }
    }

    return ret
}

function xcollect_bb_rotors(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)
                and thisModel}.atomName == "CA") {
                if (is_rotor_avail(iNo)) {# xxx
                    if (({(atomno=iNo) and (chain=gChain)
                        and thisModel}.group != "PRO") and (iNo > cargoNo)) { # phi
                        rotors += [{(atomno=@{get_cm_no(iNo-1)}) and (chain=gChain)
                            and thisModel}.atomIndex,
                            {(atomno=@{iNo-1}) and (chain=gChain)
                                and thisModel}.atomIndex]
                        rotors += [{(atomno=@{iNo}) and (chain=gChain)
                            and thisModel}.atomIndex,
                            {(atomno=@{iNo+1}) and (chain=gChain)
                                and thisModel}.atomIndex]
                    }
                    if (iNo != (anchorNo-1)) { # psi
                        rotors += [{(atomno=@{iNo-1}) and (chain=gChain)
                            and thisModel}.atomIndex,
                            {(atomno=@{iNo}) and (chain=gChain)
                                and thisModel}.atomIndex]
                        rotors += [{(atomno=@{iNo+1}) and (chain=gChain)
                            and thisModel}.atomIndex,
                            {(atomno=@{get_np_no(iNo+2)}) and (chain=gChain)
                                and thisModel}.atomIndex]
                    }
                }
            }
        }
    }
    else {

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

    if (nWard) {
        gNrotors = rotors
    }
    else {
        gCrotors = rotors
    }
}
function collect_bb_rotors(nWard) {
    if (nWard) {
        gNrotors = array()
        var nres = {atomIndex=gNcargoIdx}.resno
        var nname = {atomIndex=gNcargoIdx}.atomName
        var ares = get_resno_min(gChain)
        var aA = get_atom_rcn( ares, gChain, "N")
        if (gNanchorIdx >= 0) {
            aA = {atomIndex=gNanchorIdx}
        }
        var aname = aA.atomName
        for (var r = nres; r >= ares; r--) {
            var aCp = get_atom_rcn( r-1, gChain, "C")
            var aN = get_atom_rcn( r, gChain, "N")
            var aCa = get_atom_rcn( r, gChain, "CA")
            var aC = get_atom_rcn( r, gChain, "C")
            var aNn = get_atom_rcn( r+1, gChain, "N")
            if (aCp.size < 1) {
                aCp = aNn
            }
            # psi
            if (((r < nres) and (r > ares))
                or ((r == nres) and (nname == "C"))
                or ((r == ares) and (aname != "C"))) {
                if (is_rotor_avail(aCa.atomIndex, aC.atomIndex)) {
                    gNrotors += aNn.atomIndex
                    gNrotors += aC.atomIndex
                    gNrotors += aCa.atomIndex
                    gNrotors += aN.atomIndex
                }
            }
                        
            # phi
            if (aCa.group != "PRO") {
                if (((r <= nres) and (r > ares))
                    or ((r == ares) and (aname == "N"))) {
                    if (is_rotor_avail(aCa.atomIndex, aN.atomIndex)) {
                        gNrotors += aC.atomIndex
                        gNrotors += aCa.atomIndex
                        gNrotors += aN.atomIndex
                        gNrotors += aCp.atomIndex
                    }
                }
            }
        } # endfor
    }
    else {
        gCrotors = array()
        var cres = {atomIndex=gCcargoIdx}.resno
        var cname = {atomIndex=gCcargoIdx}.atomName
        var ares = get_resno_max(gChain)
        var aA = get_atom_rcn( ares, gChain, "C")
        if (gCanchorIdx >= 0) {
            aA = {atomIndex=gCanchorIdx}
        }
        var aname = aA.atomName
        for (var r = cres; r <= ares; r++) {
            var aCp = get_atom_rcn( r-1, gChain, "C")
            var aN = get_atom_rcn( r, gChain, "N")
            var aCa = get_atom_rcn( r, gChain, "CA")
            var aC = get_atom_rcn( r, gChain, "C")
            var aNn = get_atom_rcn( r+1, gChain, "N")
            if (aNn.size < 1) {
                aNn = aCp
            }
            
            # phi
            if (aCa.group != "PRO") {
                if (((r > cres) and (r < ares))
                    or ((r == cres) and (aname == "N"))
                    or ((r == ares) and (aname != "N"))) {
                    if (is_rotor_avail(aN.atomIndex, aCa.atomIndex)) {
                        gCrotors += aCp.atomIndex
                        gCrotors += aN.atomIndex
                        gCrotors += aCa.atomIndex
                        gCrotors += aC.atomIndex
                    }
                }
            }
            
            # psi
            if (((r >= cres) and (r < ares))
                or ((r == cres) and (aname != "C"))
                or ((r == ares) and (aname == "C"))) {
                if (is_rotor_avail(aCa.atomIndex, aC.atomIndex)) {
                    gCrotors += aN.atomIndex
                    gCrotors += aCa.atomIndex
                    gCrotors += aC.atomIndex
                    gCrotors += aNn.atomIndex
                }
            }
        } # endfor
    }
}

function collect_rotors() {
    collect_bb_rotors(false)
    collect_bb_rotors(true)
}

function tug_sc(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 set_colors() {
    select (thisModel)
    color {selected} @gScheme
    color {atomIndex=g1pivotIdx} green
    color {atomIndex=g2pivotIdx} green
    color @gCargoSet @gAltScheme
    color {gCargoSet and oxygen} pink
    select {(atomIndex=gCcargoIdx) or (atomIndex=gNcargoIdx)
        or (atomIndex=gCanchorIdx) or (atomIndex=gNanchorIdx)}
    halo on
    select {atomIndex=gDestAtomIdx}
    star on
    select none
}

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

function timed_out (s) {
    timeout ID"tug" OFF
    refresh
    if (prompt(format("%s - Undo?", s), "Yes|No", true) == "Yes") {
        gBusy = false
        restore state gState
        connect
        select gCargoSet
        set bondPicking true
        refresh
        for (var i = 1; i <= gFreeze.size; i+=2) {
            select {atomIndex=@{gFreeze[i]}} or {atomIndex=@{gFreeze[i+1]}}
            color bonds lightblue
        }
        background ECHO yellow
        echo @gEcho
        select all
        quit
    }
}

function record_drag() {
    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 += "collect_rotors();"
    ls += "tug_drag_done_mb();"
    plico_record(ls)
}

# Pick call-back for freeze
function tug_pick_cb() {
    if (_pickInfo[3][6] == "bond") {
        var sel = {selected}
        var i = _pickInfo.find(":")
        var iChain = _pickInfo[i+1]
        i = _pickInfo.find("#")
        var a1no = 0 + _pickInfo[i+1][i+3]
        var j = _pickInfo[i+1][9999].find("#")
        var a2no = 0 + _pickInfo[i+j+1][i+j+3]
        var i1idx = {(atomno=a1no) and (chain=iChain)
            and thisModel}.atomIndex
        var i2idx = {(atomno=a2no) and (chain=iChain)
            and thisModel}.atomIndex
        if (({atomIndex=i1idx}.atomName == "CA")
            or ({atomIndex=i2idx}.atomName == "CA")) {
            if (({atomIndex=i1idx}.atomName != "CB")
                and ({atomIndex=i2idx}.atomName != "CB")) {
                if (i1idx > i2idx) {
                    var idx = 0 + i1idx
                    i1idx = 0 + i2idx
                    i2idx = 0 + idx
                }
                select {atomIndex=i1idx} or {atomIndex=i2idx}

                for (i = 1; i <= gFreeze.size; i += 2) {
                    if ((gFreeze[i] == i1idx) and (gFreeze[i+1] == i2idx)) {
                        if (i > 1) {
                            if (gFreeze.size = (i+1)) {
                                gFreeze = gFreeze[1][i-1]
                            }
                            else {
                                gFreeze = gFreeze[1][i-1] + gFreeze[i+2][0]
                            }
                        }
                        else {
                            gFreeze = gFreeze[i+2][0]
                        }
                        color bonds NONE
                        i = 0
                        break
                    }
                }
                if (i > gFreeze.size) {
                    gFreeze += i1idx
                    gFreeze += i2idx
                    color bonds lightblue
                }
            }
        }
        select {sel}
    }
}

# Bound to LEFT-UP by tug_enable_drag
function tug_drag_done_mb() {
    if (not gBusy) {
        if (gPlicoRecord != "") {
            record_drag()
        }

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

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

        # Else
        else if (not gTow) {
            gOK = true
            timeout ID"tug" 20.0 "timed_out(\"Tug timed out\")"
            if ((gCrotors.size < gNrotors.size) or (gNanchorIdx < 0)) {
                if (gCrotors.size > 4) {
                    tug_track_c()
                }
                if (gOK and (gNrotors.size > 4)) {
                    tug_track_n()
                }
            }
            else {
                if (gNrotors.size > 4) {
                    tug_track_n()
                }
                if (gOK and (gCrotors.size > 4)) {
                    tug_track_c()
                }
            }
            timeout ID"tug" OFF

            # If anchor angles acute, fail
            if (gOK == true) {
                if (gCanchorIdx >= 0) {
                    var ic = get_cward_bb_idx(gCanchorIdx, gChain)
                    var in = get_nward_bb_idx(gCanchorIdx, gChain)
                    if ((ic >= 0) and
                        angle({atomIndex=ic}, {atomIndex=gCanchorIdx}, {atomIndex=in})
                        < 100.0) {
                        gOK = false
                    }
                }
                if (gNanchorIdx >= 0) {
                    var ic = get_cward_bb_idx(gNanchorIdx, gChain)
                    var in = get_nward_bb_idx(gNanchorIdx, gChain)
                    if ((in >= 0) and
                        angle({atomIndex=ic}, {atomIndex=gNanchorIdx}, {atomIndex=in})
                        < 100.0) {
                        gOK = false
                    }
                }
            }

            # If too far
            if (not gOK) {
                timed_out("TUG TOO FAR!")
            }

            # Else OK
            else {

                var idx = {
                    (atomno=@{{(chain=gChain) and thisModel}.atomno.min})
                    and (chain=gChain) and thisModel}.atomIndex


                var ihc = 0
                for (ihc = 0; ihc < 10; ihc++) {
                    select ((atomno >= gNanchorNo) and (atomno <= gCanchorNo)
                        and (chain = gChain) and thisModel)
                    handle_collisions( idx)
                    if (count_collisions(({})).size == 0) {
                        break
                    }
                }
                if (ihc == 10) {
                    timed_out("Unable to handle all collisions!")
                }
            }
        }
        select {gCargoSet}
        gBusy = false
        background ECHO yellow
        set bondPicking true
        refresh
    }
}

# Bound to ALT-SHIFT-LEFT-DRAG by tug_enable_drag
function tug_drag_2_mb() {
    tug_drag_mb(true)
}

# Bound to ALT-LEFT-DRAG by tug_enable_drag
function tug_drag_mb(alt) {
    if (not gBusy) {
        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 caxis = {0 0 0}
        if (distance(pt,  {0 0 0}) > 0.004) {
            # If sidechain mode
            if (gSCidx >= 0) {
                if ({atomIndex=gSCidx}.atomName == "O") {
                    if ({atomIndex=gSCidx}.group != "PRO") {
                        var dir = ((abs(dx) > abs(dy))
                            ? ((dx < 0) ? 10 : -10)
                            : ((dy < 0) ? 1 : -1))
                        counter_rotate(gSCidx, dir, not alt)
                    }
                }
                else {
                    gSCcheck = not alt
                    tug_sc(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) {
                        caxis = {atomIndex=g2pivotIdx}
                    }

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

                    var dir = ((abs(dx) > abs(dy))
                        ? ((dx < 0) ? 2 : -2)
                        : ((dy < 0) ? 2 : -2))
                    rotate_selected_record(g1pivotIdx, caxis, dir)

                }

                # Else translate it
                else {
                    translate_selected_record(pt)
                }

                # If collisions
                var cNotSels = (within(kCtolerance, false, {selected})
                    and not {gMovingSet})
                if ((cNotSels) and (not alt)) {
                    gOk2 = true
                    for (var i = 1; i <= cNotSels.size;  i++) {

                        # If net collision vector same as move vector
                        var 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
                                        set_distance_idx(cNotSels[i].atomIndex,
                                            cSels[j].atomIndex,
                                            kCtolerance + kDtolerance)
                                    }
                                    else if (g2pivotIdx < 0) {
                                        g2pivotIdx = cSels[j].atomIndex
                                        g2dynamicIdx = cNotSels[i].atomIndex
                                        color {atomIndex=g2pivotIdx} lightgreen
                                        set_distance_idx(cNotSels[i].atomIndex,
                                            cSels[j].atomIndex,
                                            kCtolerance + kDtolerance)
                                    }
                                    else {
                                        gOk2 = false
                                    }
                                }
                                else {

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

                    # If unable
                    if (not gOk2) {

                        # Back off
                        background ECHO pink
                        delay 1
                        if (g1pivotIdx >= 0) {
                            rotate_selected_record(g1pivotIdx, caxis, -a)
                        }
                        else {
                            translate_selected_record(-pt)
                        }
                        background ECHO yellow
                        set bondPicking true
                    }
                }
            }

            # If dynamic pivots
            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 tug_enable_drag
function tug_mark_mb() {
    gMouseX = _mouseX
    gMouseY = _mouseY
    gNewDrag = true
}

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

    # Allow atoms to be dragged
    bind "ALT-LEFT-DOWN" "tug_mark_mb";
    bind "ALT-LEFT-UP" "tug_drag_done_mb";
    bind "ALT-SHIFT-LEFT-DOWN" "tug_mark_mb";
    bind "ALT-LEFT-DRAG" "tug_drag_mb";
    bind "ALT-SHIFT-LEFT-DRAG" "tug_drag_2_mb";

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

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

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

# Bound to SHIFT-LEFT-CLICK by tug_cargo_mb
function tug_anchor_mb() {
    if ({atomIndex=_atomPicked}.chain == gChain) {
        var aPidx = get_sc_bb_idx( _atomPicked, gChain)

        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
            }
            collect_bb_rotors(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
            }
            collect_bb_rotors(true)
        }
        else {
            tow_cargo_mb()
        }

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

# Bound to ALT-SHIFT-LEFT-CLICK by tug_cargo_mb
function tug_dest_atom_mb() {
    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 tug_cargo_mb
function tug_pivot_mb() {
    if (g1pivotIdx == _atomPicked) {
        color {atomIndex=g1pivotIdx} @gScheme
        if (g2pivotIdx >= 0) {
            g1pivotIdx = g2pivotIdx
            g2pivotIdx = -1
            g2dynamicIdx = -1
        }
        else {
            g1pivotIdx = -1
            g1dynamicIdx = -1
        }
    }
    else if (g2pivotIdx == _atomPicked) {
        color {atomIndex=g2pivotIdx} @gScheme
        g2pivotIdx = -1
        g2dynamicIdx = -1
    }
    else if (g1pivotIdx >= 0) {
        if (g2pivotIdx >= 0) {
            color {atomIndex=g2pivotIdx} @gScheme
        }

        g2pivotIdx = _atomPicked
        g2dynamicIdx = -1
        color {atomIndex=g2pivotIdx} green
    }
    else {
        g1pivotIdx = _atomPicked
        g1dynamicIdx = -1
        color {atomIndex=g1pivotIdx} green
    }
    select {gCargoSet}
}

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

    # Highlight cargo cluster
    select {(chain=gChain) and thisModel}
    gCargoSet = {selected}
    gMovingSet = {selected}
    set_colors()

    # Enable dragging
    tug_enable_drag()
    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" "+:tow_cargo_mb";
}

# Bound to ALT-LEFT-CLICK by plico_tug or called by plicotoab.toabCargoMB
function tug_cargo_mb() {

    # If O or movable side chain atom picked
    if ((({atomIndex=_atomPicked}.atomName = "O")
        and ({atomIndex=_atomPicked}.group != "PRO"))
        or (is_moveable_sc( _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) and thisModel}
                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) and thisModel}.atomno.min
        gMaxNo = {(chain=gChain) and thisModel}.atomno.max
        if (gNanchorIdx < 0) {
            gNanchorNo = gMinNo - 1
        }
        if (gCanchorIdx < 0) {
            gCanchorNo = gMaxNo + 1
        }
        var aPidx = get_sc_bb_idx( _atomPicked, gChain)

        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 = get_cp_idx(gNcargoIdx)
                gCcargoNo = {atomIndex=gCcargoIdx}.atomno
            }
            else {
                gCcargoIdx = -1
                gNcargoIdx = -1
            }
        }
        else if (gNcargoIdx == aPidx) {
            select {atomIndex=gNcargoIdx}
            halo off
            gNcargoIdx = get_nm_idx(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 = get_nm_idx(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 = get_cp_idx(aPidx)
                gCcargoNo = {atomIndex=gCcargoIdx}.atomno
            }
        }

        # Else no cWard cargo
        else {

            # Set new cWard cargo and highlight
            gCcargoIdx = get_cp_idx(aPidx)
            gCcargoNo = {atomIndex=gCcargoIdx}.atomno
            gNcargoIdx = get_nm_idx(gCcargoIdx)
            gNcargoNo = {atomIndex=gNcargoIdx}.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
        select_nward_idx(gCcargoIdx, gNcargoIdx)
        gCargoSet = {selected}
        set_colors()

        # Collect the rotor sets
        collect_rotors()

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

    # Enable dragging
    if (gToab) {
        to_ab_enable_drag()
    }
    else {
        tug_enable_drag()
    }

    select {gCargoSet}
}

function tug_undo_mb() {
    if (prompt("Undo", "Yes|No", true) == "Yes") {
        restore state gState
    }
}

# Top level of Tug
function plico_tug() {

    # Load common functions if not already
    if (kCommon < 7) {
        script $SCRIPT_PATH$plicoCommon.spt
        if (kCommon < 7) {
            prompt ("A newer version of plicoCommon.SPT is required")
            quit
        }
    }

    gPlico = "TUG"
    plico_prelim(true, true)
    
    gBondPicking = bondPicking
    set bondPicking true
    set PickCallback "jmolscript:tug_pick_cb"

    gEcho = ("_________TUG_________|ALT-CLICK=mark block|SHIFT-CLICK=mark chain" +
        "|CLICK bond=freeze|ALT-DOUBLE-CLICK=undo|SHIFT-DOUBLE-CLICK=exit")
    echo @gEcho
    gCrotors = array()
    gNrotors = array()
    clear_atom_idxs()
    gToab = false

    bind "ALT-LEFT-CLICK" "_pickAtom";
    bind "ALT-LEFT-CLICK" "+:tug_cargo_mb";
    bind "SHIFT-LEFT-CLICK" "_pickAtom";
    bind "SHIFT-LEFT-CLICK" "+:tow_cargo_mb";
    bind "ALT-LEFT-DOUBLE" "tug_undo_mb";
    bind "SHIFT-DOUBLE" "tug_exit(true)";
    bind "LEFT-CLICK" "+:plico_menu_toggle";
}

# Bound to DOUBLE by plico_tug
function tug_exit() {
    if (plico_exit()) {
        set bondPicking gBondPicking
        set PickCallback NONE
        select all
        color bonds none
        
        reset kTug
        reset gCanchorIdx  
        reset gCanchorNo   
        reset gPlico       
        reset gNanchorIdx  
        reset gNanchorNo   
        reset gCcargoIdx   
        reset gNcargoIdx   
        reset gCcargoNo    
        reset gNcargoNo    
        reset gDestAtomIdx 
        reset g1pivotIdx   
        reset g2pivotIdx   
        reset gSelSaves    
        reset gCrotors     
        reset gNrotors     
        reset gOkCollide   
        reset gChain       
        reset gMinNo       
        reset gMaxNo       
        reset gCargoSet    
        reset gMovingSet   
        reset gSCidx       
        reset gSCcircle    
        reset gSCpt        
        reset gTargetPt    
        reset gNewDrag     
        reset gTow         
        reset g1dynamicIdx 
        reset g2dynamicIdx 
        reset gSCcheck     
        reset gBondPicking 
        reset gFreeze      
        reset gToab        
        reset function "get_cp_idx"
        reset function "get_cm_no"
        reset function "get_nm_idx"
        reset function "get_np_no"
        reset function "get_cb_idx"
        reset function "get_o_idx"
        reset function "get_nward_bb_idx"
        reset function "get_cward_bb_idx"
        reset function "get_sc_set"
        reset function "get_sc_bb_idx"
        reset function "is_bb_idx"
        reset function "is_sc_idx"
        reset function "select_add_sc"
        reset function "handle_collisions"
        reset function "tug_track_idx"
        reset function "do_counter_rotate"
        reset function "counter_rotate"
        reset function "counter_rotate_2"
        reset function "repair_proline"
        reset function "repair_sc"
        reset function "tug_track_c"
        reset function "tug_track_n"
        reset function "translate_selected_record"
        reset function "rotate_selected_record"
        reset function "collect_sc_rotors"
        reset function "drag_sc"
        reset function "fix_sc_collision_2"
        reset function "is_moveable_sc"
        reset function "is_rotor_avail"
        reset function "xcollect_bb_rotors"
        reset function "collect_bb_rotors"
        reset function "collect_rotors"
        reset function "tug_sc"
        reset function "set_colors"
        reset function "clear_atom_idxs"
        reset function "timed_out"
        reset function "record_drag"
        reset function "tug_pick_cb"
        reset function "tug_drag_done_mb"
        reset function "tug_drag_2_mb"
        reset function "tug_drag_mb"
        reset function "tug_mark_mb"
        reset function "tug_anchor_mb"
        reset function "tug_enable_drag"
        reset function "tug_dest_atom_mb"
        reset function "tug_pivot_mb"
        reset function "tow_cargo_mb"
        reset function "tug_cargo_mb"
        reset function "tug_undo_mb"
        reset function "plico_tug"


    }
}

# End of TUG.SPT

Contributors

Remig