Code
--[[
SelectoPro
Version 1.2 allows selecting segments by segment number, chain, or amino
acid. Segment number input uses the syntax of Timo van der Laan's segment
set logic.
Version 1.3 better implements segment set logic
select segments based on criteria
v1.0 - LociOiling - 2017/10/31
-- select mutable, locked, frozen, hydrophobic, hydrophilic, etc.
v1.1 - LociOiling - 2022/01/23
-- select monomer or complex core, boundary, surface
v1.2 - LociOiling - 2023/05/16
-- group global variables in tables
-- select by chain (detect chains)
-- select by segment number, or segment range
-- select by amino acid
-- cache selection info for performance
-- capture existing selections as set
-- trim down SLT (remove segment type functions)
-- scriptlog output of final selections
-- a ligand is just another segment
v1.3 - LociOiling - 2024/01/
-- speed things up, work in segment sets instead of one-by-one
-- remove protein design features: mutable, simplex core, and complex core,
no more protein design puzzles, no reference to puzzle objectives now
-- allow selecting *unlocked"
-- split unlocked into backbone, sidechain
-- allow selecting *unfrozen*
-- split frozen into backbone, sidechain
]]--
--
-- globals section
--
Recipe = "SelectoPro"
Version = "1.3"
ReVersion = Recipe .. " " .. Version
PST = {
segCnt = 0,
segCnt2 = 0,
sel = {}, -- selected segments
selSet = {}, -- selected segment set
selCnt = {}, -- initial selections
segDist = {}, -- alpha carbon distances
chain = {}, -- chain as a list
chainSet = {}, -- chain as a set (ranges of segments)
ACRF = 4.0, -- alpha carbon reference distance
CHAINID = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" },
AAMaster = {
a = { name = "alanine", code = 'ALA', sel = false, },
c = { name = "cysteine", code = 'CYS', sel = false, },
d = { name = "aspartate", code = 'ASP', sel = false, },
e = { name = "glutamate", code = 'GLU', sel = false, },
f = { name = "phenylalanine", code = 'PHE', sel = false, },
g = { name = "glycine", code = 'GLY', sel = false, },
h = { name = "histidine", code = 'HIS', sel = false, },
i = { name = "isoleucine", code = 'ILE', sel = false, },
k = { name = "lysine", code = 'LYS', sel = false, },
l = { name = "leucine", code = 'LEU', sel = false, },
m = { name = "methionine", code = 'MET', sel = false, },
n = { name = "asparagine", code = 'ASN', sel = false, },
p = { name = "proline", code = 'PRO', sel = false, },
q = { name = "glutamine", code = 'GLN', sel = false, },
r = { name = "arginine", code = 'ARG', sel = false, },
s = { name = "serine", code = 'SER', sel = false, },
t = { name = "threonine", code = 'THR', sel = false, },
v = { name = "valine", code = 'VAL', sel = false, },
y = { name = "tyrosine", code = 'TYR', sel = false, },
w = { name = "tryptophan", code = 'TRP', sel = false, },
},
}
local SPX = {
sUnlockedBB = false,
sUnlockedSC = false,
sUnfrozenBB = false,
sUnfrozenSC = false,
sPhobic = false,
sPhilic = false,
sLigand = false,
sChain = false, -- implied dialog value, select by chain
sAA = false, -- implied dialog value, select by amino acid
sSeg = false, -- implied dialog value, select by segment/range
ranges = "", -- ranges of segments as string
rtab = {}, -- ranges as a table
sKeep = false,
sInvert = false,
}
SLT = { -- SLT--SLT--SLT--SLT--SLT--SLT--SLT--SLT--SLT--SLT--
--[[
SLT - Segment set, list, and type module v0.7
Includes the segment set and list module and the segment type module
developed by Timo van der Laan.
The following Foldit recipes contain the original code for these modules:
* Tvdl enhanced DRW 3.1.1 - https://fold.it/portal/recipe/102840
* TvdL DRemixW 3.1.2 - https://fold.it/portal/recipe/102398
The "set and list" module performs logical operations and
transformations on tables containing ranges of segment.
The segment type module find lists and sets of segments with various
properties, such as selected or frozen.
A "list" is one-dimensional table containing segment numbers.
A "set" is a two-dimensional table containing segment number ranges.
For example, given a list of segments:
list = { 1, 2, 3, 7, 8, 11, 13, 14, 15 }
the corresponding set is:
set = { { 1, 3 }, { 7, 8 }, { 11, 11 }, {13, 15 } }
Most functions assume that the sets are well-formed,
meaning they are ordered and have no overlaps.
As an example, the method FindUnlocked returns a set of
all the unlocked segments in a puzzle. The method can be
called as follows:
funlocked = SLT:FindUnlocked ()
The return value funlocked is a two-dimensional table
containing ranges of unlocked segments.
In source format, the table might look like this:
funlocked = {
{ 27, 35, },
{ 47, 62, },
{ 78, 89, },
}
The code to use this table would look like:
--
-- for each range of segments
--
for ii = 1, #funlocked do
--
-- for each segment in the range, so something
--
for jj = funlocked [ ii ] [ 1 ], funlocked [ ii ] [ 2 ] do
... something ...
end
end
This psuedo-module is a table containing a mix of
data fields and methods. This wiki article explains
the packaging technique:
https://foldit.fandom.com/wiki/Lua_packaging_for_Foldit
Authorship
----------
Original by Timo van der Laan:
02-05-2012 TvdL Free to use for non commercial purposes
French comments by Bruno Kestemont and perhaps others.
v0.1 - LociOiling
+ extract and reformat code
v0.2 - LociOiling - 2017/11/03
+ add primary FindUnlocked function
v0.3 - LociOiling
+ add FindRotamers function
v0.4 - LociOiling - 2019/10/29
+ package as table
+ remove dependencies on segCnt and segCnt2
v0.5 - LociOiling - 2019/12/17
+ convert functions to methods, update internal references
v0.6 - LociOiling - 2022/04/18
+ add FindAAList
v0.7 - LociOiling - 2023/05/02
+ add FindWorkableList
]]--
--
-- variables
--
segCnt = nil, -- segment count, not adjusted for ligands
segCnt2 = nil, -- segment count, not including terminal ligands
--
-- initializer - can be called externally, but invoked inline if segCnt or segCnt2 are nil
--
Init = function ( self )
self.segCnt = structure.GetCount ()
self.segCnt2 = self.segCnt
while structure.GetSecondaryStructure ( self.segCnt2 ) == "M" do
self.segCnt2 = self.segCnt2 - 1
end
end,
--
-- segment set and list functions
--
SegmentListToSet = function ( self, list ) -- retirer doublons
local result = {}
local ff = 0
local ll = -1
table.sort ( list )
for ii = 1, #list do
if list [ ii ] ~= ll + 1 and list [ ii ] ~= ll then
-- note: duplicates are removed
if ll > 0 then
result [ #result + 1 ] = { ff, ll }
end
ff = list [ ii ]
end
ll = list [ ii ]
end
if ll > 0 then
result [ #result + 1 ] = { ff, ll }
end
return result
end,
SegmentSetToList = function ( self, set ) -- faire une liste a partir d'une zone
local result = {}
for ii = 1, #set do
for kk = set [ ii ] [ 1 ], set [ ii ] [ 2 ] do
result [ #result + 1 ] = kk
end
end
return result
end,
SegmentCleanSet = function ( self, set )
-- Makes it well formed
return self:SegmentListToSet ( self:SegmentSetToList ( set ) )
end,
SegmentInvertSet = function ( self, set, maxseg )
--
-- Gives back all segments not in the set
-- maxseg is added for ligand
--
local result={}
if maxseg == nil then
maxseg = structure.GetCount ()
end
if #set == 0 then
return { { 1, maxseg } }
end
if set [ 1 ] [ 1 ] ~= 1 then
result [ 1 ] = { 1, set [ 1 ] [ 1 ] - 1 }
end
for ii = 2, #set do
result [ #result + 1 ] = { set [ ii - 1 ] [ 2 ] + 1, set [ ii ] [ 1 ] - 1, }
end
if set [ #set ] [ 2 ] ~= maxseg then
result [ #result + 1 ] = { set [ #set ] [ 2 ] + 1, maxseg }
end
return result
end,
SegmentInvertList = function ( self, list )
if self.segCnt2 == nil then
self:Init ()
end
table.sort ( list )
local result = {}
for ii = 1, #list - 1 do
for jj = list [ ii ] + 1, list [ ii + 1 ] - 1 do
result [ #result + 1 ] = jj
end
end
for jj = list [ #list ] + 1, self.segCnt2 do
result [ #result + 1 ] = jj
end
return result
end,
SegmentInList = function ( self, seg, list ) -- verifier si segment est dans la liste
table.sort ( list )
for ii = 1, #list do
if list [ ii ] == seg then
return true
elseif list [ ii ] > seg then
return false
end
end
return false
end,
SegmentInSet = function ( self, set, seg ) --verifie si segment est dans la zone
for ii = 1, #set do
if seg >= set [ ii ] [ 1 ] and seg <= set [ ii ] [ 2 ] then
return true
elseif seg < set [ ii ] [ 1 ] then
return false
end
end
return false
end,
SegmentJoinList = function ( self, list1, list2 ) -- fusionner 2 listes de segments
local result = list1
if result == nil then
return list2
end
for ii = 1, #list2 do
result [ #result + 1 ] = list2 [ ii ]
end
table.sort ( result )
return result
end,
SegmentJoinSet = function ( self, set1, set2 ) --fusionner (ajouter) 2 zones
return self:SegmentListToSet ( self:SegmentJoinList ( self:SegmentSetToList ( set1 ), self:SegmentSetToList ( set2 ) ) )
end,
SegmentCommList = function ( self, list1, list2 ) -- chercher intersection de 2 listes
local result = {}
table.sort ( list1 )
table.sort ( list2 )
if #list2 == 0 then
return result
end
local jj = 1
for ii = 1, #list1 do
while list2 [ jj ] < list1 [ ii ] do
jj = jj + 1
if jj > #list2 then
return result
end
end
if list1 [ ii ] == list2 [ jj ] then
result [ #result + 1 ] = list1 [ ii ]
end
end
return result
end,
SegmentCommSet = function ( self, set1, set2 ) -- intersection de 2 zones
return self:SegmentListToSet ( self:SegmentCommList ( self:SegmentSetToList ( set1 ), self:SegmentSetToList ( set2 ) ) )
end,
SegmentSetMinus = function ( self, set1, set2 )
return self:SegmentCommSet ( set1, self:SegmentInvertSet ( set2 ) )
end,
SegmentPrintSet = function ( self, set )
print ( self:SegmentSetToString ( set ) )
end,
SegmentSetToString = function ( self, set ) -- pour pouvoir imprimer
local line = ""
for ii = 1, #set do
if ii ~= 1 then
line = line .. ", "
end
line = line .. set [ ii ] [ 1 ] .. "-" .. set [ ii ] [ 2 ]
end
return line
end,
SegmentSetInSet = function ( self, set, sub )
if sub == nil then
return true
end
--
-- Checks if sub is a proper subset of set
--
for ii = 1, #sub do
if not self:SegmentRangeInSet ( set, sub [ ii ] ) then
return false
end
end
return true
end,
SegmentRangeInSet = function ( self, set, range ) -- verifier si zone est dans suite
if range == nil or #range == 0 then
return true
end
local bb = range [ 1 ]
local ee = range [ 2 ]
for ii = 1, #set do
if bb >= set [ ii ] [ 1 ] and bb <= set [ ii ] [ 2 ] then
return ( ee <= set [ ii ] [ 2 ] )
elseif ee <= set [ ii ] [ 1 ] then
return false
end
end
return false
end,
SegmentSetToBool = function ( self, set ) --vrai ou faux pour chaque segment utilisable ou non
local result = {}
for ii = 1, structure.GetCount () do
result [ ii ] = self:SegmentInSet ( set, ii )
end
return result
end,
--
-- End of Segment Set module
--
--
-- Module Find Segment Types removed for space
--
}-- SLT--SLT--SLT--SLT--SLT--SLT--SLT--SLT--SLT--SLT--
--
-- SegmentStringToSet - convert user input to a segment set
--
-- This function is the logical inverse of SSL:SegmentSetToString.
--
-- User input is converted to a table containing ranges of segments.
--
-- User input is a comma-separated list of segment ranges or indivdual segments.
--
-- For example:
--
-- 12-23,47,65-67,69
--
-- or
--
-- 12,47,60
--
-- Segment numbers are validated such that 1 <= segnum <= structure.GetCount
--
-- Any stray characters are considered errors.
--
-- The SegmentSetToString returns a table containing the segment set, and a separarate table
-- containing error messages.
--
-- If there are errors, the segment set table is empty. The error table is empty is there are
-- no errors.
--
--
function SegmentStringToSet ( range )
local segCnt = structure.GetCount ()
local table = {}
local errz = {}
local function chkSeg ( seg, word, xtra1, xtra2, errz )
local ok = true
if seg == nil then
errz [ #errz + 1 ] = "error in \"" .. word .. "\", segment number must be numeric"
ok = false
elseif seg <= 0 then
errz [ #errz + 1 ] = "error in \"" .. word .. "\", segment number must be positive"
ok = false
elseif seg > segCnt then
errz [ #errz + 1 ] = "error in \"" .. word .. "\", segment greater than max " .. segCnt
ok = false
elseif xtra1:len () > 0 then
errz [ #errz + 1 ] = "error in \"" .. word .. "\", extra characters \"" .. xtra1 .. "\""
ok = false
elseif xtra2:len () > 0 then
errz [ #errz + 1 ] = "error in \"" .. word .. "\", extra characters \"" .. xtra2 .. "\""
ok = false
end
return ok, errz
end
--
-- pass 1 - comma-separated items
--
for word in range:gmatch ( "([^,]+)" ) do
word = word:gsub("%s+", "") -- remove spaces
local rgt = {}
--
-- pass 2a - hyphen-separated segment range
--
-- In string.gmatch, patterns enclosed in parentheses are "captures", which are returned.
--
-- Both wanted and unwanted items are captured.
--
-- The pattern ([^%d]*) is used to capture any non-numerics before the first and second
-- numerics in the range (extra0 and extra2). The square brackets indicate a custom character
-- class, and the "^" inside the brackets indicates the inverse of what follows. "%d" means
-- indicate the class of all decimal digits, so "[^%d] is the class of anything that's not a
-- decimal digit. The "*" in the pattern matches zero or more occurences.
--
-- The pattern (%d+) captures both the first and second numerics in the range (num1 and num2).
--
-- The pattern ([^%-]-) captures anything that's not a hypen after the first numeric (extra2).
--
-- The pattern %- represents the hyphen, and is not a capture. A missing hyphen means no match.
--
-- Finally, the pattern (.*) captures anything after the second numeric (extra3).
--
-- extra0 num1 extra1 - extra2 num2 extra3
for x0, r1, x1, x2, r2, x3 in word:gmatch ( "([^%d]*)(%d+)([^%-]-)%-([^%d]*)(%d+)(.*)" ) do
local ok
local r1x = tonumber ( r1 )
ok, errz = chkSeg ( tonumber ( r1 ), word, x0, x1, errz )
if ok then
rgt [ #rgt + 1 ] = r1x
end
local r2x = tonumber ( r2 )
ok, errz = chkSeg ( r2x, word, x2, x3, errz )
if ok then
rgt [ #rgt + 1 ] = r2x
end
end
--
-- pass 2b - single segment if pass 2a fails
--
-- If the first gmatch doesn't find a match, a second gmatch looks for just a numeric.
--
-- Again, unwanted items are also captured, the captures are similar to phase 2.
--
if #rgt == 0 and #errz == 0 then
for x0, r1, x1 in word:gmatch ( "([^%d]*)(%d+)(.*)" ) do
print ( "r1 = " .. r1 .. ", x1 = " .. x1 )
local r1x = tonumber ( r1 )
ok, errz = chkSeg ( r1x, word, x0, x1, errz )
if ok then
rgt [ #rgt + 1 ] = r1x
end
end
end
--
-- validate and add to table
--
if #rgt == 1 then -- double up, making a range of one segment
rgt [ #rgt + 1 ] = rgt [ 1 ]
end
if #rgt > 0 then -- swap if segments out of order
if rgt [ 1 ] > rgt [ 2 ] then
rgt [ 1 ], rgt [ 2 ] = rgt [ 2 ], rgt [ 1 ] -- switch-a-rooney
end
table [ #table + 1 ] = rgt
end
end
--
-- catch-all, error if nothing matched input
--
if #table == 0 and #errz == 0 then
errz [ #errz + 1 ] = "error in \"" .. range .. "\", invalid input"
end
if #errz > 0 then
table = {}
end
return table, errz
end
function GetParameters ()
local uerror = {}
local rc
repeat
local d = dialog.CreateDialog ( ReVersion )
d.selcnt = dialog.AddLabel ( PST.selCnt .. " segments selected initially" )
d.ranges = dialog.AddTextbox ( "Selections", SPX.ranges )
d.segmsg1 = dialog.AddLabel ( "Selections as segment ranges or single segments, " )
d.segmsg2 = dialog.AddLabel ( "specified as a comma-separated list, " )
d.segmsg3 = dialog.AddLabel ( "for example: 1-20,17,21,35-59" )
d.s0 = dialog.AddLabel ( "" )
d.l0 = dialog.AddLabel ( "Select segments if..." )
d.sUnlockedBB = dialog.AddCheckbox ( "Unlocked backbone?", SPX.sUnlockedBB )
d.sUnlockedSC = dialog.AddCheckbox ( "Unlocked sidechain?", SPX.sUnlockedSC )
d.sUnfrozenBB = dialog.AddCheckbox ( "Unfrozen backbone?", SPX.sUnfrozenBB )
d.sUnfrozenSC = dialog.AddCheckbox ( "Unfrozen sidechain?", SPX.sUnfrozenSC )
d.sPhobic = dialog.AddCheckbox ( "Hydrophobic?", SPX.sPhobic )
d.sPhilic = dialog.AddCheckbox ( "Hydrophilic?", SPX.sPhilic )
d.l5 = dialog.AddLabel ( "" )
d.sKeep = dialog.AddCheckbox ( "Keep existing selections?", SPX.sKeep )
d.sInvert = dialog.AddCheckbox ( "Invert new selections?", SPX.sInvert )
d.sLigand = dialog.AddCheckbox ( "Include ligand(s)?", SPX.sLigand )
if #uerror > 0 then
d.lerr = dialog.AddLabel ( "" )
for ii = 1, #uerror do
d [ "lerr" .. ii ] = dialog.AddLabel ( uerror [ ii ] )
end
end
d.ok = dialog.AddButton ( "OK" , 1 )
if #PST.chainSet > 1 then
d.chains = dialog.AddButton ( "Chains" , 2 )
end
d.AAs = dialog.AddButton ( "AAs", 3 )
d.cancel = dialog.AddButton ( "Cancel" , 0 )
rc = dialog.Show ( d )
uerror = {}
if rc == 2 then
GetChains ( 1 )
end
if rc == 3 then
GetAAs ( 1 )
end
if rc > 0 then
SPX.sUnlockedBB = d.sUnlockedBB.value
SPX.sUnlockedSC = d.sUnlockedSC.value
SPX.sUnfrozenBB = d.sUnfrozenBB.value
SPX.sUnfrozenSC = d.sUnfrozenSC.value
SPX.sPhobic = d.sPhobic.value
SPX.sPhilic = d.sPhilic.value
SPX.sKeep = d.sKeep.value
SPX.sInvert = d.sInvert.value
SPX.ranges = d.ranges.value
SPX.sLigand = d.sLigand.value
if SPX.ranges:len() > 0 then
local terror = {}
SPX.rtab, terror = SegmentStringToSet ( SPX.ranges )
if #terror == 0 then
SPX.rtab = SLT:SegmentCleanSet ( SPX.rtab )
else
for ii = 1, #terror do
uerror [ #uerror + 1 ] = terror [ ii ]
end
end
end
end
until rc <= 1 and #uerror == 0
return rc
end
function GetChains ( chndx )
if chndx == nil then
chndx = 1
end
local CHPAGE = 8
local rc = 0
local chmax = math.min ( #PST.chainSet, chndx + CHPAGE - 1 )
local d = dialog.CreateDialog ( ReVersion .. " Chains" )
d.l1 = dialog.AddLabel ( "Displaying chains " .. chndx .. "-" .. chmax .. " of " .. #PST.chainSet )
for ii = chndx, chmax do
local cs = PST.chainSet [ ii ]
d [ "chn" .. ii .. "l1" ] = dialog.AddCheckbox (
"Chain "
.. cs.chain ..
": "
.. cs.start ..
"-"
.. cs.stop ..
", length = "
.. cs.len,
cs.sel
)
end
d.ok = dialog.AddButton ( "OK" , 1 )
if chndx > 1 then
d.prev = dialog.AddButton ( "Prev", 2 )
end
if chmax < #PST.chainSet then
d.next = dialog.AddButton ( "Next", 3 )
end
d.cancel = dialog.AddButton ( "Cancel" , 0 )
repeat
rc = dialog.Show ( d )
if rc > 0 then
for ii = chndx, chmax do
local cs = PST.chainSet [ ii ]
cs.sel = d [ "chn" .. ii .. "l1" ].value
end
end
if rc == 2 then
rc = GetChains ( chndx - CHPAGE )
end
if rc == 3 then
rc = GetChains ( chndx + CHPAGE )
end
until rc < 2
return rc
end
function GetAAs ( aaindx )
local AAMPAGE = 10 -- amino acids / page
local AAMSIZE = 20 -- hardcode this value, #AAMaster not valid
local rc = 0
local ask = dialog.CreateDialog ( ReVersion .. " Amino Acids" )
local aamax = math.min ( AAMSIZE, aaindx + AAMPAGE - 1 )
ask.AADisp = dialog.AddLabel ( "displaying " .. aaindx .. " - " .. aamax .. " of " .. AAMSIZE )
local aacnt = 0
for key, value in pairs ( PST.AAMaster ) do
aacnt = aacnt + 1
if aacnt >= aaindx and aacnt <= aamax then
local aalabel = ""
.. string.upper ( key ) ..
" ("
.. PST.AAMaster [ key ].code ..
") - "
.. PST.AAMaster [ key ].name
ask [ "AASEL" .. aacnt ] = dialog.AddCheckbox ( aalabel, PST.AAMaster [ key ].sel )
end
end
ask.OK = dialog.AddButton ( "OK", 1 )
if aaindx > 1 then
ask.prev = dialog.AddButton ( "Prev", 2 )
end
if aamax < AAMSIZE then
ask.next = dialog.AddButton ( "Next", 3 )
end
ask.Cancel = dialog.AddButton ( "Cancel", 0 )
repeat
rc = dialog.Show ( ask )
if rc > 0 then
aacnt = 0
for key, value in pairs ( PST.AAMaster ) do
aacnt = aacnt + 1
if aacnt >= aaindx and aacnt <= aamax then
PST.AAMaster [ key ].sel = ask [ "AASEL" .. aacnt ].value
end
if aacnt > aamax then
break
end
end
end
if rc == 2 then
rc = GetAAs ( aaindx - AAMPAGE )
end
if rc == 3 then
rc = GetAAs ( aaindx + AAMPAGE )
end
until rc < 2
return rc
end
function Init ()
PST.segCnt = structure.GetCount()
PST.segCnt2 = PST.segCnt
while structure.GetSecondaryStructure ( PST.segCnt2 ) == "M" do
PST.segCnt2 = PST.segCnt2 - 1
end
local tsel = {}
for ii = 1, PST.segCnt do
PST.sel [ #PST.sel + 1 ] = selection.IsSelected ( ii )
if PST.sel [ #PST.sel ] then
tsel [ #tsel + 1 ] = ii
end
end
PST.selCnt = #tsel
if PST.selCnt > 0 then
SPX.sKeep = true
end
PST.selSet = SLT:SegmentListToSet ( tsel )
SPX.ranges = SLT:SegmentSetToString ( PST.selSet ) -- "ranges" starts with existing selections
--
-- measure distances between alpha carbons
--
for ii = 1, PST.segCnt - 1 do
PST.segDist [ #PST.segDist + 1 ] = structure.GetDistance ( ii, ii + 1 )
end
PST.segDist [ #PST.segDist + 1 ] = 9999 -- large distance
--
-- determine chains, first make a list
--
local chndx = 1
for ii = 1, #PST.segDist do
PST.chain [ #PST.chain + 1 ] = PST.CHAINID [ chndx ]
if PST.segDist [ ii ] > PST.ACRF then
chndx = chndx + 1
end
end
--
-- convert chain list to set (TODO: what would Timo think here?)
--
local chainID, chStart, chStop
chainID = PST.chain [ 1 ]
chStart = 1
chStop = 1
for ii = 2, #PST.chain do
if PST.chain [ ii ] ~= chainID then
PST.chainSet [ #PST.chainSet + 1 ] = { chain = chainID, start = chStart, stop = chStop, len = chStop - chStart + 1, sel = false, }
chainID = PST.chain [ ii ]
chStart = ii
end
chStop = ii
end
PST.chainSet [ #PST.chainSet + 1 ] = { chain = chainID, start = chStart, stop = chStop, len = chStop - chStart + 1, sel = false, }
if #PST.chainSet == 1 then
print ( "single chain" )
else
print ( #PST.chainSet .. " chains found" )
end
for ii = 1, #PST.chainSet do
print ( "chain " .. PST.chainSet [ ii ].chain .. ", " .. PST.chainSet [ ii ].start .. "-" .. PST.chainSet [ ii ].stop .. ", len = " .. PST.chainSet [ ii ].len )
end
print ( "--" )
end
function chainCheck ( seg ) -- returns true if seg is in a selected chain
for ii = 1, #PST.chainSet do
if PST.chainSet [ ii ].sel
and seg >= PST.chainSet [ ii ].start
and seg <= PST.chainSet [ ii ].stop then
return true
end
end
return false
end
function aaCheck ( seg ) -- returns true if seg is a selected amino acid
local aax = PST.AAMaster [ structure.GetAminoAcid ( seg ) ]
if aax ~= nil and aax.sel then
return true
end
return false
end
function main ()
print ( ReVersion )
print ( "Puzzle: " .. puzzle.GetName () )
local trk = ui.GetTrackName ()
if trk ~= "default" then
print ( "Track: " .. trk )
end
Init ()
if not GetParameters () then
return
end
print ( "options:" )
if SPX.sUnlockedBB then
print ( "select unlocked backbone" )
end
if SPX.sUnlockedSC then
print ( "select unlocked sidechain" )
end
if SPX.sUnfrozen then
print ( "select frozen" )
end
if SPX.sPhobic then
print ( "select hyrdophobics" )
end
if SPX.sPhilic then
print ( "select hydrophilics" )
end
if #PST.chainSet > 1 then
local chsels = 0
local chainSel = ""
for ii = 1, #PST.chainSet do
if PST.chainSet [ ii ].sel then
chsels = chsels + 1
if chsels > 1 then
chainSel = chainSel .. ", "
end
chainSel = chainSel .. PST.chainSet [ ii ].chain
end
end
if chsels > 0 then
SPX.sChain = true
cwd = "chain"
if chsels > 1 then
cwd = "chains"
end
print ( "select " .. chsels .. " " .. cwd .. " (" .. chainSel .. ")" )
end
end
local aacnt = 0
local aasel = 0
local aaStr = ""
for key, value in pairs ( PST.AAMaster ) do
aacnt = aacnt + 1
if PST.AAMaster [ key ].sel then
aasel = aasel + 1
if aasel > 1 then
aaStr = aaStr .. ", "
end
aaStr = aaStr .. key
end
end
if aasel > 0 then
SPX.sAA = true
local aawd = "amino acid"
if aasel > 1 then
aawd = "amino acids"
end
print ( "select " .. aasel .. " " .. aawd .. " (" .. aaStr .. ")" )
end
if #SPX.rtab > 0 then
SPX.sSeg = true
print ( "select segments " .. SLT:SegmentSetToString ( SPX.rtab ) )
end
if SPX.sKeep then
print ( "keep existing selections" )
end
if SPX.sInvert then
print ( "invert new selections" )
end
if SPX.sLigand then
print ( "include ligand(s)" )
end
print ( "--" )
--
-- do it
--
local sels = 0
if not SPX.sKeep then
selection.DeselectAll ()
for ii = 1, PST.segCnt do
PST.sel [ ii ] = false
end
print ( "existing selections cleared" )
else
for ii = 1, PST.segCnt do
if selection.IsSelected ( ii ) then
sels = sels + 1
end
end
print ( sels .. " existing selections" )
end
local fsels = {} -- fsels is a segment set list of what's to be selected
for ii = 1, PST.segCnt do
local sellit = false
local lbb = false
local lsc = false
if SPX.sUnlockedBB or SPX.sUnlockedSC then
lbb, lsc = structure.IsLocked ( ii )
end
if SPX.sUnlockedBB and not lbb and not SPX.sInvert then
sellit = true
end
if SPX.sUnlockedBB and lbb and SPX.sInvert then
sellit = true
end
if SPX.sUnlockedSC and not lsc and not SPX.sInvert then
sellit = true
end
if SPX.sUnlockedSC and lsc and SPX.sInvert then
sellit = true
end
local fbb = false
local fsc = false
if SPX.sUnfrozenBB or SPX.sUnfrozenSC then
fbb, fsc = freeze.IsFrozen ( ii )
end
if SPX.sUnfrozenBB and not fbb and not SPX.sInvert then
sellit = true
end
if SPX.sUnfrozenBB and fbb and SPX.sInvert then
sellit = true
end
if SPX.sUnfrozenSC and not fsc and not SPX.sInvert then
sellit = true
end
if SPX.sUnfrozenSC and fsc and SPX.sInvert then
sellit = true
end
if SPX.sPhobic and structure.IsHydrophobic ( ii ) and not SPX.sInvert then
sellit = true
end
if SPX.sPhobic and not structure.IsHydrophobic ( ii ) and SPX.sInvert then
sellit = true
end
if SPX.sPhilic and not structure.IsHydrophobic ( ii ) and not SPX.sInvert then
sellit = true
end
if SPX.sPhilic and structure.IsHydrophobic ( ii ) and SPX.sInvert then
sellit = true
end
if SPX.sChain and chainCheck ( ii ) and not SPX.sInvert then
sellit = true
end
if SPX.sChain and not chainCheck ( ii ) and SPX.sInvert then
sellit = true
end
if SPX.sAA and aaCheck ( ii ) and not SPX.sInvert then
sellit = true
end
if SPX.sAA and not aaCheck ( ii ) and SPX.sInvert then
sellit = true
end
if SPX.sSeg and SLT:SegmentInSet ( SPX.rtab, ii ) and not SPX.sInvert then
sellit = true
end
if SPX.sSeg and not SLT:SegmentInSet ( SPX.rtab, ii ) and SPX.sInvert then
sellit = true
end
local isLigand = structure.GetSecondaryStructure ( ii ) == "M"
if isLigand then
if not SPX.sLigand then
sellit = false
end
end
if sellit then
fsels [ #fsels + 1 ] = ii
end
end
local iranges = SLT:SegmentSetToString ( PST.selSet )
if iranges:len () == 0 then
iranges = "none"
end
print ( "initial selections: " .. iranges )
--
-- do the actual selecting here using ranges
--
local frangez = SLT:SegmentListToSet ( fsels )
local franges = SLT:SegmentSetToString ( frangez )
if franges:len () == 0 then
franges = "none"
end
print ( "final selections: " .. franges )
for ii = 1, #frangez do
selection.SelectRange ( frangez [ ii ] [ 1 ], frangez [ ii ] [ 2 ] )
end
--
-- determine number of changed segments
--
local cnt = 0
for ii = 1, PST.segCnt do
if selection.IsSelected ( ii ) and not PST.sel [ ii ] then
cnt = cnt + 1
end
end
--
-- determine number of errors
--
local errs = 0
for ii = 1, #fsels do
if not selection.IsSelected ( fsels [ ii ] ) then
errs = errs + 1
end
end
print ( cnt .. " selections added" )
if errs > 0 then
print ( errs .. " selection errors (locked segments, etc.) " )
end
print ( sels + cnt .. " total segments selected" )
cleanup ()
end
function cleanup ( errmsg )
if CLEANUPENTRY ~= nil then
return
end
CLEANUPENTRY = true
print ( "---" )
local reason
local start, stop, line, msg
if errmsg == nil then
reason = "complete"
else
--
-- civilized error reporting,
-- thanks to Bruno K. and Jean-Bob
--
start, stop, line, msg = errmsg:find ( ":(%d+):%s()" )
if msg ~= nil then
errmsg = errmsg:sub ( msg, #errmsg )
end
if errmsg:find ( "Cancelled" ) ~= nil then
reason = "cancelled"
else
reason = "error"
end
end
print ( ReVersion .. " " .. reason )
print ( "Puzzle: " .. puzzle.GetName () )
local trk = ui.GetTrackName ()
if trk ~= "default" then
print ( "Track: " .. trk )
end
if reason == "error" then
print ( "Unexpected error detected" )
print ( "Error line: " .. line )
print ( "Error: \"" .. errmsg .. "\"" )
end
end
xpcall ( main , cleanup )