Code
--
-- bandsome: interactively place bands between specific atoms in protein
--
-- by jeff101 8/11/12
-- edited by Wipf
--
function Main()
-- recipe information in global variables
RecipeName = "bandsome"
RecipeVersion = "2"
Recipe = RecipeName .. "" .. RecipeVersion
-- set logger variable
wipf.logger = print
local residue_count = structure.GetCount()
local band_count = band.GetCount()
print(string.format( '%s begins at %s on\n puzzle %s\n with puzzle ID %d, %d residues, %d bands,\n and score %.3f.', Recipe, os.date(), puzzle.GetName(), puzzle.GetPuzzleID(), residue_count, band_count, current.GetScore() ))
local symmetry_copy_count
if nil ~= structure.GetSymCount then
symmetry_copy_count = structure.GetSymCount()
else
-- Foldit version older than devprev 2019-Okt-29
-- For testing purposes the count can be set to any number here, it won't crash the recipe.
symmetry_copy_count = 0
end
---- create dialog with default values ----
local from_residue = 36
local to_residue = 72
if from_residue > residue_count then
from_residue = 1
end
if to_residue > residue_count then
to_residue = residue_count
end
local ask=dialog.CreateDialog( Recipe .. " - add band" )
ask.message = dialog.AddLabel("")
--ask.top_separator = dialog.AddLabel("")
-- display this text if and only if the monomer slider is available
if symmetry_copy_count > 0 then
ask.label_w1 = dialog.AddLabel("The first residue is always on the main monomer.")
end
ask.from_residue = dialog.AddSlider("from residue",from_residue,1,residue_count,0)
ask.from_atom = dialog.AddLabel("from atom")
ask.separator1 = dialog.AddLabel("")
-- If there are no symmetric copies, the slider would have a value range from 0 to 0,
-- which foldit's sliders don't like. Thus this will only be there on symmetry puzzles.
---- Wipf, 2019-Nov-09
if symmetry_copy_count > 0 then
ask.to_monomer = dialog.AddSlider( "to monomer", 0, 0,symmetry_copy_count, 0 )
ask.label_w2 = dialog.AddLabel( "monomer 0 is the main monomer,\nthe others are its symmetric copies" )
end
ask.to_residue = dialog.AddSlider("to residue",to_residue,1,residue_count,0)
ask.to_atom = dialog.AddLabel("to atom")
ask.separator2 = dialog.AddLabel("")
ask.length = dialog.AddSlider( "goal length", 2, 0,10, 1 )
ask.strength = dialog.AddSlider( "strength", 10, 0,10, 1 )
ask.bottom_separator = dialog.AddLabel("")
ask.keeplast=dialog.AddSlider( "keep last: ", 0, -1,2,0 )
ask.Label7a=dialog.AddLabel("-1: remove all existing bands\n 0: replace previous band")
--ask.Label7a=dialog.AddLabel("-1: remove all existing bands")
--ask.Label7b=dialog.AddLabel(" 0: replace previous band")
ask.Label7c=dialog.AddLabel(" 1: keep previous band")
ask.Label7d=dialog.AddLabel(" 2: quit now without adding a new band\n ") -- the space in the second line prevents the second line from being ignored, making sure the first line is raised up a little bit
--ask.Label7e=dialog.AddLabel("")
ask.label_w2a = dialog.AddLabel("Different residues have different numbers of atoms.")
ask.label_w2b = dialog.AddLabel("Use the 'update' button after selecting the residues\nto update the atom slider ranges.")
ask.update = dialog.AddButton("update", 2)
ask.OK = dialog.AddButton("OK", 1)
function log_to_dialog( message )
print(message)
ask.message.label = message
end
wipf.logger = log_to_dialog
-- 0 is always the alpha-carbon
local to_monomer
local from_atom = 0
local to_atom = 0
local band_number = 0
local band_length
local band_strength
---- get input values ----
while true do
-- make sure atom numbers are in range
local from_atom_count=structure.GetAtomCount(from_residue)
local to_atom_count=structure.GetAtomCount( to_residue)
if from_atom > from_atom_count then
from_atom = from_atom_count
end
if to_atom > to_atom_count then
to_atom = to_atom_count
end
---- update dialog ----
ask.from_atom = dialog.AddSlider( "from atom", from_atom, 0, from_atom_count, 0 )
ask.to_atom = dialog.AddSlider( "to atom", to_atom, 0, to_atom_count, 0 )
---- display dialog and read the new values ----
local answer = dialog.Show(ask)
ask.message.label = ""
if 0 == answer then
-- user closed the dialog using the red cross at the top corner
break
end
if symmetry_copy_count > 0 then
to_monomer = ask.to_monomer.value
end
from_residue = ask.from_residue.value
to_residue = ask.to_residue.value
from_atom = ask.from_atom.value
to_atom = ask.to_atom.value
band_length = ask.length.value
band_strength = ask.strength.value
local keeplast = ask.keeplast.value
-- update button: skip band deletion and creation
if 2 ~= answer then
---- delete / add bands ----
if keeplast== -1 then
print('Deleting all '..band.GetCount()..' previous bands.')
band.DeleteAll()
elseif keeplast==0 then
if band_number > 0 then
print('Deleting band '..band_number..'...')
band.Delete( band_number )
print(tostring(band.GetCount())..' bands remaining.')
end
elseif keeplast == 2 then
break -- this will quit
end
local aa1=structure.GetAminoAcid(from_residue)
local aa2=structure.GetAminoAcid(to_residue)
local ss1=structure.GetSecondaryStructure(from_residue)
local ss2=structure.GetSecondaryStructure(to_residue)
print('Adding band between atom '..from_atom..' on '..aa1..from_residue..' ('..ss1..')\n and atom '..to_atom..' on '..aa2..to_residue..' ('..ss2..') with strength '..band_strength..',\n goal length '..band_length..'.')
band_number = wipf.AddBandBetweenSegments( from_residue, to_residue, from_atom, to_atom, to_monomer )
if band_number > 0 then
band.SetGoalLength(band_number,band_length) -- 3.5 is default band length
band.SetStrength(band_number,band_strength) -- 10 is max, 1 is default
print(string.format( 'Band %d added, its present length is %.2f.', band_number, band.GetLength(band_number) ))
else
print('Failed to add new band. Still have '..band.GetCount()..' bands.')
end
end -- if (update button)
end -- while
Cleanup()
end -- Main()
-- table for Wipf's functions and variables
wipf={}
--
-- safety wrapper around band.AddBetweenSegments to prevent
-- the game from hanging up
--
function wipf.AddBandBetweenSegments( from_segment, to_segment, from_atom, to_atom, to_symmetric_copy )
-- check atom indices
if nil ~= from_atom then
if from_atom < 0 or from_atom > structure.GetAtomCount(from_segment) then
if nil ~= wipf.logger then
wipf.logger(string.format( "Invalid atom index %d for residue %d (it has %d atoms)!", from_atom, from_segment, structure.GetAtomCount(from_segment) ))
end
return 0
end
end
if nil ~= to_atom then
if to_atom < 0 or to_atom > structure.GetAtomCount(to_segment) then
if nil ~= wipf.logger then
wipf.logger(string.format( "Invalid atom index %d for residue %d (it has %d atoms)!", to_atom, to_segment, structure.GetAtomCount(to_segment) ))
end
return 0
end
end
-- add band
if nil ~= structure.GetSymCount then
-- devprev only currently (2019-Nov-10)
return band.AddBetweenSegments( from_segment, to_segment, from_atom, to_atom, to_symmetric_copy )
else
return band.AddBetweenSegments( from_segment, to_segment, from_atom, to_atom )
end
end
--
-- rounds to nearest integer
--
function round(val)
local res=math.floor(val)
if val-res>=0.5 then
res=res+1
end
return res
end
--
-- cleanup routine to be called whenever the recipe terminates,
-- optionally with an error message
--
function Cleanup( error )
-- Model 120: error reporting
local reason, start, stop, line, msg
if nil == error then
reason = "complete"
else
start, stop, line, msg = error:find( ":(%d+):%s()" )
if msg ~= nil then
error = error:sub( msg )
end
if error:find( "Cancelled" ) ~= nil then
reason = "cancelled"
else
reason = "error"
print( "---- recipe error ----" )
print( "Line number: " .. line )
print( "Error message: " .. error )
end
end
-- Model 100: report recipe, termination reason, puzzle and track
print( "---- terminated: " .. reason .. " ----" )
print('Ending with '..band.GetCount()..' bands for '..structure.GetCount()..' residues.')
print( "Puzzle: " .. puzzle.GetName() )
print( "Track: " .. ui.GetTrackName() )
print( "Recipe: " .. Recipe )
print('All done at '..os.date()..'.')
print( "==== END ====" )
end
-- start main script and catch if it crashes
-- according to rule 'Model 000'
xpcall(Main,Cleanup)