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

From Jmol
Jump to navigation Jump to search
(add plico_prelim param)
(Avoid "axis," a newly reserved word)
 
(6 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.  It also allows the user to move an entire chain to nest against another chain.
+
'''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;plico_tug</pre>
+
Script=script <path to your scripts directory>/tug.spt;plico_tug</pre>
 
saved as plicotug.macro in your .jmol/macros directory 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 directory 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.12 beta    5/16/2014 -add plico_prelim param
+
#  v1.20 beta    4/12/2016 -axis is now a reserved word
 
#
 
#
 
#  Translate or rotate a stretch of a polypeptide against itself
 
#  Translate or rotate a stretch of a polypeptide against itself
Line 35: Line 35:
 
gCargoSet = ({})
 
gCargoSet = ({})
 
gMovingSet = ({})
 
gMovingSet = ({})
gBusy = FALSE
+
gBusy = false
 
gSCidx = -1
 
gSCidx = -1
 
gSCcircle = -1
 
gSCcircle = -1
 
gSCpt = {0 0 0}
 
gSCpt = {0 0 0}
 
gTargetPt = {0 0 0}
 
gTargetPt = {0 0 0}
gNewDrag = FALSE
+
gNewDrag = false
gEcho = ""
+
gTow = false
gZoom = ""
 
gRotate = ""
 
gTow = FALSE
 
 
g1dynamicIdx = -1
 
g1dynamicIdx = -1
 
g2dynamicIdx = -1
 
g2dynamicIdx = -1
gSCcheck = TRUE
+
gSCcheck = true
gBondPicking = FALSE
+
gBondPicking = false
 +
gFreeze = array()
 +
gToab = false
  
  
function get_cam_no (iNo) {
+
function get_cp_idx (idx) {
     while ((iNo > 0) and ({(atomno=iNo) and (chain=gChain)}.atomName != "CA")) {
+
    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--
 
         iNo--
 
     }
 
     }
Line 58: Line 67:
 
}
 
}
  
function get_cam_idx (idx) {
+
function get_nm_idx (idx) {
     var no = {atomIndex=idx and (chain=gChain)}.atomno
+
     var no = {atomIndex=idx}.atomno
     no = get_cam_no( no)
+
    while ((no > 0) and ({(atomno=no) and (chain=gChain)
     return ({(atomno=no) and (chain=gChain)}.atomIndex)
+
        and thisModel}.atomName != "N")) {
 +
        no--
 +
     }
 +
     return ({(atomno=no) and (chain=gChain) and thisModel}.atomIndex)
 
}
 
}
  
function get_cap_no (iNo) {
+
function get_np_no (iNo) {
     while ((iNo < gMaxNo) and ({(atomno=iNo) and (chain=gChain)}.atomName != "CA")) {
+
     while ((iNo < gMaxNo) and ({(atomno=iNo) and (chain=gChain)
 +
        and thisModel}.atomName != "N")) {
 
         iNo++
 
         iNo++
 
     }
 
     }
Line 71: Line 84:
 
}
 
}
  
function getCApIdx (idx) {
+
function get_cb_idx (BBidx) {
     var no = {atomIndex=idx and (chain=gChain)}.atomno
+
     var no = {atomIndex=BBidx}.atomno
     no = get_cap_no( no)
+
    var i = 1
     return ({(atomno=no) and (chain=gChain)}.atomIndex)
+
    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_cp_no (iNo) {
+
function get_o_idx (BBidx) {
     while ((iNo < gMaxNo) and ({(atomno=iNo) and (chain=gChain)}.atomName != "C")) {
+
     var no = {atomIndex=BBidx}.atomno
         iNo++
+
    var i = 1
 +
    for (; i < 4; i++) {
 +
        if ({(atomno=@{no+i}) and (chain=gChain)
 +
            and thisModel}.atomName == "O") {
 +
            break
 +
         }
 
     }
 
     }
     return iNo
+
     return {(atomno=@{no+i}) and (chain=gChain) and thisModel}.atomIndex
 
}
 
}
  
function get_cp_idx (idx) {
+
function get_nward_bb_idx (idx, iChain) {
     var no = {atomIndex=idx and (chain=gChain)}.atomno
+
     var no = {atomIndex=idx}.atomno - 1
     no = get_cp_no( no)
+
     for (; no >= 0; no--) {
    return ({(atomno=no) and (chain=gChain)}.atomIndex)
+
        var atomName = {(atomno=no) and (chain=iChain)
}
+
            and thisModel}.atomName
 
+
        if ((atomName = "N") or (atomName = "C") or (atomName = "CA")) {
function get_cm_no (iNo) {
+
            break
    while ((iNo > 0) and ({(atomno=iNo) and (chain=gChain)}.atomName != "C")) {
+
         }
         iNo--
 
 
     }
 
     }
     return iNo
+
     return ((no >= 0) ? ({(atomno=no) and (chain=iChain)
 +
        and thisModel}.atomIndex) : -1)
 
}
 
}
  
function get_cm_idx (idx) {
+
function get_cward_bb_idx (idx, iChain) {
     var no = {atomIndex=idx and (chain=gChain)}.atomno
+
     var no = {atomIndex=idx}.atomno + 1
    no = get_cm_no( no)
+
    for (; no < gMaxNo; no++) {
     return ({(atomno=no) and (chain=gChain)}.atomIndex)
+
        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_nm_no (iNo) {
+
function get_sc_set (scIdx, iChain) {
     while ((iNo > 0) and ({(atomno=iNo) and (chain=gChain)}.atomName != "N")) {
+
     var scSet = ({})
         iNo--
+
    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 iNo
+
     return scSet
 
}
 
}
  
function get_nm_idx (idx) {
+
function get_sc_bb_idx (idx, iChain) {
    var no = {atomIndex=idx and (chain=gChain)}.atomno
 
    no = get_nm_no( no)
 
    return ({(atomno=no) and (chain=gChain)}.atomIndex)
 
}
 
 
 
function get_np_no (iNo) {
 
    while ((iNo < gMaxNo) and ({(atomno=iNo) and (chain=gChain)}.atomName != "N")) {
 
        iNo++
 
    }
 
    return iNo
 
}
 
 
 
function get_np_idx (idx) {
 
 
     var no = {atomIndex=idx}.atomno
 
     var no = {atomIndex=idx}.atomno
     no = get_np_no( no)
+
     for (; no > 0; no--) {
    return ({(atomno=no) and (chain=gChain)}.atomIndex)
+
        if ({(atomno=no) and (chain=iChain)
}
+
            and thisModel}.atomName == "CA") {
 
+
            break
function get_cb_idx (BBidx) {
+
        }
    var no = {atomIndex=BBidx}.atomno
+
        else if ({(atomno=no) and (chain=iChain)
    var i = 1
+
            and thisModel}.atomName == "C") {
    for (; i < 5; i++) {
+
            break
         if ({(atomno=@{no+i}) and (chain=gChain)}.atomName == "CB") {
+
         }
 +
        else if ({(atomno=no) and (chain=iChain)
 +
            and thisModel}.atomName == "N") {
 
             break
 
             break
 
         }
 
         }
    }
+
         else if ({(atomno=no) and (chain=iChain)
    return {(atomno=@{no+i}) and (chain=gChain)}.atomIndex
+
            and thisModel}.atomName == "CB") {
}
+
            no -= 3
 
 
function get_o_idx (BBidx) {
 
    var no = {atomIndex=BBidx}.atomno
 
    var i = 1
 
    for (; i < 4; i++) {
 
         if ({(atomno=@{no+i}) and (chain=gChain)}.atomName == "O") {
 
 
             break
 
             break
 
         }
 
         }
 
     }
 
     }
     return {(atomno=@{no+i}) and (chain=gChain)}.atomIndex
+
     return {(atomno=no) and (chain=iChain) and thisModel}.atomIndex
 
}
 
}
  
function get_nward_bb_no (iNo, iChain) {
+
function is_bb_idx(aIdx) {
     while ((iNo >= 0) and (
+
     var ret = false
        ({(atomno=iNo) and (chain=iChain)}.atomName != "N")
+
    switch({atomIndex=aIdx}.atomName) {
        and ({(atomno=iNo) and (chain=iChain)}.atomName != "C")
+
    case "N":
         and ({(atomno=iNo) and (chain=iChain)}.atomName != "CA"))) {
+
    case "CA":
         iNo--
+
    case "C":
 +
         ret = true
 +
         break
 
     }
 
     }
     return iNo
+
     return ret
 
}
 
}
  
function get_nward_bb_idx (idx, iChain) {
+
function is_sc_idx(aIdx) {
     var no = {atomIndex=idx}.atomno - 1
+
 
     no = get_nward_bb_no( no, iChain)
+
     var ret = false
    return ((no >= 0) ? ({(atomno=no) and (chain=iChain)}.atomIndex) : -1)
+
     if (not is_bb_idx(aIDx)) {
 +
 
 +
        ret = true
 +
        switch({atomIndex=aIdx}.atomName) {
 +
        case "O":
 +
        case "CB":
 +
            ret = false
 +
            break
 +
        }
 +
    }
 +
    return ret
 
}
 
}
  
function get_cward_bb_no (iNo, iChain) {
+
function select_add_sc(fromIdx) {
     while ((iNo < gMaxNo) and (
+
     var iNo = {atomIndex=fromIdx}.atomno
        ({(atomno=iNo) and (chain=iChain)}.atomName != "N")
+
    var iChain = {atomIndex=fromIdx}.chain
        and ({(atomno=iNo) and (chain=iChain)}.atomName != "C")
+
    select none
         and ({(atomno=iNo) and (chain=iChain)}.atomName != "CA"))) {
+
    while ({(atomno=iNo) and (chain=iChain)
 +
        and thisModel and sidechain}) {
 +
         var a = {(atomno=iNo) and (chain=iChain)and thisModel}
 +
        a.selected = true
 
         iNo++
 
         iNo++
 
     }
 
     }
    return iNo
 
 
}
 
}
  
function get_cward_bb_idx (idx, iChain) {
+
# Resolve collisions on selection
     var no = {atomIndex=idx}.atomno + 1
+
function handle_collisions( targetIdx) {
    no = get_cward_bb_no( no, iChain)
+
 
    return ((no >= 0) ? ({(atomno=no) and (chain=iChain)}.atomIndex) : -1)
+
    # 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) {
  
function get_sc_set (scIdx, iChain) {
+
            # Collect local colliders
    var scSet = ({})
+
            var lcAtoms = (within(kCtolerance, false, {atomIndex=idx})
    var idx = get_sc_bb_idx(scIdx, iChain)
+
                and not {atomIndex=idx}
    var iNo = {atomIndex=idx}.atomno + 3
+
                and not {gOkCollide}
   
+
                and not connected({atomIndex=idx}))
    for (var i = 1; i < 20; i++) {
+
            if (lcAtoms) {
        idx = {(atomno=@{iNo+i}) and (chain=iChain)}.atomIndex
+
                # Ignore kinked BB
        if (is_bb_idx(idx)) {
+
                try {
            break
+
                    if (is_bb_idx(idx) and (angle(
        }
+
                        {atomIndex=@{get_cward_bb_idx(idx, gChain)}}, {atomIndex=idx},
        scSet = scSet or {atomIndex=idx}
+
                        {atomIndex=@{get_nward_bb_idx(idx, gChain)}}) < 100.0)) {
    }
+
                        continue
    return scSet
+
                    }
}
+
                }
 +
                catch {
 +
                }
  
function get_sc_bb_idx (idx, iChain) {
+
                # For all local colliders
    var no = {atomIndex=idx}.atomno
+
                for (var c = 1; c <= lcAtoms.size; c++ ) {
    for (; no > 0; no--) {
+
                    var cidx = lcAtoms[c].atomIndex
        if ({(atomno=no) and (chain=iChain)}.atomName == "CA") {
+
 
            break
+
                    # If it is with water, delete it
        }
+
                    if (lcAtoms[c].group = "HOH") {
        else if ({(atomno=no) and (chain=iChain)}.atomName == "C") {
+
                        delete {atomIndex=cidx}
            break
+
                    }
        }
 
        else if ({(atomno=no) and (chain=iChain)}.atomName == "N") {
 
            break
 
        }
 
        else if ({(atomno=no) and (chain=iChain)}.atomName == "CB") {
 
            no -= 3
 
            break
 
        }
 
    }
 
    return {(atomno=no) and (chain=iChain)}.atomIndex
 
}
 
  
function is_bb_idx(aIdx) {
+
                    # else if it is with side chain not proline, fix it
    var ret = FALSE
+
                    else if (is_sc_idx(cidx) and ({atomIndex=cidx}.group != "PRO")) {
    switch({atomIndex=aIdx}.atomName) {
+
                        fix_sc_collision_2(cidx)
    case "N":
+
                       
    case "CA":
+
                        # If not fixed, exit fail
    case "C":
+
                        if (not gOk2) {
        ret = TRUE
+
                            return # early exit (break n jmol bug)
        break
+
                        }
    }
+
                    }
    return ret
+
 
}
+
                    # 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)
  
function is_sc_idx(aIdx) {
+
                        # If not fixed, exit fail
 +
                        if (not gOk2) {
 +
                            return # early exit (break n jmol bug)
 +
                        }
 +
                    }
  
    var ret = FALSE
+
                    # Else if it is with O, counter-rotate
    if (not is_bb_idx(aIDx)) {
+
                    else if (lcAtoms[c].atomName = "O") {
 +
                        counter_rotate_2(lcAtoms[c].atomIndex,
 +
                            {atomIndex=idx}.xyz, targetIdx, false)
  
        ret = TRUE
+
                        # If not fixed, exit fail
        switch({atomIndex=aIdx}.atomName) {
+
                        if (not gOk2) {
        case "O":
+
                            return # early exit (break n jmol bug)
        case "CB":
+
                        }
            ret = FALSE
+
                    }
            break
+
 
        }
+
                    # Else if it is itself O, counter-rotate
    }
+
                    else if ({atomIndex=idx}.atomName = "O") {
    return ret
+
                        counter_rotate_2(idx, lcAtoms[c].xyz, targetIdx, false)
}
+
 
 +
                        # If not fixed, exit fail
 +
                        if (not gOk2) {
 +
                            return # early exit (break n jmol bug)
 +
                        }
 +
                    }
  
function add_sc_to_select(CAno, isAdd, addOXT, iChain) {
+
                    else {    # Else not fixed, exit fail
    var iNo = CAno+3
+
                        gOk2 = false
    while ({(atomno=iNo) and (chain=iChain)}.resno == {(atomno=CAno) and (chain=iChain)}.resno) {
+
                        return # early exit (break n jmol bug)
        {(atomno=iNo) and (chain=iChain)}.selected = isAdd
+
                    }
        if ({(atomno=iNo) and (chain=iChain)}.atomName == "OXT") {
+
                } # endfor
             {(atomno=iNo) and (chain=iChain)}.selected = addOXT
+
             }
 
         }
 
         }
        iNo++
+
     } # endfor iNo
     }
 
 
}
 
}
  
function select_add_sc(fromIdx) {
+
# Rotate rotor set to move target atom to its proper place
     var iNo = {atomIndex=fromIdx}.atomno
+
function tug_track_idx(targetIdx, targetPt, nWard, cDetect) {
     var iChain = {atomIndex=fromIdx}.chain
+
     gOK = false
     select none
+
     var pt = targetPt
    while ({(atomno=iNo) and (chain=iChain)}.atomName != "N") {
+
     var dist = distance(pt, {atomIndex=targetIdx}.xyz)
        {(atomno=iNo) and (chain=iChain)}.selected = TRUE
 
        iNo++
 
        if (iNo > {chain=iChain}.atomno.max) {
 
            break
 
        }
 
    }
 
}
 
  
# First and last are BB atoms
+
     var rotors = (nWard ? gNrotors : gCrotors)
# Any side atoms in the range are also selected
 
function select_nward_idx (firstIdx, lastIdx) {
 
     var firstno = ((firstIdx < 0) ? {atomIndex=lastIdx}.atomno : {atomIndex=firstIdx}.atomno)
 
    var lastno = ((lastIdx < 0) ? firstno : {atomIndex=lastIdx}.atomno)
 
    var iChain = ((firstIdx < 0)
 
        ? {atomIndex=lastIdx}.chain : {atomIndex=firstIdx}.chain)
 
  
     select (atomno <= firstno) and (atomno >= lastno) and (chain = iChain)
+
     # For a number of passes
 +
    for (var pass1 = 0; pass1 < 20; pass1++) {
 +
        var blocked = ({})
 +
        for (var pass2 = 0; pass2 < (rotors.size/4); pass2++) {
  
    if ({(atomno=firstno) and (chain=gChain)}.atomName == "C") { # if psi
+
            var v1 = {atomIndex=targetIdx}.xyz - pt
        add_sc_to_select(firstno-1, TRUE, TRUE, iChain)
 
        {(atomno=@{firstno+1}) and (chain=iChain)}.selected = TRUE # add O
 
    }
 
    if ({(atomno=firstno) and (chain=iChain)}.atomName == "CA") {
 
        add_sc_to_select(firstno, TRUE, FALSE, iChain)
 
    }
 
    if ({(atomno=lastno) and (chain=iChain)}.atomName == "C") { # if psi
 
        add_sc_to_select(lastno-1, FALSE, FALSE, iChain)
 
    }
 
}
 
  
# First and last are BB atoms
+
            # Find the most orthgonal unused rotor
# Any side atoms in the range are also selected
+
            var imax = 0
function select_cward_idx (firstIdx, lastIdx) {
+
            var smax = 0.5
    var firstno = ((firstIdx < 0) ? gMaxNo : {atomIndex=firstIdx}.atomno)
+
            for (var i = 1; i < rotors.size; i += 4) {
    var lastno = ((lastIdx < 0) ? 1 : {atomIndex=lastIdx}.atomno)
+
                var i2 = rotors[i+1]
    var iChain = ((firstIdx < 0)
+
                var i3 = rotors[i+2]
        ? {atomIndex=lastIdx}.chain : {atomIndex=firstIdx}.chain)
+
                var i4 = rotors[i+3]
 
+
                if ((i2 != targetIdx) and (i3 != targetIdx) and (i4 != targetIdx)) {
    # If nWard anchor in range, begin selection with it
+
                    if ({blocked and {atomIndex=i2}}.count == 0) {
    if ((gNanchorIdx >= 0) and ({atomIndex=gNanchorIdx}.chain == iChain){
+
                        var v2 = {atomIndex=i3}.xyz - {atomIndex=i2}.xyz
        var aNo = {atomIndex=gNanchorIdx}.atomno
 
        if (aNo > firstNo) {
 
            firstno = aNo
 
        }
 
    }
 
  
    # If cWard anchor in range, end selection with it
+
                        var s = sin(abs(angle(v1, {0 0 0}, v2)))
    if ((gCanchorIdx >= 0) and ({atomIndex=gCanchorIdx}.chain == iChain)) {
+
                        if (s > smax) {
        var aNo = {atomIndex=gCanchorIdx}.atomno
+
                            smax = s
        if (aNo < lastNo) {
+
                            imax = i
            lastno = aNo
+
                        }
        }
+
                    }
    }
+
                }
 +
            }
  
    select (atomno >= firstno) and (atomno <= lastno) and (chain = iChain)
+
            # 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]
  
    if ({(atomno=firstno) and (chain=iChain)}.atomName == "C") { # if psi
+
            # Get dihedral of rotor with target point
        add_sc_to_select(firstno-1, FALSE, FALSE, iChain)
+
            var dt = angle({atomIndex=targetIdx}, {atomIndex=i2}, {atomIndex=i3}, pt)
    }
+
            var dh = angle({atomIndex=i1}, {atomIndex=i2}, {atomIndex=i3}, {atomIndex=i4})
    if ({(atomno=lastno) and (chain=iChain)}.atomName == "CA") {
+
            if (abs(dh).type == "string") {
        add_sc_to_select(lastno, TRUE, FALSE, iChain)
+
                dh = -50
    }
+
            }
    if ({(atomno=lastno) and (chain=iChain)}.atomName == "C") { # if psi
+
            var psi = dh + dt
        add_sc_to_select(lastno-1, TRUE, TRUE, iChain)
+
            var phi = dh + dt
        {(atomno=@{lastno+1}) and (chain=iChain)}.selected = TRUE # add O
 
    }
 
}
 
  
 +
            # 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
 +
                }
  
# Resolve collisions
+
                if ({atomIndex=i2}.atomno > {atomIndex=targetIdx}.atomno) {
function handle_collisions( targetIdx) {
+
                    movePt = true
 
+
                    select_nward_idx(i3, get_cward_bb_idx(targetIdx, gChain))
    # For all selected atoms
+
                    {atomIndex=targetIdx}.selected = true
    for (var iNo = {selected}.min.atomno; iNo <= {selected}.max.atomno; iNo++) {
+
                }
        var idx = {(atomno=iNo) and (chain=gchain)}.atomIndex
+
                else {
        if ({atomindex=idx}.selected) {
+
                    select_cward_idx(i2, targetIdx)
 
+
                }
            # Collect local colliders
+
            }
            var lcAtoms = (within(kCtolerance, FALSE, {atomIndex=idx})
+
            else {
                and not {atomIndex=idx}
+
                if (({atomIndex=i2}.atomName="CA")) {
                 and not {gOkCollide}
+
                    phi = angle({atomIndex=@{get_nward_bb_idx(i1, gChain)}}, {atomIndex=i1},
                 and not connected({atomIndex=idx}))
+
                        {atomIndex=i2}, {atomIndex=i3}) + dt
            if (lcAtoms.size > 0) {
+
                 }
 +
                 else {
 +
                    psi = angle({atomIndex=i2}, {atomIndex=i3},
 +
                        {atomIndex=i4}, {atomIndex=@{get_cward_bb_idx(i4, gChain)}}) + dt
 +
                }
  
                # Ignore kinked BB
+
                 if ({atomIndex=i2}.atomno < {atomIndex=targetIdx}.atomno) {
                 if (is_bb_idx(idx) and angle({atomIndex=@{get_cward_bb_idx(idx, gChain)}},
+
                    movePt = true
                    {atomIndex=idx} , {atomIndex=@{get_nward_bb_idx(idx, gChain)}}) < 100) {
+
                    select_cward_idx(i3, get_nward_bb_idx(targetIdx, gChain))
                     continue
+
                    {atomIndex=targetIdx}.selected = true
 +
                }
 +
                else {
 +
                     select_nward_idx(i2, targetIdx)
 
                 }
 
                 }
 +
            }
  
                # For all local colliders
+
            # Relax rules if desperate
                for (var c = 1; c <= lcAtoms.size; c++ ) {
+
            if (pass1 > 10) {
                    var cidx = lcAtoms[c].atomIndex
+
                phi = -50
 +
            }
  
                    # If it is with water, delete it
+
            # If rotation within ramachandran limits
                    if (lcAtoms[c].group = "HOH") {
+
            if ((abs(dt) >= 0.1) and
                        delete {atomIndex=cidx}
+
                (({atomIndex=i2}.group=="GLY") or (phi < 0))) {
                    }
 
  
                    # else if it is with side chain not proline, fix it
+
                # If moving target point, put the target atom there
                    else if (is_sc_idx(cidx) and ({atomIndex=cidx}.group != "PRO")) {
+
                var cp = {atomIndex=targetIdx}.xyz
                        fix_sc_collision_2(cidx)
+
                if (movePt) {
                        recollect = TRUE
+
                    dt = -dt
 +
                    {atomIndex=targetIdx}.xyz = pt
 +
                }
  
                        # If not fixed, exit fail
+
                # Rotate to minimize vector ====================
                        if (not gOk2) {
+
                rotateSelected {atomIndex=i2} {atomIndex=i3} @dt
                            return # early exit (break n jmol bug)
 
                        }
 
                    }
 
  
                    # else if it is itself a side chain not proline, fix it
+
                # If collision checking
                    else if (is_sc_idx(idx) and ({atomIndex=idx}.group != "PRO")) {
+
                if (cDetect) {
                        fix_sc_collision_2(idx)
 
                        recollect = TRUE
 
  
                        # If not fixed, exit fail
+
                    # 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) {
 
                         if (not gOk2) {
                             return # early exit (break n jmol bug)
+
                             wasCollision = true
 +
                            rotateSelected {atomIndex=i2} {atomIndex=i3} @{-dt}
 +
                        }
 +
                        else if (wasCollision) {
 +
                            if (ci <3) {
 +
                                rotateSelected {atomIndex=i2} {atomIndex=i3} @{dt}
 +
                            }
 +
                        }
 +
                        else {
 +
                            break
 
                         }
 
                         }
                    }
 
  
                    # Else if it is with O, counter-rotate
+
                        if (dt < 0.01) {
                    else if (lcAtoms[c].atomName = "O") {
+
                            break
                         counter_rotate_2(lcAtoms[c].atomIndex,
+
                         }
                            {atomIndex=idx}.xyz, targetIdx, FALSE)
+
                    } # endfor
 +
                }
  
                        # If not fixed, exit fail
+
                # If moving target point, put the target atom back
                        if (not gOk2) {
+
                if (movePt) {
                            return # early exit (break n jmol bug)
+
                    pt = {atomIndex=targetIdx}.xyz
                        }
+
                    {atomIndex=targetIdx}.xyz = cp
                    }
+
                }
  
                    # 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 close enough, stop
                        if (not gOk2) {
+
            if (distance(pt, {atomIndex=targetIdx}) < kDtolerance) {
                            return # early exit (break n jmol bug)
+
                gOK = true
                        }
+
                gTargetPt = pt
                    }
+
                break
 +
            }
  
                    else {    # Else not fixed, exit fail
+
            # Block rotor
                        gOk2 = FALSE
+
            blocked |= {atomIndex=i2}
                        return # early exit (break n jmol bug)
+
 
                    }
+
        }   # endfor num rotors passes
                } # endfor
+
 
             }
+
        if (gOK) {
 +
             break
 
         }
 
         }
     } # endfor iNo
+
     }   # endfor 20 passes
 
}
 
}
  
# Rotate rotor set to move target atom to its proper place
+
# Counter rotate bonds on either side of a BB O
function tug_track_idx(targetIdx, targetPt, nWard, cDetect) {
+
function do_counter_rotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, nWard) {
    gOK = FALSE
 
    var pt = targetPt
 
    var dist = distance(pt, {atomIndex=targetIdx}.xyz)
 
  
     var rotors = (nWard ? gNrotors : gCrotors)
+
     # Rotate psi
 +
    {atomIndex=nIdx}.selected = nWard
 +
    {atomIndex=cIdx}.selected = nWard
 +
    {atomIndex=oIdx}.selected = nward
 +
    rotateSelected {atomIndex=caPsiIdx} {atomIndex=cIdx} @{dir}
  
     # For a number of passes
+
     # Counter-rotate phi
     for (var pass1 = 0; pass1 < 20; pass1++) {
+
     {atomIndex=nIdx}.selected = not nWard
        var blocked = ({})
+
    {atomIndex=cIdx}.selected = not nWard
        for (var pass2 = 0; pass2 < (rotors.size/4); pass2++) {
+
    {atomIndex=oIdx}.selected = not nward
 +
    rotateSelected {atomIndex=nIdx} {atomIndex=caPhiIdx} @{-dir}
 +
}
  
            var v1 = {atomIndex=targetIdx}.xyz - pt
+
function counter_rotate(oIdx, dir, nWard) {
  
            # Find the most orthgonal unused rotor
+
    var iChain = {atomIndex=oIdx}.chain
            var imax = 0
+
    var selsave = {selected}
            var smax = 0.5
+
    var cIdx = get_sc_bb_idx(oIdx, iChain)
            for (var i = 1; i < rotors.size; i += 4) {
+
    var nIdx = get_cward_bb_idx(cIdx, iChain)
                var i2 = rotors[i+1]
+
    var caPhiIdx = get_cward_bb_idx(nIdx, iChain)
                var i3 = rotors[i+2]
+
    var caPsiIdx = get_nward_bb_idx(cIdx, iChain)
                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 (nWard) {
                        if (s > smax) {
+
        var nNo = {(chain=iChain) and thisModel}.atomno.min
                            smax = s
+
        select_nward_idx(caPsiIdx, {(atomno=nNo) and (chain=iChain)
                            imax = i
+
            and thisModel}.atomIndex)
                        }
+
    }
                    }
+
    else {
                }
+
        var cNo = {(chain=iChain) and thisModel}.atomno.max
            }
+
        select_cward_idx(caPhiIdx, {(atomno=cNo) and (chain=iChain)
 +
            and thisModel}.atomIndex)
 +
    }
  
            # If no more rotors, break to next full try
+
    # Counter-rotate
            if (imax == 0) {
+
    do_counter_rotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, not nWard)
              break
+
    select selsave
            }
+
}
            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
+
function counter_rotate_2(oIdx, toPt, terminalIdx, oDrag) {
            var dt = angle({atomIndex=targetIdx}, {atomIndex=i2}, {atomIndex=i3}, pt)
 
            var dh = angle({atomIndex=i1}, {atomIndex=i2}, {atomIndex=i3}, {atomIndex=i4})
 
            if (dh == "NaN") {
 
                dh = -50
 
            }
 
            var psi = dh + dt
 
            var phi = dh + dt
 
  
            # Compute resultant psi and phi
+
    var iChain = {atomIndex=oIdx}.chain
            #  and select from target atom to first half of rotor
+
    var selsave = {selected}
            var movePt = FALSE
+
    var gOk2 = true
            if (nWard) {
+
    var cIdx = get_sc_bb_idx(oIdx, iChain)
                if ({atomIndex=i2}.atomName="CA") {
+
    var nIdx = get_cward_bb_idx(cIdx, iChain)
                    psi = angle({atomIndex=@{get_cward_bb_idx(i1, gChain)}}, {atomIndex=i1},
+
    var caPhiIdx = get_cward_bb_idx(nIdx, iChain)
                        {atomIndex=i2}, {atomIndex=i3}) + dt
+
    var caPsiIdx = get_nward_bb_idx(cIdx, iChain)
                }
 
                else {
 
                    phi = angle({atomIndex=i1}, {atomIndex=i2},
 
                        {atomIndex=i3}, {atomIndex=@{get_nward_bb_idx(i3, gChain)}}) + dt
 
                }
 
  
                if ({atomIndex=i2}.atomno > {atomIndex=targetIdx}.atomno) {
+
    var nTward = ({atomIndex=oIdx}.atomno < {atomIndex=terminalIdx}.atomno)
                    movePt = TRUE
+
    if (nTward) {
                    select_nward_idx(i3, get_cward_bb_idx(targetIdx, gChain))
+
        select_cward_idx(cIdx, terminalIdx)
                    {atomIndex=targetIdx}.selected = TRUE
+
    }
                }
+
    else {
                else {
+
        select_nward_idx(nIdx, terminalIdx)
                    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) {
+
    # Until all collisions cancelled
                    movePt = TRUE
+
    var dir = 5
                    select_cward_idx(i3, get_nward_bb_idx(targetIdx, gChain))
+
    var ang = angle(toPt, {atomIndex=oIdx}, {atomIndex=cIdx})
                    {atomIndex=targetIdx}.selected = TRUE
+
    var tcount = 0
                }
+
    while (oDrag or (within(kCtolerance, false, {atomIndex=oIdx})
                else {
+
            and not {atomIndex=oIdx} and not connected({atomIndex=oIdx})
                    select_nward_idx(i2, targetIdx)
+
             and not {gOkCollide} > 0)) {
                }
 
             }
 
  
            # Relax rules if desperate
+
        # Counter-rotate
            if (pass1 > 10) {
+
        do_counter_rotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, nTward)
                phi = -50
+
        var newang = angle(toPt, {atomIndex=oIdx}, {atomIndex=cIdx})
            }
 
  
            # If rotation within ramachandran limits
+
        # If wrong direction once, undo and reverse
            if ((abs(dt) >= 0.1) and
+
        if (newang > ang) {
                (({atomIndex=i2}.group=="GLY") or (phi < 0))) {
+
            do_counter_rotate(caPhiIDx, nIdx, cIdx, oIdx, caPsiIdx, -dir, nTward)
  
                # If moving target point, put the target atom there
+
            # If first time, continue in opposite direction
                var cp = {atomIndex=targetIdx}.xyz
+
            dir *= -1
                if (movePt) {
+
            if (dir < 0) {
                    dt = -dt
+
                continue
                    {atomIndex=targetIdx}.xyz = pt
+
            }
                }
+
        }
  
                # Rotate to minimize vector ====================
+
        if (oDrag) {
                rotateSelected {atomIndex=i2} {atomIndex=i3} @dt
+
            break
 +
        }
  
                # If collision checking
+
        # If no go, undo and exit
                if (cDetect) {
+
        tcount++
 +
        if (tcount > (360/abs(dir))) {
 +
            gOk2 = false
 +
            break
 +
        }
  
                    # If collision, back off by eighths
+
    } # endwhile
                    var wasCollision = FALSE
+
    select selsave
                    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) {
+
# Repair proline
                            break
+
function repair_proline(BBidx) {
                        }
+
    var cbidx = get_cb_idx(BBidx)
                    } # endfor
+
    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
  
                # If moving target point, put the target atom back
+
    select {atomIndex=cbidx}
                if (movePt) {
+
    set_angle_idx(nidx, caidx, cbidx, 109.5)
                    pt = {atomIndex=targetIdx}.xyz
 
                    {atomIndex=targetIdx}.xyz = cp
 
                }
 
  
            }
+
    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)
  
            # If close enough, stop
+
    select {atomIndex=cgidx}
            if (distance(pt, {atomIndex=targetIdx}) < kDtolerance) {
+
    set_distance_idx(cdidx, cgidx, 1.51)
                gOK = TRUE
+
    set_angle_idx(nidx, cdidx, cgidx, 106.4)
                gTargetPt = pt
+
    set_dihedral_idx(caidx, nidx, cdidx, cgidx, 16.2)
                break
+
}
            }
 
  
            # Block rotor
+
# Repair side chain
            blocked |= {atomIndex=i2}
+
function repair_sc(targetIdx, nWard) {
  
        }  # endfor num rotors passes
+
    var idx = (nWard ? get_cward_bb_idx(targetIdx, gChain) : get_nward_bb_idx(targetIdx, gChain))
  
         if (gOK) {
+
    if (({atomIndex=targetIdx}.atomName == "CA")
             break
+
        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)
 +
                }
 +
            }
 
         }
 
         }
    }  # endfor 20 passes
+
        else {
}
+
            if (nWard) {
 
+
            }
# Counter rotate bonds on either side of a BB O
+
            else {
function do_counter_rotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, nWard) {
+
                set_dihedral_idx(get_nward_bb_idx(idx, gChain), idx, targetIdx, cbidx, 174.2)
 +
            }
 +
        }
 +
    }
  
     # Rotate psi
+
     else if ({atomIndex=targetIdx}.atomName == "C") {
    {atomIndex=nIdx}.selected = nWard
+
        var oidx = get_o_idx(targetIdx)
    {atomIndex=cIdx}.selected = nWard
+
        select {atomIndex=oidx}
    {atomIndex=oIdx}.selected = nward
+
        set_angle_idx(idx, targetIdx, oidx, 120.0)
    rotateSelected {atomIndex=caPsiIdx} {atomIndex=cIdx} @{dir}
+
        set_distance_idx(targetIdx, oidx, 1.21)
 
+
        if (nWard) {
    # Counter-rotate phi
+
            set_dihedral_idx(get_cward_bb_idx(idx, gChain), idx, targetIdx, oidx, 0.0)
    {atomIndex=nIdx}.selected = not nWard
+
        }
    {atomIndex=cIdx}.selected = not nWard
+
        if ({atomIndex=idx}.group == "PRO") {
    {atomIndex=oIdx}.selected = not nward
+
            repair_proline(idx)
    rotateSelected {atomIndex=nIdx} {atomIndex=caPhiIdx} @{-dir}
+
            var dNo = {atomIndex=targetIdx}.atomno + 4
}
+
            var dIdx = {(atomno=dNO) and (chain=gChain)
 
+
                and thisModel}.atomIndex
function counter_rotate(oIdx, dir, nWard) {
+
            var colliders = (within(kCtolerance, false, {atomIndex=dIdx})
 
+
                and not connected({atomIndex=dIdx})
    var iChain = {atomIndex=oIdx}.chain
+
                and not {atomIndex=dIdx})
    var selsave = {selected}
+
            for (var i = 1; i <= colliders.size; i++) {
    var cIdx = get_sc_bb_idx(oIdx, iChain)
+
                if (colliders[i].atomName == "O") {
    var nIdx = get_cward_bb_idx(cIdx, iChain)
+
                    counter_rotate_2(colliders[i].atomIndex,
    var caPhiIdx = get_cward_bb_idx(nIdx, iChain)
+
                        {atomIndex=dIdx}.xyz, targetIdx, false)
    var caPsiIdx = get_nward_bb_idx(cIdx, iChain)
+
                }
 
+
            }
    if (nWard) {
+
         }
        nNo = {chain=iChain}.atomno.min
 
         select_nward_idx(caPsiIdx, {(atomno=nNo) and (chain=iChain)}.atomIndex)
 
 
     }
 
     }
    else {
 
        cNo = {chain=iChain}.atomno.max
 
        select_cward_idx(caPhiIdx, {(atomno=cNo) and (chain=iChain)}.atomIndex)
 
    }
 
 
    # Counter-rotate
 
    do_counter_rotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, not nWard)
 
    select selsave
 
 
}
 
}
  
function counter_rotate_2(oIdx, toPt, terminalIdx, oDrag) {
+
# Rebuild Cward rotors set
 +
function tug_track_c() {
  
     var iChain = {atomIndex=oIdx}.chain
+
     # For all bb atoms cWard of cargo
    var selsave = {selected}
+
     var targetIdx = gCcargoIdx
    var gOk2 = TRUE
+
     var okCount = 0
    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)
+
     # Allow collisions with cargo
    if (nTward) {
+
     gOkCollide = gCargoSet
        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
 
     var tcount = 0
     while (oDrag or (within(kCtolerance, FALSE, {atomIndex=oIdx})
+
     while (targetIdx != gNanchorIdx) {
            and not {atomIndex=oIdx} and not connected({atomIndex=oIdx})
 
            and not {gOkCollide} > 0)) {
 
  
         # Counter-rotate
+
         # Step to next atom
         do_counter_rotate(caPhiIdx, nIdx, cIdx, oIdx, caPsiIdx, dir, nTward)
+
         targetIdx = get_cward_bb_idx(targetIdx, gChain)
        var newang = angle(toPt, {atomIndex=oIdx}, {atomIndex=cIdx})
+
         if (targetIdx < 0) {
 
+
             break
        # 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) {
+
        # No collision with cargo allowed after two atoms placed
            break
+
         if (tcount == 2) {
 +
          gOkCollide = ({})
 
         }
 
         }
 
        # If no go, undo and exit
 
 
         tcount++
 
         tcount++
        if (tcount > (360/abs(dir))) {
 
            gOk2 = FALSE
 
            break
 
        }
 
  
     } # endwhile
+
        # Compute targets desired coords
     select selsave
+
        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 {
 +
        }
  
# Repair proline
+
        # If successful
function repair_proline(BBidx) {
+
        if (gOK == true) {
    var cbidx = get_cb_idx(BBidx)
 
    var cbno = {atomIndex=cbidx}.atomno
 
    var cgidx = {(atomno=@{cbno+1}) and (chain=gChain)}.atomIndex
 
    var cdidx = {(atomno=@{cbno+2}) and (chain=gChain)}.atomIndex
 
    var caidx = {(atomno=@{cbno-3}) and (chain=gChain)}.atomIndex
 
    var nidx = {(atomno=@{cbno-4}) and (chain=gChain)}.atomIndex
 
  
    select {atomIndex=cbidx}
+
            # Adust any side atoms
    set_angle_idx(nidx, caidx, cbidx, 109.5)
+
            repair_sc(targetIdx, false)
 +
        }
  
    select {atomIndex=cdidx}
+
        # Else fail
    set_distance_idx(nidx, cdidx, 1.47)
+
        else {
    set_angle_idx(caidx, nidx, cdidx, 102.7)
+
            break
    set_dihedral_idx(cbidx, caidx, nidx, cdidx, 16.2)
+
        }
  
    select {atomIndex=cgidx}
+
        # If no movement in 4 tries, we are done
    set_distance_idx(cdidx, cgidx, 1.51)
+
        if (okCount > 3) {
    set_angle_idx(nidx, cdidx, cgidx, 106.4)
+
            break
     set_dihedral_idx(caidx, nidx, cdidx, cgidx, 16.2)
+
        }
 +
     } # endwhile (targetIdx != gCanchorIdx) {
 
}
 
}
  
# Repair side chain
+
# Rebuild Nward rotors set
function repair_sc(targetIdx, nWard) {
+
function tug_track_n() {
 +
 
 +
    gOK = true
  
     var idx = (nWard ? get_cward_bb_idx(targetIdx, gChain) : get_nward_bb_idx(targetIdx, gChain))
+
    # For all bb atoms nWard of cargo
 +
    var targetIdx = gNcargoIdx
 +
     var okCount = 0
  
     if (({atomIndex=targetIdx}.atomName == "CA")
+
     # Allow collisions with cargo
        and ({atomIndex=targetIdx}.group != "GLY")) {
+
    gOkCollide = gCargoSet
        var cbidx = get_cb_idx(targetIdx)
+
    var tcount = 0
        select none
+
    while (targetIdx != gNanchorIdx) {
         select_add_sc(cbidx)
+
 
         set_angle_idx(idx, targetIdx, cbidx, 110.0)
+
         # Step to next atom
        set_distance_idx(targetIdx, cbidx, 1.5)
+
         targetIdx = get_nward_bb_idx(targetIdx, gChain)
         if ({atomIndex=targetIdx}.group != "PRO") {
+
         if (targetIdx < 0) {
            var colliders = (within(kCtolerance, FALSE, {selected})
+
             break
                and not {atomIndex=targetIdx} and not {selected})
 
            if (colliders.size > 0) {
 
                if ({atomIndex=targetIdx}.group != "ALA") {
 
                    fix_sc_collision_2(cbidx)
 
                }
 
             }
 
 
         }
 
         }
         else {
+
          
            if (nWard) {
+
        # No collision with cargo allowed after two atoms placed
            }
+
        if (tcount == 2) {
            else {
+
          gOkCollide = ({})
                set_dihedral_idx(get_nward_bb_idx(idx, gChain), idx, targetIdx, cbidx, 174.2)
 
            }
 
 
         }
 
         }
    }
+
        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") {
  
    else if ({atomIndex=targetIdx}.atomName == "C") {
+
            # Desired target location is trigonal O
        var oidx = get_o_idx(targetIdx)
+
            var oidx = get_o_idx(c1idx)
        select {atomIndex=oidx}
+
            select {atomIndex=oidx}
        set_angle_idx(idx, targetIdx, oidx, 120.0)
+
            set_distance_idx(c1idx, oidx, 1.39)
        set_distance_idx(targetIdx, oidx, 1.21)
+
            pt = get_trigonal_idx(c2idx, c1idx, oidx, 1.41)
        if (nWard) {
+
             set_distance_idx(c1idx, oidx, 1.21)
             set_dihedral_idx(get_cward_bb_idx(idx, gChain), idx, targetIdx, oidx, 0.0)
 
 
         }
 
         }
         if ({atomIndex=idx}.group == "PRO") {
+
         else if (({atomIndex=targetIdx}.atomName == "N")
             repair_proline(idx)
+
             and ({atomIndex=targetIdx}.group != "GLY")) {
            var dNo = {atomIndex=targetIdx}.atomno + 4
+
 
            var dIdx = {(atomno=dNO) and (chain=gChain)}.atomIndex
+
            # Desired target location is r-tetragonal CB
            var colliders = (within(kCtolerance, FALSE, {atomIndex=dIdx})
+
             var cbidx = get_cb_idx(c1idx)
                and not connected({atomIndex=dIdx})
+
            pt = get_tet_idx(cbidx, c1idx, c2idx, 1.5)
                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)
 
                }
 
            }
 
 
         }
 
         }
    }
+
        else { # C
}
 
  
# Rebuild Cward rotors set
+
            # Save current target coords
function tug_track_c() {
+
            var cp = {atomIndex=targetIdx}.xyz
  
    # For all bb atoms cWard of cargo
+
            # Set target atom at desired distance and angle
    var targetIdx = gCcargoIdx
+
            select {atomIndex=targetIdx}
    var okCount = 0
+
            set_distance_idx(c1idx, targetIdx, 1.37)
 +
            set_angle_idx(c2idx, c1idx, targetIdx, 110.0)
  
    # Allow collisions with cargo
+
             if ({atomIndex=targetIdx}.group == "PRO") {
    gOkCollide = gCargoSet
+
                 set_dihedral_idx(c3idx, c2idx, c1idx, targetIdx, -57.0)
    var tcount = 0
 
    while (targetIdx != gCanchorIdx) {
 
 
 
        # Step to next atom
 
        targetIdx = get_cward_bb_idx(targetIdx, gChain)
 
 
 
        # No collision with cargo allowed after two atoms placed
 
        if (tcount == 2) {
 
          gOkCollide = ({})
 
        }
 
        tcount++
 
 
 
        # Compute targets desired coords
 
        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)
 
 
             }
 
             }
  
Line 819: Line 823:
 
         # If target not at desired location
 
         # If target not at desired location
 
         if (distance(pt, {atomIndex=targetIdx}) > kDtolerance) {
 
         if (distance(pt, {atomIndex=targetIdx}) > kDtolerance) {
             okCount = 0
+
             var okCount = 0
 
             gTargetPt = pt
 
             gTargetPt = pt
 
             var xcount = 0
 
             var xcount = 0
             gOK = FALSE
+
             gOK = false
 
             while ((xcount < 20) and (not gOK)) {
 
             while ((xcount < 20) and (not gOK)) {
  
                 # Rotate on cWard rotor set to move it there
+
                 # Rotate on nWard rotor set to move it there
                 tug_track_idx(targetIdx, pt, FALSE, FALSE)
+
                 tug_track_idx(targetIdx, pt, true, false)
 
                 xcount++
 
                 xcount++
 
             }
 
             }
 
         }
 
         }
 
         else {
 
         else {
             gOK = TRUE
+
             gOK = true
 
             okCount++
 
             okCount++
 
         }
 
         }
  
         # If successful
+
         # If sucessful
         if (gOK == TRUE) {
+
         if (gOK == true) {
  
 
             # Adust any side atoms
 
             # Adust any side atoms
             repair_sc(targetIdx, FALSE)
+
             repair_sc(targetIdx, true)
 
         }
 
         }
  
Line 851: Line 855:
 
             break
 
             break
 
         }
 
         }
     } # endwhile (targetIdx != gCanchorIdx) {
+
 
 +
     }   # endwhile (targetIdx != gNanchorIdx) {
 
}
 
}
  
# Rebuild Nward rotors set
+
# gPlicoRecord is maintained by the macro pilcoRecord
function tug_track_n() {
+
function translate_selected_record(pt) {
 +
    if (gPlicoRecord != "") {
 +
        plico_record(format("select %s;translateSelected %s;", {selected}, pt))
 +
    }
 +
    translateSelected @pt
 +
}
  
     gOK = TRUE
+
# 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
 +
}
  
     # For all bb atoms nWard of cargo
+
function collect_sc_rotors(no, iChain) {
     var targetIdx = gNcargoIdx
+
     var scBondIdxs = array()
    var okCount = 0
+
     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
 +
        }
  
     # Allow collisions with cargo
+
     }
    gOkCollide = gCargoSet
+
 
     var tcount = 0
+
     return scBondIdxs
    while (targetIdx != gNanchorIdx) {
+
}
  
        # Step to next atom
+
# Drag Side Chain
        targetIdx = get_nward_bb_idx(targetIdx, gChain)
+
function drag_sc() {
 +
    var iNo = {atomIndex=gSCidx}.atomno
 +
    var iChain  = {atomIndex=gSCidx}.chain
 +
    if ({atomIndex=gSCidx}.group != "PRO") {
  
         # No collision with cargo allowed after two atoms placed
+
         var scBondIdxs = collect_sc_rotors( iNo, iChain)
         if (tcount == 2) {
+
         var numChi = scBondIdxs.size / 4
          gOkCollide = ({})
+
        var dist = distance({atomIndex=gSCidx}.xyz, gSCpt)
         }
+
         var scSet = get_sc_set(gSCidx, iChain)
        tcount++
 
  
         # Compute targets desired coords
+
         # For all rotor combinations
         var n1idx = get_nward_bb_idx(targetIdx, gChain)
+
         var dh = array()
         var c1idx = get_cward_bb_idx(targetIdx, gChain)
+
         for (var i = 0; i < numChi; i++) {
        var c2idx = get_cward_bb_idx(c1idx, gChain)
+
            dh += angle({atomIndex=@{scBondIdxs[4+(4*i)]}},
        var c3idx = get_cward_bb_idx(c2idx, gChain)
+
                {atomIndex=@{scBondIdxs[3+(4*i)]}},
        var pt = {0 0 0}
+
                {atomIndex=@{scBondIdxs[2+(4*i)]}},
        if ({atomIndex=targetIdx}.atomName == "CA") {
+
                {atomIndex=@{scBondIdxs[1+(4*i)]}})
 
 
            # 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")
+
         for (var i = 0; i < numChi; i++) {
            and ({atomIndex=targetIdx}.group != "GLY")) {
+
            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
 +
                    }
 +
                }
  
            # Desired target location is r-tetragonal CB
+
                # Find the best
            var cbidx = get_cb_idx(c1idx)
+
                if (newDist < dist) {
            pt = get_tet_idx(cbidx, c1idx, c2idx, 1.5)
+
                    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)]}})
 +
                    }
 +
                }
 +
            }
 
         }
 
         }
        else { # C
 
  
             # Save current target coords
+
        # Now set the best
             var cp = {atomIndex=targetIdx}.xyz
+
        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}
  
            # Set target atom at desired distance and angle
+
        if (angle({atomIndex=ica}, {atomIndex=in},
             select {atomIndex=targetIdx}
+
             {atomIndex=icd}, {atomIndex=gSCidx}) < -10.0) {
             set_distance_idx(c1idx, targetIdx, 1.37)
+
            set_dihedral_idx(ica, in, icd, gSCidx, 8.7)
             set_angle_idx(c2idx, c1idx, targetIdx, 110.0)
+
            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)
 +
        }
 +
    }
  
            if ({atomIndex=targetIdx}.group == "PRO") {
+
    draw gSCcircle CIRCLE {atomIndex=gSCidx} MESH NOFILL
                set_dihedral_idx(c3idx, c2idx, c1idx, targetIdx, -57.0)
+
    gSCpt = {atomIndex=gSCidx}.xyz
            }
+
}
  
            # Record and restore target
+
# Fix side chain collisions
            pt = {atomIndex=targetIdx}.xyz
+
function fix_sc_collision_2(idx) {
            {atomIndex=targetIdx}.xyz = cp
+
    gOk2 = false
        }
+
    var iNo = {atomIndex=idx}.atomno
 
+
    var iChain = {atomIndex=idx}.chain
        # If target not at desired location
+
    var resno = {(atomno=iNo) and (chain=iChain)
        if (distance(pt, {atomIndex=targetIdx}) > kDtolerance) {
+
        and thisModel}.resno
            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
+
    # Get SC terminus
                tug_track_idx(targetIdx, pt, TRUE, FALSE)
+
    while (resno == {(atomno=iNo) and (chain=iChain)
                xcount++
+
         and thisModel}.resno) {
            }
+
         iNo++
         }
+
    }
         else {
+
    iNo--
            gOK = TRUE
 
            okCount++
 
        }
 
  
         # If sucessful
+
    var sc = array()
         if (gOK == TRUE) {
+
    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
  
            # Adust any side atoms
+
    var scBondIdxs = collect_sc_rotors( iNo, iChain)
            repair_sc(targetIdx, TRUE)
+
    var numChi = scBondIdxs.size / 4
        }
 
  
         # Else fail
+
    # For all rotor combinations
         else {
+
    for (var i = 0; i < numChi; i++) {
             break
+
         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 movement in 4 tries, we are done
+
            # If no collision, exit
        if (okCount > 3) {
+
            var colliders = (within(kCtolerance, false, {sc})
            break
+
                and not {atomIndex=cbidx} and not {sc})
        }
 
  
    }  # endwhile (targetIdx != gNanchorIdx) {
+
            # 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]}}
 +
                }
 +
            }
  
# gPlicoRecord is maintained by the macro pilcoRecord
+
            if (colliders.size == 0) {
function translate_selected_record(pt) {
+
                gOk2 = true
    if (gPlicoRecord != "") {
+
                return # Early exit since break 1 appears broken
         plico_record(format("select %s;translateSelected %s;", {selected}, pt))
+
            }
 +
 
 +
         }
 
     }
 
     }
    translateSelected @pt
 
 
}
 
}
  
# gPlicoRecord is maintained by the macro pilcoRecord
+
function is_moveable_sc(aIdx) {
function rotate_selected_record(pivotIdx, axis, a) {
+
 
     if (gPlicoRecord != "") {
+
     var ret = (({atomIndex=aIdx}.group != "PRO")
         plico_record(format("select %s;", {selected}))
+
         or ({atomIndex=aIdx}.atomName == "CG"))
        plico_record(format("rotateSelected {atomIndex=%d} @%s @%s;",
+
    switch({atomIndex=aIdx}.atomName) {
            pivotIdx, axis, a))
+
    case "N":
 +
    case "CA":
 +
    case "C":
 +
    case "CB":
 +
    case "O":
 +
    case "O4\'":
 +
        ret = false
 +
        break
 
     }
 
     }
     rotateSelected {atomIndex=pivotIdx} @axis @a
+
     return ret
 
}
 
}
  
function collect_sc_rotors(no, iChain) {
+
function is_rotor_avail(i1idx, i2idx) {
     var scBondIdxs = array()
+
     var ret = true
     for (var iNo = no; iNo >= 0; iNo--) {
+
    if (i1idx > i2idx) { # frozen bonds are kept as lo-hi
         var ile = 0
+
        var idx = @i1idx
        switch ({(atomno=iNo) and (chain=iChain)}.atomName) {
+
        i1idx = @i2idx
        case "CA" :
+
        i2idx = @idx
             return scBondIdxs # Early exit since break 1 appears broken
+
    }
         case "CZ" :
+
 
            if ({(atomno=iNo) and (chain=iChain)}.group == "TYR") {
+
     for (var i = 1; i <= gFreeze.size; i += 2) {
                break
+
         if ((gFreeze[i] == i1idx) and (gFreeze[i+1] == i2idx)) {
            }
+
            ret = false
         case "CE" :
+
             break
            if ({(atomno=iNo) and (chain=iChain)}.group == "MET") {
+
         }
                break
+
    }
            }
+
 
         case "CG1" :
+
    return ret
             if ({(atomno=iNo) and (chain=iChain)}.group == "VAL") {
+
}
                 break
+
 
            }
+
function xcollect_bb_rotors(nWard) {
            if ({(atomno=iNo) and (chain=iChain)}.group == "ILE") {
+
    var anchorNo = (nWard
                ile = 1
+
        ? ((gNanchorIdx >= 0) ? {atomIndex=gNanchorIdx}.atomno : gMinNo)
            }
+
        : ((gCanchorIdx >= 0) ? {atomIndex=gCanchorIdx}.atomno : gMaxNo))
        case "NE" :
+
    var cargoNo = (nWard
        case "CD" :
+
        ? ((gNcargoIdx >= 0) ? {atomIndex=gNcargoIdx}.atomno
        case "SD" :
+
         : {atomIndex=gCcargoIdx}.atomno)
        case "CG" :
+
        : {atomIndex=gCcargoIdx}.atomno)
        case "CB" :
+
    var rotors = array()
            scBondIdxs += {(atomno=@{iNo+1+ile}) and (chain=iChain)}.atomIndex
+
    if (cargoNo < anchorNo) {
            scBondIdxs += {(atomno=@{iNo+0}) and (chain=iChain)}.atomIndex
+
 
            if ({(atomno=iNo) and (chain=iChain)}.atomName%2 == "CG") {
+
         for (var iNo = cargoNo; iNo <= anchorNo; iNo++) {
                scBondIdxs += {(atomno=@{iNo-1}) and (chain=iChain)}.atomIndex
+
             if ({(atomno=iNo) and (chain=gChain)
                scBondIdxs += {(atomno=@{iNo-4}) and (chain=iChain)}.atomIndex
+
                and thisModel}.atomName == "CA") {
            }
+
                 if (is_rotor_avail(iNo)) {# xxx
            else if ({(atomno=iNo) and (chain=iChain)}.atomName == "CB") {
+
                    if (({(atomno=iNo) and (chain=gChain)
                scBondIdxs += {(atomno=@{iNo-3}) and (chain=iChain)}.atomIndex
+
                        and thisModel}.group != "PRO") and (iNo > cargoNo)) { # phi
                scBondIdxs += {(atomno=@{iNo-4}) and (chain=iChain)}.atomIndex
+
                        rotors += [{(atomno=@{get_cm_no(iNo-1)}) and (chain=gChain)
            }
+
                            and thisModel}.atomIndex,
            else {
+
                            {(atomno=@{iNo-1}) and (chain=gChain)
                scBondIdxs += {(atomno=@{iNo-1}) and (chain=iChain)}.atomIndex
+
                                and thisModel}.atomIndex]
                scBondIdxs += {(atomno=@{iNo-2}) and (chain=iChain)}.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]
 +
                    }
 +
                }
 
             }
 
             }
            break
 
 
         }
 
         }
 
 
     }
 
     }
 +
    else {
  
     return scBondIdxs
+
        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) {
# Drag Side Chain
+
     if (nWard) {
function drag_sc() {
+
         gNrotors = array()
 
+
         var nres = {atomIndex=gNcargoIdx}.resno
    var iNo = {atomIndex=gSCidx}.atomno
+
         var nname = {atomIndex=gNcargoIdx}.atomName
    var iChain  = {atomIndex=gSCidx}.chain
+
        var ares = get_resno_min(gChain)
 
+
         var aA = get_atom_rcn( ares, gChain, "N")
     if ({atomIndex=gSCidx}.group != "PRO") {
+
         if (gNanchorIdx >= 0) {
 
+
             aA = {atomIndex=gNanchorIdx}
         var scBondIdxs = collect_sc_rotors( iNo, iChain)
 
         var numChi = scBondIdxs.size / 4
 
         var dist = distance({atomIndex=gSCidx}.xyz, gSCpt)
 
 
 
         var scSet = ({})
 
         if (gSCcheck) {
 
             scSet = get_sc_set(gSCidx, iChain)
 
 
         }
 
         }
       
+
         var aname = aA.atomName
        # For all rotor combinations
+
         for (var r = nres; r >= ares; r--) {
         var dh = array()
+
             var aCp = get_atom_rcn( r-1, gChain, "C")
         for (var i = 0; i < numChi; i++) {
+
            var aN = get_atom_rcn( r, gChain, "N")
             dh += angle({atomIndex=@{scBondIdxs[4+(4*i)]}},
+
            var aCa = get_atom_rcn( r, gChain, "CA")
                {atomIndex=@{scBondIdxs[3+(4*i)]}},
+
            var aC = get_atom_rcn( r, gChain, "C")
                {atomIndex=@{scBondIdxs[2+(4*i)]}},
+
            var aNn = get_atom_rcn( r+1, gChain, "N")
                {atomIndex=@{scBondIdxs[1+(4*i)]}})
+
             if (aCp.size < 1) {
        }
+
                 aCp = aNn
        for (var i = 0; i < numChi; i++) {
+
            }
             var rot = -120
+
            # psi
            for (var j = 0; j < 6; j++) {
+
            if (((r < nres) and (r > ares))
                 rot += 60*j
+
                 or ((r == nres) and (nname == "C"))
                select_add_sc(scBondIdxs[1+(4*i)])
+
                or ((r == ares) and (aname != "C"))) {
                 set_dihedral_idx(scBondIdxs[4+(4*i)], scBondIdxs[3+(4*i)],
+
                 if (is_rotor_avail(aCa.atomIndex, aC.atomIndex)) {
                    scBondIdxs[2+(4*i)], scBondIdxs[1+(4*i)], rot)
+
                    gNrotors += aNn.atomIndex
                 var newDist = distance({atomIndex=gSCidx}.xyz, gSCpt)
+
                    gNrotors += aC.atomIndex
                 if (gSCcheck) {
+
                    gNrotors += aCa.atomIndex
                     var colliders = (within(kCtolerance, FALSE, scSet)
+
                    gNrotors += aN.atomIndex
                        and not connected(scSet) and not {scSet})
+
                }
                     if (colliders.size > 0) {
+
            }
                         continue
+
                       
 +
            # 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
 
                     }
 
                     }
 
                 }
 
                 }
               
+
            }
                # Find the best
+
        } # endfor
                if (newDist < dist) {
+
    }
                    dist = newDist
+
    else {
                    for (var k = 0; k < numChi; k++) {
+
        gCrotors = array()
                        dh[k+1] = angle({atomIndex=@{scBondIdxs[4+(4*k)]}},
+
        var cres = {atomIndex=gCcargoIdx}.resno
                        {atomIndex=@{scBondIdxs[3+(4*k)]}},
+
        var cname = {atomIndex=gCcargoIdx}.atomName
                        {atomIndex=@{scBondIdxs[2+(4*k)]}},
+
        var ares = get_resno_max(gChain)
                         {atomIndex=@{scBondIdxs[1+(4*k)]}})
+
        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
        # Now set the best
+
            if (((r >= cres) and (r < ares))
        for (var i = 0; i < numChi; i++) {
+
                or ((r == cres) and (aname != "C"))
            select_add_sc(scBondIdxs[1+(4*i)])
+
                or ((r == ares) and (aname == "C"))) {
            set_dihedral_idx(scBondIdxs[4+(4*i)], scBondIdxs[3+(4*i)],
+
                 if (is_rotor_avail(aCa.atomIndex, aC.atomIndex)) {
                 scBondIdxs[2+(4*i)], scBondIdxs[1+(4*i)], dh[i+1])
+
                    gCrotors += aN.atomIndex
         }
+
                    gCrotors += aCa.atomIndex
 +
                    gCrotors += aC.atomIndex
 +
                    gCrotors += aNn.atomIndex
 +
                }
 +
            }
 +
         } # endfor
 
     }
 
     }
    else { # PRO - toggle between puckers up and down
+
}
        var icd = {(atomno=@{iNo+1}) and (chain=iChain)}.atomIndex
 
        var icb = {(atomno=@{iNo-1}) and (chain=iChain)}.atomIndex
 
        var ica = {(atomno=@{iNo-4}) and (chain=iChain)}.atomIndex
 
        var in = {(atomno=@{iNo-5}) and (chain=iChain)}.atomIndex
 
        select {atomIndex=gSCidx}
 
  
        if (angle({atomIndex=ica}, {atomIndex=in},
+
function collect_rotors() {
            {atomIndex=icd}, {atomIndex=gSCidx}) < -10.0) {
+
    collect_bb_rotors(false)
             set_dihedral_idx(ica, in, icd, gSCidx, 8.7)
+
    collect_bb_rotors(true)
            set_angle_idx(in, icd, gSCidx, 110.0)
+
}
            set_distance_idx(icd, gSCidx, 1.5)
+
 
 +
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 {
 
         else {
             set_dihedral_idx(ica, in, icd, gSCidx, -29.5)
+
             pt = v/20.0
            set_angle_idx(in, icd, gSCidx, 108.8)
 
            set_distance_idx(icd, gSCidx, 1.5)
 
 
         }
 
         }
 
     }
 
     }
 
+
    gSCpt += pt
     draw gSCcircle CIRCLE {atomIndex=gSCidx} MESH NOFILL
+
     draw arrow {atomIndex=gSCidx} @gSCpt
    gSCpt = {atomIndex=gSCidx}.xyz
 
 
}
 
}
  
# Fix side chain collisions
+
function set_colors() {
function fix_sc_collision_2(idx) {
+
     select (thisModel)
     gOk2 = FALSE
+
    color {selected} @gScheme
     var iNo = {atomIndex=idx}.atomno
+
     color {atomIndex=g1pivotIdx} green
     var iChain = {atomIndex=idx}.chain
+
     color {atomIndex=g2pivotIdx} green
     var resno = {(atomno=iNo) and (chain=iChain)}.resno
+
    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
 +
}
  
     # Get SC terminus
+
function clear_atom_idxs() {
     while (resno == {(atomno=iNo) and (chain=iChain)}.resno) {
+
     gCcargoIdx = -1
        iNo++
+
     gNcargoIdx = -1
     }
+
    gCanchorIdx = -1
     iNo--
+
    gNanchorIdx = -1
 +
    g1pivotIdx = -1
 +
    g2pivotIdx = -1
 +
    g1dynamicIdx = -1
 +
     g2dynamicIdx = -1
 +
     gDestAtomIdx = -1
 +
    gSCidx = -1
 +
}
  
    var sc = array()
+
function timed_out (s) {
     var iBno = iNo
+
     timeout ID"tug" OFF
     while ({(atomno=iBno) and (chain=iChain)}.atomName != "CB") {
+
    refresh
         sc += {(atomno=iBno) and (chain=iChain)}
+
     if (prompt(format("%s - Undo?", s), "Yes|No", true) == "Yes") {
         iBno--
+
         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
 
     }
 
     }
    var cbidx = {(atomno=iBno) and (chain=iChain)}.atomIndex
+
}
  
     var scBondIdxs = collect_sc_rotors( iNo, iChain)
+
function record_drag() {
     var numChi = scBondIdxs.size / 4
+
     var ls = format("select %s;", {selected})
 
+
     ls += format("gCanchorIdx = %d;", gCanchorIdx)
     # For all rotor combinations
+
    ls += format("gCanchorNo = %d;", gCanchorNo)
     for (var i = 0; i < numChi; i++) {
+
     ls += format("gNanchorIdx = %d;", gNanchorIdx)
        var rot = -120
+
     ls += format("gNanchorNo = %d;", gNanchorNo)
        for (var j = 0; j < 6; j++) {
+
    ls += format("gCcargoIdx = %d;", gCcargoIdx)
            rot += 60
+
    ls += format("gNcargoIdx = %d;", gNcargoIdx)
            select_add_sc(scBondIdxs[1+(4*i)])
+
    ls += format("gCcargoNo = %d;", gCcargoNo)
            set_dihedral_idx(scBondIdxs[1+(4*i)], scBondIdxs[2+(4*i)],
+
    ls += format("gNcargoNo = %d;", gNcargoNo)
                scBondIdxs[3+(4*i)], scBondIdxs[4+(4*i)], rot)
+
    ls += format("gDestAtomIdx = %d;", gDestAtomIdx)
               
+
    ls += format("g1pivotIdx = %d;", g1pivotIdx)
            # If no collision, exit
+
    ls += format("g2pivotIdx = %d;", g2pivotIdx)
            colliders = (within(kCtolerance, FALSE, {sc})
+
    ls += format("gOkCollide = %s;", gOkCollide)
                and not {atomIndex=cbidx} and not {sc})
+
    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)
 +
}
  
            # If it is with water, delete the water
+
# Pick call-back for freeze
            for (var c = 1; c < colliders.size; c++ ) {
+
function tug_pick_cb() {
                if (colliders[c].group = "HOH") {
+
    if (_pickInfo[3][6] == "bond") {
                    delete {atomIndex=@{colliders[c].atomIndex}}
+
        var sel = {selected}
                     colliders = {colliders and not @{colliders[c]}}
+
        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}
  
            if (colliders.size == 0) {
+
                for (i = 1; i <= gFreeze.size; i += 2) {
                gOk2 = TRUE
+
                    if ((gFreeze[i] == i1idx) and (gFreeze[i+1] == i2idx)) {
                 return # Early exit since break 1 appears broken
+
                        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}
 +
     }
 
}
 
}
  
function is_moveable_sc(aIdx) {
+
# Bound to LEFT-UP by tug_enable_drag
 +
function tug_drag_done_mb() {
 +
    if (not gBusy) {
 +
        if (gPlicoRecord != "") {
 +
            record_drag()
 +
        }
  
    var ret = (({atomIndex=aIdx}.group != "PRO")
+
        # Move by rotation on rotor sets, smallest first
         or ({atomIndex=aIdx}.atomName == "CG"))
+
         gBusy = true
    switch({atomIndex=aIdx}.atomName) {
+
         background ECHO pink
    case "N":
+
         refresh
    case "CA":
 
    case "C":
 
    case "CB":
 
    case "O":
 
    case "O4\'":
 
         ret = FALSE
 
         break
 
    }
 
    return ret
 
}
 
  
function is_rotor_avail(i1idx, i2idx) {
+
        # If side chain mode
    var ret = TRUE
+
        if (gSCidx >= 0) {
    if (i1idx > i2idx) {
+
            drag_sc()
        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 collect_bb_rotors(nWard) {
+
         # Else
    var anchorNo = (nWard
+
         else if (not gTow) {
         ? ((gNanchorIdx >= 0) ? {atomIndex=gNanchorIdx}.atomno : gMinNo)
+
            gOK = true
         : ((gCanchorIdx >= 0) ? {atomIndex=gCanchorIdx}.atomno : gMaxNo))
+
            timeout ID"tug" 20.0 "timed_out(\"Tug timed out\")"
    var cargoNo = (nWard
+
            if ((gCrotors.size < gNrotors.size) or (gNanchorIdx < 0)) {
        ? ((gNcargoIdx >= 0) ? {atomIndex=gNcargoIdx}.atomno
+
                 if (gCrotors.size > 4) {
        : {atomIndex=gCcargoIdx}.atomno)
+
                    tug_track_c()
        : {atomIndex=gCcargoIdx}.atomno)
+
                }
    var rotors = array()
+
                if (gOK and (gNrotors.size > 4)) {
    if (cargoNo < anchorNo) {
+
                    tug_track_n()
 
 
        for (var iNo = cargoNo; iNo <= anchorNo; iNo++) {
 
            if ({(atomno=iNo) and (chain=gChain)}.atomName == "CA") {
 
                 if (is_rotor_avail(iNo)) {
 
                    if (({(atomno=iNo) and (chain=gChain)}.group != "PRO") and (iNo > cargoNo)) { # phi
 
                        rotors += [{(atomno=@{get_cm_no(iNo-1)}) and (chain=gChain)}.atomIndex,
 
                            {(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex]
 
                        rotors += [{(atomno=@{iNo}) and (chain=gChain)}.atomIndex,
 
                            {(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex]
 
                    }
 
                    if (iNo != (anchorNo-1)) { # psi
 
                        rotors += [{(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex,
 
                            {(atomno=@{iNo}) and (chain=gChain)}.atomIndex]
 
                        rotors += [{(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex,
 
                            {(atomno=@{get_np_no(iNo+2)}) and (chain=gChain)}.atomIndex]
 
                    }
 
 
                 }
 
                 }
 
             }
 
             }
        }
+
            else {
    }
+
                if (gNrotors.size > 4) {
    else {
+
                    tug_track_n()
 +
                }
 +
                if (gOK and (gCrotors.size > 4)) {
 +
                    tug_track_c()
 +
                }
 +
            }
 +
            timeout ID"tug" OFF
  
        for (var iNo = cargoNo; iNo >= anchorNo; iNo--) {
+
            # If anchor angles acute, fail
             if ({(atomno=iNo) and (chain=gChain)}.atomName == "CA") {
+
             if (gOK == true) {
                 if (is_rotor_avail(iNo)) {
+
                 if (gCanchorIdx >= 0) {
                     if ((iNo != (anchorNo-1)) and (iNo < cargoNo)) { # psi
+
                     var ic = get_cward_bb_idx(gCanchorIdx, gChain)
                        rotors += [{(atomno=@{get_np_no(iNo+2)}) and (chain=gChain)}.atomIndex,
+
                    var in = get_nward_bb_idx(gCanchorIdx, gChain)
                            {(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex]
+
                    if ((ic >= 0) and
                         rotors += [{(atomno=@{iNo}) and (chain=gChain)}.atomIndex,
+
                         angle({atomIndex=ic}, {atomIndex=gCanchorIdx}, {atomIndex=in})
                            {(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex]
+
                        < 100.0) {
 +
                        gOK = false
 
                     }
 
                     }
                    if ({(atomno=iNo) and (chain=gChain)}.group != "PRO") { # phi
+
                }
                        rotors += [{(atomno=@{iNo+1}) and (chain=gChain)}.atomIndex,
+
                if (gNanchorIdx >= 0) {
                            {(atomno=@{iNo}) and (chain=gChain)}.atomIndex]
+
                    var ic = get_cward_bb_idx(gNanchorIdx, gChain)
                         rotors += [{(atomno=@{iNo-1}) and (chain=gChain)}.atomIndex,
+
                    var in = get_nward_bb_idx(gNanchorIdx, gChain)
                            {(atomno=@{get_cm_no(iNo-1)}) and (chain=gChain)}.atomIndex]
+
                    if ((in >= 0) and
 +
                         angle({atomIndex=ic}, {atomIndex=gNanchorIdx}, {atomIndex=in})
 +
                        < 100.0) {
 +
                        gOK = false
 
                     }
 
                     }
 
                 }
 
                 }
 
             }
 
             }
        }
 
    }
 
  
    if (nWard) {
+
            # If too far
        gNrotors = rotors
+
            if (not gOK) {
    }
+
                timed_out("TUG TOO FAR!")
    else {
+
            }
        gCrotors = rotors
 
    }
 
}
 
  
function collect_rotors() {
+
            # Else OK
    collect_bb_rotors(FALSE)
+
            else {
    collect_bb_rotors(TRUE)
 
}
 
  
function tug_sc(pt) {
+
                var idx = {
 +
                    (atomno=@{{(chain=gChain) and thisModel}.atomno.min})
 +
                    and (chain=gChain) and thisModel}.atomIndex
  
    # 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() {
+
                var ihc = 0
    select all
+
                for (ihc = 0; ihc < 10; ihc++) {
    color {selected} @gScheme
+
                    select ((atomno >= gNanchorNo) and (atomno <= gCanchorNo)
    color {atomIndex=g1pivotIdx} green
+
                        and (chain = gChain) and thisModel)
    color {atomIndex=g2pivotIdx} green
+
                    handle_collisions( idx)
    color @gCargoSet @gAltScheme
+
                    if (count_collisions(({})).size == 0) {
    select {(atomIndex=gCcargoIdx) or (atomIndex=gNcargoIdx)
+
                        break
        or (atomIndex=gCanchorIdx) or (atomIndex=gNanchorIdx)}
+
                    }
    halo on
+
                }
    select {atomIndex=gDestAtomIdx}
+
                if (ihc == 10) {
    star on
+
                    timed_out("Unable to handle all collisions!")
     select none
+
                }
 +
            }
 +
        }
 +
        select {gCargoSet}
 +
        gBusy = false
 +
        background ECHO yellow
 +
        set bondPicking true
 +
        refresh
 +
     }
 
}
 
}
  
function clear_atom_idxs() {
+
# Bound to ALT-SHIFT-LEFT-DRAG by tug_enable_drag
     gCcargoIdx = -1
+
function tug_drag_2_mb() {
    gNcargoIdx = -1
+
     tug_drag_mb(true)
    gCanchorIdx = -1
 
    gNanchorIdx = -1
 
    g1pivotIdx = -1
 
    g2pivotIdx = -1
 
    g1dynamicIdx = -1
 
    g2dynamicIdx = -1
 
    gDestAtomIdx = -1
 
    gSCidx = -1
 
 
}
 
}
  
function timed_out (s) {
+
# Bound to ALT-LEFT-DRAG by tug_enable_drag
     timeout ID"tug" OFF
+
function tug_drag_mb(alt) {
    p = prompt(format("%s - Undo?", s), "Yes|No", TRUE)
+
     if (not gBusy) {
    if (p == "Yes") {
+
        gBusy = true
        gBusy = FALSE
+
        var dx = (40.0 * (_mouseX - gMouseX))/_width
        background ECHO yellow
+
        var dy = (40.0 * (_mouseY - gMouseY))/_height
        restore state gState
+
        var q = quaternion()
        connect
+
        var ptd = {@dx @dy 0}
        select gCargoSet
+
        var pt = (!q)%ptd
        refresh
+
        var caxis = {0 0 0}
        quit
+
        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
 +
                }
  
function record_drag() {
+
                # If destination atom defined
    var ls = format("select %s;", {selected})
+
                if (gDestAtomIdx >= 0) {
    ls += format("gCanchorIdx = %d;", gCanchorIdx)
+
                    var v = {atomIndex=gDestAtomIdx}.xyz - {selected}.xyz
    ls += format("gCanchorNo = %d;", gCanchorNo)
+
                    if (abs(angle({atomIndex=gDestAtomIdx}.xyz, {0 0 0}, pt)) < 90) {
    ls += format("gNanchorIdx = %d;", gNanchorIdx)
+
                        pt = -v/20.0
    ls += format("gNanchorNo = %d;", gNanchorNo)
+
                    }
    ls += format("gCcargoIdx = %d;", gCcargoIdx)
+
                    else {
    ls += format("gNcargoIdx = %d;", gNcargoIdx)
+
                        pt = v/20.0
    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
+
                # Move the cargo
function tug_pick_cb() {
+
                select {gCargoSet}
 +
 
 +
                # If pivots defined, rotate it
 +
                if (g1pivotIdx >= 0) {
  
    if (_pickInfo[3][6] == "bond") {
+
                    # If two pivots
        var sel = {selected}
+
                    if (g2pivotIdx >= 0) {
        var i = _pickInfo.find(":")
+
                        caxis = {atomIndex=g2pivotIdx}
        var iChain = _pickInfo[i+1]
+
                    }
        i = _pickInfo.find("#")
+
 
        var a1no = _pickInfo[i+1][i+3]
+
                    # Else
        j = _pickInfo[i+1][9999].find("#")
+
                    else {
        var a2no = _pickInfo[i+j+1][i+j+3]
+
                        caxis = cross(pt, {0 0 0}) + {atomIndex=g1pivotIdx}.xyz
        var i1idx = {(atomno=a1no) and (chain=iChain)}.atomIndex
 
        var i2idx = {(atomno=a2no) and (chain=iChain)}.atomIndex
 
       
 
        if (({atomIndex=i1idx}.atomName == "CA")
 
            or ({atomIndex=i2idx}.atomName == "CA")) {
 
            if (({atomIndex=i1idx}.atomName != "CB")
 
                and ({atomIndex=i2idx}.atomName != "CB")) {
 
                if (i1idx > i2idx) {
 
                    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)) {
 
                        gFreeze[i] == -1
 
                        color bonds NONE
 
                        break
 
 
                     }
 
                     }
                }
 
                if (i > gFreeze.size) {
 
                    gFreeze += i1idx
 
                    gFreeze += i2idx
 
                    color bonds lightblue
 
                }
 
            }
 
        }
 
        select {sel}
 
    }
 
}
 
  
# Bound to LEFT-UP by tug_enable_drag
+
                    var dir = ((abs(dx) > abs(dy))
function tug_drag_done_mb() {
+
                        ? ((dx < 0) ? 2 : -2)
    if (not gBusy) {
+
                        : ((dy < 0) ? 2 : -2))
        if (gPlicoRecord != "") {
+
                    rotate_selected_record(g1pivotIdx, caxis, dir)
            record_drag()
 
        }
 
  
        # Move by rotation on rotor sets, smallest first
+
                }
        gBusy = TRUE
 
        background ECHO pink
 
        refresh
 
  
        # If side chain mode
+
                # Else translate it
        if (gSCidx >= 0) {
+
                else {
            drag_sc()
+
                    translate_selected_record(pt)
        }
+
                }
  
        # Else
+
                # If collisions
        else if (not gTow) {
+
                var cNotSels = (within(kCtolerance, false, {selected})
            gOK = TRUE
+
                     and not {gMovingSet})
            timeout ID"tug" 20.0 "timed_out(\"Tug timed out\")"
+
                 if ((cNotSels) and (not alt)) {
            if ((gCrotors.size < gNrotors.size) or (gNanchorIdx < 0)) {
+
                     gOk2 = true
                if (gCrotors.size > 4) {
+
                     for (var i = 1; i <= cNotSels.size;  i++) {
                     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 net collision vector same as move vector
            if (gOK == TRUE) {
+
                        var cSels = (within(kCtolerance, false, cNotSels[i]) and {selected})
                if (gCanchorIdx >= 0) {
+
                         for (var j = 1; j <= cSels.size;  j++) {
                    var ic = get_cward_bb_idx(gCanchorIdx, gChain)
+
                            var v1 = cNotSels[i].xyz - cSels[j].xyz
                    var in = get_nward_bb_idx(gCanchorIdx, gChain)
+
                            if (abs(angle(v1, {0 0 0}, pt)) < 90) {
                    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 tow mode
            if (not gOK) {
+
                                if (gTow) {
                timed_out("TUG TOO FAR!")
 
            }
 
  
            # Else OK
+
                                    # Make a dynamic pivot
            else {
+
                                    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 {
  
                select ((atomno >= gNanchorNo) and (atomno <= gCanchorNo)
+
                                    # Try to resolve
                    and (chain = gChain))
+
                                    select @{cSels[j]}
                var idx = {(atomno=@{{chain=gChain}.atomno.min})
+
                                    var idx = {(atomno=@{{(chain=gChain)
                    and (chain=gChain)}.atomIndex
+
                                        and thisModel}.atomno.min})
 
+
                                        and (chain=gChain) and thisModel}.atomIndex
 
+
                                    handle_collisions( idx)
                var ihc = 0
+
                                }
                for (ihc = 0; ihc < 10; ihc++) {
+
                            }
                     handle_collisions( idx)
+
                        } # endfor
                    if (count_collisions(({})).size == 0) {
+
                        if (not gOk2) {
                         break
+
                            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 (ihc == 10) {
 
                    timed_out("Unable to handle all collisions!")
 
 
                 }
 
                 }
 
             }
 
             }
         }
+
 
 +
            # 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}
 
         select {gCargoSet}
         gBusy = FALSE
+
         gBusy = false
        background ECHO yellow
 
        refresh
 
 
     }
 
     }
 
}
 
}
  
# Bound to ALT-SHIFT-LEFT-DRAG by tug_enable_drag
+
# Bound to ALT-LEFT-DOWN by tug_enable_drag
function tug_drag_2_mb() {
+
function tug_mark_mb() {
     tug_drag_mb(TRUE)
+
     gMouseX = _mouseX
 +
    gMouseY = _mouseY
 +
    gNewDrag = true
 
}
 
}
  
# Bound to ALT-LEFT-DRAG by tug_enable_drag
+
# Called by tug_cargo_mb
function tug_drag_mb(alt) {
+
function tug_enable_drag() {
     if (not gBusy) {
+
     gEcho = "__________TUG__________|ALT-CLICK=mark block|SHIFT-CLICK=anchors" +
        gBusy = TRUE
+
         "|ALT-CTRL-CLICK=pivots|ALT-SHIFT-CLICK=dest atom|ALT-DRAG=move" +
        var dx = (40.0 * (_mouseX - gMouseX))/_width
+
         "|ALT-SHIFT-DRAG=alt move|CLICK bond=freeze|ALT-DOUBLE-CLICK=undo" +
        var dy = (40.0 * (_mouseY - gMouseY))/_height
+
        "|SHIFT-DOUBLE-CLICK=exit"
        var q = quaternion()
+
    echo @gEcho
         var ptd = {@dx @dy 0}
 
        var pt = (!q)%ptd
 
        var axis = {0 0 0}
 
         if (distance(pt,  {0 0 0}) > 0.004) {
 
            # If sidechain mode
 
            if (gSCidx >= 0) {
 
                if ({atomIndex=gSCidx}.atomName == "O") {
 
                    dir = ((abs(dx) > abs(dy))
 
                        ? ((dx < 0) ? 10 : -10)
 
                        : ((dy < 0) ? 1 : -1))
 
                    counter_rotate(gSCidx, dir, not alt)
 
                }
 
                else {
 
                    gSCcheck = not alt
 
                    tug_sc(pt)
 
                }
 
            }
 
  
            # Else
+
    # Allow atoms to be dragged
            else {
+
    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";
  
                # If new drag
+
    unbind "SHIFT-LEFT-CLICK"
                if (gNewDrag) {
+
    bind "SHIFT-LEFT-CLICK" "_pickAtom";
                    gNewDrag = FALSE
+
    bind "SHIFT-LEFT-CLICK" "+:tug_anchor_mb";
                    save state gState
 
                }
 
  
                # If destination atom defined
+
    bind "ALT-CTRL-LEFT-CLICK" "_pickAtom";
                if (gDestAtomIdx >= 0) {
+
    bind "ALT-CTRL-LEFT-CLICK" "+:tug_pivot_mb";
                    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
+
    bind "ALT-SHIFT-LEFT-CLICK" "_pickAtom";
                select {gCargoSet}
+
    bind "ALT-SHIFT-LEFT-CLICK" "+:tug_dest_atom_mb";
 +
}
  
                # If pivots defined, rotate it
+
# Bound to SHIFT-LEFT-CLICK by tug_cargo_mb
                if (g1pivotIdx >= 0) {
+
function tug_anchor_mb() {
 +
    if ({atomIndex=_atomPicked}.chain == gChain) {
 +
        var aPidx = get_sc_bb_idx( _atomPicked, gChain)
  
                    # If two pivots
+
        var pno = {atomIndex=aPidx}.atomno
                    if (g2pivotIdx >= 0) {
+
        if (pno > {atomIndex=gCcargoIdx}.atomno) {
                        axis = {atomIndex=g2pivotIdx}
+
            select {atomIndex=gCanchorIdx}
                    }
+
            halo off
 
+
            if (gCanchorIdx == aPidx) {
                    # Else
+
                gCanchorIdx = -1
                    else {
+
                gCanchorNo = gMaxNo + 1
                        axis = cross(pt, {0 0 0}) + {atomIndex=g1pivotIdx}.xyz
+
            }
                    }
+
            else {
 
+
                gCanchorIdx = aPidx
                    dir = ((abs(dx) > abs(dy))
+
                gCanchorNo = {atomIndex=gCanchorIdx}.atomno
                        ? ((dx < 0) ? 2 : -2)
+
                select {atomIndex=gCanchorIdx}
                        : ((dy < 0) ? 2 : -2))
+
                halo on
                    rotate_selected_record(g1pivotIdx, axis, dir)
+
            }
 +
            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}
 +
}
  
                # Else translate it
+
# Bound to ALT-SHIFT-LEFT-CLICK by tug_cargo_mb
                else {
+
function tug_dest_atom_mb() {
                    translate_selected_record(pt)
+
    var aOk = true
                }
+
    if ({atomIndex=_atomPicked}.chain == gChain) {
  
                # If collisions
+
        var pno = {atomIndex=_atomPicked}.atomno
                var cNotSels = (within(kCtolerance, FALSE, {selected})
+
        if ((pno <= {atomIndex=gCcargoIdx}.atomno)
                    and not {gMovingSet})
+
            and (pno >= {atomIndex=gNcargoIdx}.atomno)) {
                if ((cNotSels.size > 0) and (not alt)) {
+
            aOk = false
                    gOk2 = TRUE
+
        }
                    for (var i = 1; i <= cNotSels.size;  i++) {
+
    }
 +
    if (aOk) {
 +
        select {atomIndex=gDestAtomIdx}
 +
        star off
 +
        if (gDestAtomIdx == _atomPicked) {
 +
            gDestAtomIdx = -1
 +
        }
 +
        else {
 +
            gDestAtomIdx = _atomPicked
 +
            select {atomIndex=gDestAtomIdx}
 +
            star on
 +
        }
 +
        select {gCargoSet}
 +
    }
 +
}
  
                        # If net collision vector same as move vector
+
# Bound to CTRL-LEFT-CLICK by tug_cargo_mb
                        cSels = (within(kCtolerance, FALSE, cNotSels[i]) and {selected})
+
function tug_pivot_mb() {
                        for (var j = 1; j <= cSels.size;  j++) {
+
    if (g1pivotIdx == _atomPicked) {
                            var v1 = cNotSels[i].xyz - cSels[j].xyz
+
        color {atomIndex=g1pivotIdx} @gScheme
                            if (abs(angle(v1, {0 0 0}, pt)) < 90) {
+
        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
 +
        }
  
                                # If tow mode
+
        g2pivotIdx = _atomPicked
                                if (gTow) {
+
        g2dynamicIdx = -1
 +
        color {atomIndex=g2pivotIdx} green
 +
    }
 +
    else {
 +
        g1pivotIdx = _atomPicked
 +
        g1dynamicIdx = -1
 +
        color {atomIndex=g1pivotIdx} green
 +
    }
 +
    select {gCargoSet}
 +
}
  
                                    # Make a dynamic pivot
+
# Bound to SHIFT-LEFT-CLICK by plico_tug
                                    if (g1pivotIdx < 0) {
+
function tow_cargo_mb() {
                                        g1pivotIdx = cSels[j].atomIndex
+
    gTow = true
                                        g1dynamicIdx = cNotSels[i].atomIndex
+
    gChain = {atomIndex=_atomPicked}.chain
                                        color {atomIndex=g1pivotIdx} lightgreen
+
    gMinNo = {(chain=gChain) and thisModel}.atomno.min
                                        set_distance_idx(cNotSels[i].atomIndex,
+
    gMaxNo = {(chain=gChain) and thisModel}.atomno.max
                                            cSels[j].atomIndex,
+
    gCcargoIdx = -1
                                            kCtolerance + kDtolerance)
+
    gNcargoIdx = -1
                                    }
+
    gCanchorIdx = -1
                                    else if (g2pivotIdx < 0) {
+
    gCanchorNo = gMaxNo + 1
                                        g2pivotIdx = cSels[j].atomIndex
+
    gNanchorIdx = -1
                                        g2dynamicIdx = cNotSels[i].atomIndex
+
    gNanchorNo = gMinNo - 1
                                        color {atomIndex=g2pivotIdx} lightgreen
 
                                        set_distance_idx(cNotSels[i].atomIndex,
 
                                            cSels[j].atomIndex,
 
                                            kCtolerance + kDtolerance)
 
                                    }
 
                                    else {
 
                                        gOk2 = FALSE
 
                                    }
 
                                }
 
                                else {
 
  
                                    # Try to resolve
+
    # Highlight cargo cluster
                                    select cSels[j]
+
    select {(chain=gChain) and thisModel}
                                    var idx = {(atomno=@{{chain=gChain}.atomno.min})
+
    gCargoSet = {selected}
                                        and (chain=gChain)}.atomIndex
+
    gMovingSet = {selected}
                                    handle_collisions( idx)
+
    set_colors()
                                }
 
                            }
 
                        } # endfor
 
                        if (not gOk2) {
 
                            break
 
                        }
 
                    } # endfor
 
  
                    # If unable
+
    # Enable dragging
                    if (not gOk2) {
+
    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";
 +
}
  
                        # Back off
+
# Bound to ALT-LEFT-CLICK by plico_tug or called by plicotoab.toabCargoMB
                        background ECHO pink
+
function tug_cargo_mb() {
                        delay 1
 
                        if (g1pivotIdx >= 0) {
 
                            rotate_selected_record(g1pivotIdx, axis, -a)
 
                        }
 
                        else {
 
                            translate_selected_record(-pt)
 
                        }
 
                        background ECHO yellow
 
                    }
 
                }
 
            }
 
  
            # If dynamic pivots
+
    # If O or movable side chain atom picked
            if (g1dynamicIdx >= 0) {
+
    if ((({atomIndex=_atomPicked}.atomName = "O")
                var v1 = {atomIndex=g1dynamicIdx}.xyz - {atomIndex=g1pivotIdx}.xyz
+
        and ({atomIndex=_atomPicked}.group != "PRO"))
                if (abs(angle(v1, {0 0 0}, pt)) > 90) {
+
        or (is_moveable_sc( _atomPicked))) {
                    color {atomIndex=g1pivotIdx} @gAltScheme
+
        if (gSCidx >= 0) {
                    g1pivotIdx = -1
+
            draw gSCcircle DELETE
                    g1dynamicIdx = -1
+
        }
 
+
        if (gSCidx == _atomPicked) {
                }
+
            gSCidx = -1
            }
+
        }
            if (g2dynamicIdx >= 0) {
+
        else {
                var v1 = {atomIndex=g2dynamicIdx}.xyz - {atomIndex=g2pivotIdx}.xyz
+
             gSCidx = _atomPicked
                if (abs(angle(v1, {0 0 0}, pt)) > 90) {
+
             gSCpt = {atomIndex=gSCidx}.xyz
                    color {atomIndex=g2pivotIdx} @gAltScheme
+
             draw gSCcircle CIRCLE {atomIndex=gSCidx} MESH NOFILL
                    g2pivotIdx = -1
 
                    g2dynamicIdx = -1
 
                }
 
             }
 
 
 
             gMouseX = _mouseX
 
             gMouseY = _mouseY
 
 
         }
 
         }
        select {gCargoSet}
 
        gBusy = FALSE
 
 
     }
 
     }
}
+
    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
  
# Bound to ALT-LEFT-DOWN by tug_enable_drag
+
        # If existing cWard cargo picked
function tug_mark_mb() {
+
        if (gCcargoIdx == aPidx) {
    gMouseX = _mouseX
 
    gMouseY = _mouseY
 
    gNewDrag = TRUE
 
}
 
  
# Called by tug_cargo_mb
+
            # Clear the highlight
function tug_enable_drag() {
+
            select {atomIndex=gCcargoIdx}
    gEcho = "__________TUG__________|ALT-CLICK=mark block|SHIFT-CLICK=anchors" +
+
            halo off
        "|ALT-CTRL-CLICK=pivots|ALT-SHIFT-CLICK=dest atom|ALT-DRAG=move" +
 
        "|SHIFT-ALT-DRAG=alt move|CLICK bond=freeze|DOUBLE-CLICK=exit"
 
    echo @gEcho
 
  
    # Allow atoms to be dragged
+
            # If nWard cargo exists, mark it as the cWard cargo
    bind "ALT-LEFT-DOWN" "tug_mark_mb";
+
            if (gNcargoIdx != gCcargoIdx) {
    bind "ALT-LEFT-UP" "tug_drag_done_mb";
+
                gCcargoIdx = get_cp_idx(gNcargoIdx)
    bind "ALT-SHIFT-LEFT-DOWN" "tug_mark_mb";
+
                gCcargoNo = {atomIndex=gCcargoIdx}.atomno
    bind "ALT-SHIFT-LEFT-UP" "tug_drag_done_mb";
+
            }
    bind "ALT-LEFT-DRAG" "tug_drag_mb";
+
            else {
    bind "ALT-SHIFT-LEFT-DRAG" "tug_drag_2_mb";
+
                gCcargoIdx = -1
 
+
                gNcargoIdx = -1
    unbind "SHIFT-LEFT-CLICK"
+
            }
    bind "SHIFT-LEFT-CLICK" "_pickAtom";
+
        }
    bind "SHIFT-LEFT-CLICK" "+:tug_anchor_mb";
+
        else if (gNcargoIdx == aPidx) {
 +
            select {atomIndex=gNcargoIdx}
 +
            halo off
 +
            gNcargoIdx = get_nm_idx(gCcargoIdx)
 +
            gNcargoNo = {atomIndex=gNcargoIdx}.atomno
 +
        }
 +
        else if (gCcargoIdx >= 0) {
  
    bind "ALT-CTRL-LEFT-CLICK" "_pickAtom";
+
            var no = {atomIndex=aPidx}.atomno
    bind "ALT-CTRL-LEFT-CLICK" "+:tug_pivot_mb";
 
  
    bind "ALT-SHIFT-LEFT-CLICK" "_pickAtom";
+
            # If pick is nWard of it
    bind "ALT-SHIFT-LEFT-CLICK" "+:tug_dest_atom_mb";
+
            if (no < {atomIndex=gCcargoIdx}.atomno) {
}
 
  
# Bound to SHIFT-LEFT-CLICK by tug_cargo_mb
+
                # If exists, clear its highlight
function tug_anchor_mb() {
+
                if (gNcargoIdx != gCcargoIdx) {
    if ({atomIndex=_atomPicked}.chain == gChain) {
+
                    select {atomIndex=gNcargoIdx}
        var aPidx = get_sc_bb_idx( _atomPicked, gChain)
+
                    halo off
 +
                }
  
        var pno = {atomIndex=aPidx}.atomno
+
                # Set new nWard cargo and highlight it
        if (pno > {atomIndex=gCcargoIdx}.atomno) {
+
                gNcargoIdx = get_nm_idx(aPidx)
            select {atomIndex=gCanchorIdx}
+
                gNcargoNo = {atomIndex=gNcargoIdx}.atomno
            halo off
 
            if (gCanchorIdx == aPidx) {
 
                gCanchorIdx = -1
 
                gCanchorNo = gMaxNo + 1
 
 
             }
 
             }
 +
 +
            # Else cWard
 
             else {
 
             else {
                 gCanchorIdx = aPidx
+
 
                 gCanchorNo = {atomIndex=gCanchorIdx}.atomno
+
                # Clear its old highlight
                 select {atomIndex=gCanchorIdx}
+
                 select {atomIndex=gCcargoIdx}
                halo on
+
                 if (gNcargoIdx != gCcargoIdx) {
 +
                    halo off
 +
                }
 +
 
 +
                # Set new cWard cargo and highlight
 +
                gCcargoIdx = get_cp_idx(aPidx)
 +
                 gCcargoNo = {atomIndex=gCcargoIdx}.atomno
 
             }
 
             }
            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 no cWard cargo
 
         else {
 
         else {
             tow_cargo_mb()
+
 
 +
             # Set new cWard cargo and highlight
 +
            gCcargoIdx = get_cp_idx(aPidx)
 +
            gCcargoNo = {atomIndex=gCcargoIdx}.atomno
 +
            gNcargoIdx = get_nm_idx(gCcargoIdx)
 +
            gNcargoNo = {atomIndex=gNcargoIdx}.atomno
 
         }
 
         }
  
         # Get moving atoms set
+
         # If any anchor now inside cargo cluster, kill it
         gMovingSet = {((atomno < gCanchorNo) and (atomno > gNanchorNo)
+
         if ({atomIndex=gCanchorIdx}.atomno <= {atomIndex=gCcargoIdx}.atomno) {
             and (chain=gChain))}
+
            gCanchorIdx = -1
    }
+
            gCanchorNo = gMaxNo + 1
    select {gCargoSet}
+
        }
}
+
        if ({atomIndex=gNanchorIdx}.atomno >= {atomIndex=gNcargoIdx}.atomno) {
 +
            gNanchorIdx = -1
 +
             gNanchorNo = gMinNo - 1
 +
        }
 +
 
 +
        # Highlight cargo cluster
 +
        select_nward_idx(gCcargoIdx, gNcargoIdx)
 +
        gCargoSet = {selected}
 +
        set_colors()
  
# Bound to ALT-SHIFT-LEFT-CLICK by tug_cargo_mb
+
        # Collect the rotor sets
function tug_dest_atom_mb() {
+
        collect_rotors()
    var aOk = TRUE
 
    if ({atomIndex=_atomPicked}.chain == gChain) {
 
  
         var pno = {atomIndex=_atomPicked}.atomno
+
         # Get moving atoms set
        if ((pno <= {atomIndex=gCcargoIdx}.atomno) and (pno >= {atomIndex=gNcargoIdx}.atomno)) {
+
        gMovingSet = {((atomno < gCanchorNo) and (atomno > gNanchorNo)
             aOk = FALSE
+
             and (chain=gChain) and thisModel)}
        }
 
 
     }
 
     }
     if (aOk) {
+
 
         select {atomIndex=gDestAtomIdx}
+
    # Enable dragging
        star off
+
     if (gToab) {
        if (gDestAtomIdx == _atomPicked) {
+
         to_ab_enable_drag()
            gDestAtomIdx = -1
+
    }
        }
+
    else {
        else {
+
         tug_enable_drag()
            gDestAtomIdx = _atomPicked
 
            select {atomIndex=gDestAtomIdx}
 
            star on
 
         }
 
        select {gCargoSet}
 
 
     }
 
     }
 +
 +
    select {gCargoSet}
 
}
 
}
  
# Bound to CTRL-LEFT-CLICK by tug_cargo_mb
+
function tug_undo_mb() {
function tug_pivot_mb() {
+
     if (prompt("Undo", "Yes|No", true) == "Yes") {
     if (g1pivotIdx == _atomPicked) {
+
         restore state gState
        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
+
# Top level of Tug
        g2dynamicIdx = -1
+
function plico_tug() {
     }
+
 
     else if (g1pivotIdx >= 0) {
+
     # Load common functions if not already
         if (g2pivotIdx >= 0) {
+
     if (kCommon < 7) {
             color {atomIndex=g2pivotIdx} @gScheme
+
        script $SCRIPT_PATH$plicoCommon.spt
 +
         if (kCommon < 7) {
 +
             prompt ("A newer version of plicoCommon.SPT is required")
 +
            quit
 
         }
 
         }
 
        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
+
     gPlico = "TUG"
function tow_cargo_mb() {
+
     plico_prelim(true, true)
     gTow = TRUE
+
      
     gChain = {atomIndex=_atomPicked}.chain
+
     gBondPicking = bondPicking
     gMinNo = {chain=gChain}.atomno.min
+
     set bondPicking true
     gMaxNo = {chain=gChain}.atomno.max
+
     set PickCallback "jmolscript:tug_pick_cb"
     gCcargoIdx = -1
 
     gNcargoIdx = -1
 
    gCanchorIdx = -1
 
    gCanchorNo = gMaxNo + 1
 
    gNanchorIdx = -1
 
    gNanchorNo = gMinNo - 1
 
  
     # Highlight cargo cluster
+
     gEcho = ("_________TUG_________|ALT-CLICK=mark block|SHIFT-CLICK=mark chain" +
     select {chain=gChain}
+
        "|CLICK bond=freeze|ALT-DOUBLE-CLICK=undo|SHIFT-DOUBLE-CLICK=exit")
     gCargoSet = {selected}
+
     echo @gEcho
     gMovingSet = {selected}
+
     gCrotors = array()
     set_colors()
+
     gNrotors = array()
 +
     clear_atom_idxs()
 +
    gToab = false
  
     # Enable dragging
+
     bind "ALT-LEFT-CLICK" "_pickAtom";
    tug_enable_drag()
+
     bind "ALT-LEFT-CLICK" "+:tug_cargo_mb";
    select {gCargoSet}
+
     bind "SHIFT-LEFT-CLICK" "_pickAtom";
    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";
 
     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 ALT-LEFT-CLICK by plico_tug or called by plicotoab.toabCargoMB
+
# Bound to DOUBLE by plico_tug
function tug_cargo_mb() {
+
function tug_exit() {
 
+
     if (plico_exit()) {
    # If O or movable side chain atom picked
+
         set bondPicking gBondPicking
     if (({atomIndex=_atomPicked}.atomName = "O")
+
        set PickCallback NONE
        or (is_moveable_sc( _atomPicked))) {
+
         select all
         if (gSCidx >= 0) {
+
         color bonds none
            draw gSCcircle DELETE
+
       
         }
+
         reset kTug
         if (gSCidx == _atomPicked) {
+
         reset gCanchorIdx 
            gSCidx = -1
+
        reset gCanchorNo 
         }
+
        reset gPlico     
         else {
+
        reset gNanchorIdx 
            gSCidx = _atomPicked
+
         reset gNanchorNo 
            gSCpt = {atomIndex=gSCidx}.xyz
+
        reset gCcargoIdx 
            draw gSCcircle CIRCLE {atomIndex=gSCidx} MESH NOFILL
+
         reset gNcargoIdx  
         }
+
        reset gCcargoNo   
    }
+
        reset gNcargoNo   
    else {
+
        reset gDestAtomIdx
         if ({atomIndex=_atomPicked}.chain != gChain) {
+
        reset g1pivotIdx 
            if (gTow) {
+
        reset g2pivotIdx 
                select {chain=gChain}
+
        reset gSelSaves   
                color {selected} @gScheme
+
         reset gCrotors   
            }
+
         reset gNrotors   
            gChain = {atomIndex=_atomPicked}.chain
+
         reset gOkCollide 
            select ({atomIndex=gCcargoIdx} or {atomIndex=gNcargoIdx}
+
         reset gChain      
                or {atomIndex=gCanchorIdx} or {atomIndex=gNanchorIdx})
+
         reset gMinNo      
            halo off
+
         reset gMaxNo      
            gCcargoIdx = -1
+
         reset gCargoSet   
            gNcargoIdx = -1
+
         reset gMovingSet 
            gCanchorIdx = -1
+
         reset gSCidx      
            gNanchorIdx = -1
+
         reset gSCcircle  
         }
+
        reset gSCpt       
         gTow = FALSE
+
         reset gTargetPt   
         gMinNo = {chain=gChain}.atomno.min
+
         reset gNewDrag   
         gMaxNo = {chain=gChain}.atomno.max
+
        reset gTow       
         if (gNanchorIdx < 0) {
+
        reset g1dynamicIdx
            gNanchorNo = gMinNo - 1
+
        reset g2dynamicIdx
        }
+
        reset gSCcheck   
         if (gCanchorIdx < 0) {
+
        reset gBondPicking
            gCanchorNo = gMaxNo + 1
+
        reset gFreeze     
         }
+
        reset gToab       
         var aPidx = get_sc_bb_idx( _atomPicked, gChain)
+
        reset function "get_cp_idx"
 
+
         reset function "get_cm_no"
         gSCidx = -1
+
         reset function "get_nm_idx"
         draw gSCcircle DELETE
+
        reset function "get_np_no"
 
+
         reset function "get_cb_idx"
         # If existing cWard cargo picked
+
         reset function "get_o_idx"
         if (gCcargoIdx == aPidx) {
+
        reset function "get_nward_bb_idx"
 
+
        reset function "get_cward_bb_idx"
            # Clear the highlight
+
        reset function "get_sc_set"
            select {atomIndex=gCcargoIdx}
+
        reset function "get_sc_bb_idx"
            halo off
+
        reset function "is_bb_idx"
 
+
        reset function "is_sc_idx"
            # If nWard cargo exists, mark it as the cWard cargo
+
        reset function "select_add_sc"
            if (gNcargoIdx != gCcargoIdx) {
+
        reset function "handle_collisions"
                gCcargoIdx = get_cp_idx(gNcargoIdx)
+
        reset function "tug_track_idx"
                gCcargoNo = {atomIndex=gCcargoIdx}.atomno
+
        reset function "do_counter_rotate"
            }
+
        reset function "counter_rotate"
            else {
+
        reset function "counter_rotate_2"
                gCcargoIdx = -1
+
        reset function "repair_proline"
                gNcargoIdx = -1
+
        reset function "repair_sc"
            }
+
        reset function "tug_track_c"
         }
+
         reset function "tug_track_n"
         else if (gNcargoIdx == aPidx) {
+
         reset function "translate_selected_record"
            select {atomIndex=gNcargoIdx}
+
         reset function "rotate_selected_record"
            halo off
+
         reset function "collect_sc_rotors"
            gNcargoIdx = get_nm_idx(gCcargoIdx)
+
         reset function "drag_sc"
            gNcargoNo = {atomIndex=gNcargoIdx}.atomno
+
         reset function "fix_sc_collision_2"
         }
+
         reset function "is_moveable_sc"
         else if (gCcargoIdx >= 0) {
+
         reset function "is_rotor_avail"
 
+
         reset function "xcollect_bb_rotors"
            var no = {atomIndex=aPidx}.atomno
+
         reset function "collect_bb_rotors"
 
+
         reset function "collect_rotors"
            # If pick is nWard of it
+
         reset function "tug_sc"
            if (no < {atomIndex=gCcargoIdx}.atomno) {
+
         reset function "set_colors"
 
+
        reset function "clear_atom_idxs"
                # If exists, clear its highlight
+
         reset function "timed_out"
                if (gNcargoIdx != gCcargoIdx) {
+
         reset function "record_drag"
                    select {atomIndex=gNcargoIdx}
+
        reset function "tug_pick_cb"
                    halo off
+
         reset function "tug_drag_done_mb"
                }
+
         reset function "tug_drag_2_mb"
 
+
        reset function "tug_drag_mb"
                # Set new nWard cargo and highlight it
+
        reset function "tug_mark_mb"
                gNcargoIdx = get_nm_idx(aPidx)
+
        reset function "tug_anchor_mb"
                gNcargoNo = {atomIndex=gNcargoIdx}.atomno
+
        reset function "tug_enable_drag"
            }
+
        reset function "tug_dest_atom_mb"
 
+
         reset function "tug_pivot_mb"
            # Else cWard
+
        reset function "tow_cargo_mb"
            else {
+
        reset function "tug_cargo_mb"
 
+
         reset function "tug_undo_mb"
                # Clear its old highlight
+
        reset function "plico_tug"
                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
 
 
 
            # Set default cWard anchor at cWard N
 
            var iNo = gMaxNo
 
            for (; iNo > 0; iNo--) {
 
                if ({(atomno=iNo) and (chain=gChain)}.atomName == "N") {
 
                    break;
 
                }
 
            }
 
            gCanchorIdx = {(atomno=iNo) and (chain=gChain)}.atomIndex
 
            gCanchorNo = {atomIndex=gCanchorIdx}.atomno
 
         }
 
 
 
         # If any anchor now inside cargo cluster, kill it
 
         if ({atomIndex=gCanchorIdx}.atomno <= {atomIndex=gCcargoIdx}.atomno) {
 
            gCanchorIdx = -1
 
            gCanchorNo = gMaxNo + 1
 
         }
 
         if ({atomIndex=gNanchorIdx}.atomno >= {atomIndex=gNcargoIdx}.atomno) {
 
            gNanchorIdx = -1
 
            gNanchorNo = gMinNo - 1
 
         }
 
 
 
         # Highlight cargo cluster
 
         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))}
 
    }
 
 
 
    # Enable dragging
 
    if (gToab) {
 
         toabEnableDrag()
 
    }
 
    else {
 
         tug_enable_drag()
 
    }
 
   
 
    select {gCargoSet}
 
}
 
  
# Top level of Tug
 
function plico_tug() {
 
  
    # Load common functions if not already
 
    if (kCommon < 2) {
 
        script $SCRIPT_PATH$plicoCommon.spt
 
        if (kCommon < 2) {
 
            prompt ("A newer version of plicoCommon.SPT is required")
 
            quit
 
        }
 
 
     }
 
     }
   
 
    gPlico = "TUG"
 
    plico_prelim(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|DOUBLE-CLICK=exit")
 
    echo @gEcho
 
    gCrotors = array()
 
    gNrotors = array()
 
    clear_atom_idxs()
 
 
    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 "DOUBLE" "tug_exit";
 
}
 
 
# Bound to DOUBLE by plico_tug
 
function tug_exit() {
 
    set bondPicking gBondPicking
 
    set PickCallback NONE
 
    plico_exit()
 
 
}
 
}
  
# 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