Icon representing a recipe

Recipe: Band Between Atoms 1.01 -- brow42

created by brow42

Profile


Name
Band Between Atoms 1.01 -- brow42
ID
102533
Shared with
Public
Parent
None
Children
None
Created on
December 03, 2017 at 20:29 PM UTC
Updated on
December 03, 2017 at 20:29 PM UTC
Description

Interactive LUA2 script to band any two atoms in the puzzle together. Works around residue and atom number restrictions. Contains short function for use in other scripts.

Best for


Code


--[[ * Band Between Atoms -- Brow42 * Original Author: Brow42 * Version 1.0 Nov 7, 2017 * Bands between two atoms, even on the same or adjacent residue * This works around restrictions in band.AddBetweenSegments by * banding to a distant residue, then banding to an endpoint. * 1.01 add error trap for builtin band, deletes helper band on addfail. --]] Helper = 1 -- This is a bandable residue that is not adjacent or equal to the target residue HELPERFAIL=-1 ADDFAIL=-2 FAIL=0 -- Returns band id of new band, or zero or negative value on error. Uses Helper global variable function AddBetweenSegments(res1, res2, atom1, atom2) -- atom arguments are optional if math.abs(res2-res1) > 1 then success,addedband = pcall(band.AddBetweenSegments(res1,res2,atom1,atom2)) if not success then print("Error making interresidue band:",addedband) return ADDFAIL end return addedband end success, helperband = pcall(band.AddBetweenSegments,Helper,res2,0,atom2) if not success then print("Error making helper band:",helperband) return HELPERFAIL end success, addedband = pcall(band.AddToBandEndpoint,res1,helperband,atom1) if not success then band.Delete(helperband) print("Error making requested band:",addedband) return ADDFAIL end band.Delete(helperband) return band.GetCount() end -- End generalized AddBetweenSegments -- Begin helper and menu routines for Band Between Atoms title = "Band Between Atoms 1.01" print(title) options = { res1=0, res2=0, atom1=0, atom2=0, helper=1, str=1.0, goal=3.5 } -- default values NSeg = structure.GetCount() NOriginalBands = band.GetCount() function DeleteAddedBands() if NOriginalBands == 0 then band.DeleteAll() end while band.GetCount() > NOriginalBands do band.Delete(NOriginalBands+1) end end -- Returns 0 if no bandable pair exists in the puzzle (unlikely) -- Helper is not adjacent to either residue specified function FindHelper(excluded1,excluded2) for i = 1,structure.GetCount()-2 do local skip = (excluded1 and math.abs(i-excluded1)<=1) or (excluded2 and math.abs(i-excluded2)<=1) if not skip then for j = i+2,structure.GetCount() do -- I was going to do band to space but that's just extra work success,bandid = pcall(band.AddBetweenSegments,i,j) if success and bandid > 0 then band.Delete(bandid) return i end end end end return 0 -- fail end -- ================================== Begin New Dialog Library Functions -- Add a wall of text from a table function dialog.AddLabels(d,msg,nlabels) -- pass in # of existing autolabels local nlabels = nlabels or #(d._Order or {}) -- default, valid if never delete dialog elements if type(msg) == 'string' then msg = { msg } end for i = 1,#msg do d['autolabel'..tostring(i+nlabels)] = dialog.AddLabel(msg[i]) end end -- Create but don't display a wall of text and 1 or 2 buttons function dialog.CreateMessageBox(msg,title,buttontext1,buttontext0,buttontext2) title = title or '' local d = dialog.CreateDialog(title) dialog.AddLabels(d,msg) buttontext1 = buttontext1 or 'Ok' d.button = dialog.AddButton(buttontext1,1) if buttontext2 ~= nil then d.button2 = dialog.AddButton(buttontext2,2) end if buttontext0 ~= nil then d.button0 = dialog.AddButton(buttontext0,0) end return d end -- Display a dialog box function dialog.ShowMessageBox(msg,title,buttontext1,buttontext0,buttontext2) return dialog.Show(dialog.CreateMessageBox(msg,title,buttontext1,buttontext0,buttontext2)) end -- Display a box AND print to the output function ErrorMessage(msg,title) if type(msg) == 'string' then msg = { msg } end for i = 1,#msg do print(msg[i]) end return dialog.ShowMessageBox(msg,title) end -- ======================================= End Dialog Functions -- Begin main menu and main() OKAY = 1 FINDHELPER = 2 DATAERROR = 3 FATAL = -1 CANCEL = 0 -- cancel is always 0 function OptionsDump() for u,v in pairs(options) do print(u,v) end end function Validate() if options.res1 < 1 or options.res2 < 1 or options.helper < 1 or options.res1 > NSeg or options.res2 > NSeg or options.helper > NSeg then ErrorMessage({"Residue numbers must be between 1 and","the # of residues."},"Data Entry Error") return false end if options.atom1 < 0 or options.atom2 < 0 then ErrorMessage("Atom numbers must be 0 (default atom) or positive.","Data Entry Error") return false end if options.goal <= 0.0 then ErrorMessage("The goal length must be > 0.", "Data Entry Error") return false end if options.res1 == options.res2 and options.atom1 == options.atom2 then ErrorMessage("You seem to be banding an atom to itself!") return false end return true end function MainMenu() local d = dialog.CreateDialog(title) d["label1"] = dialog.AddLabel("This script adds a band between any two atoms on") d["label2"] = dialog.AddLabel("any two residues. It works even if banding between") d["label3"] = dialog.AddLabel("two atoms on the same residue, or adjacent residues,") d["label4"] = dialog.AddLabel("but this requires an extra helper residue. Click the") d["label5"] = dialog.AddLabel("Helper button to find a helper that works.") d["label6"] = dialog.AddLabel("") d["label7"] = dialog.AddLabel("Atom 0 is a default atom, but you should avoid") d["label8"] = dialog.AddLabel("using it on a single residue to avoid banding") d["label9"] = dialog.AddLabel("an atom to itself!") d["label10"] = dialog.AddLabel("") d["res1"] = dialog.AddTextbox("Residue 1:",tostring(options.res1)) d["res2"] = dialog.AddTextbox("Residue 2:",tostring(options.res2)) d["atom1"] = dialog.AddTextbox("Atom 1:",tostring(options.atom1)) d["atom2"] = dialog.AddTextbox("Atom 2:",tostring(options.atom2)) d["helper"] = dialog.AddTextbox("Helper residue:",tostring(options.helper)) d["str"] = dialog.AddSlider("Strength:",tostring(options.str),0.1,10,1) d["goal"] = dialog.AddTextbox("Goal Length:",tostring(options.goal)) d["ok"] = dialog.AddButton("OK",OKAY) d["find"] = dialog.AddButton("Helper",FINDHELPER) d["cancel"] = dialog.AddButton("Cancel",CANCEL) local rc = dialog.Show(d) if rc == CANCEL then return CANCEL end local conv,convfail = nil,false for u,v in pairs(options) do conv = tonumber(d[u].value) if conv then options[u] = conv else convfail = true end end if convfail == true then ErrorMessage("Numerical values only please!","Data Entry Error") return DATAERROR end if Validate() == false then return DATAERROR end NeedHelper = math.abs(options.res2-options.res1) <= 1 if rc == FINDHELPER then options.helper = FindHelper(options.res1,options.res2) if options.helper == 0 then ErrorMessage({"Couldn't find a second bandable residue.","You might not be able to use this script on this puzzle."},"No Helper Found!") return FATAL else ErrorMessage("New helper picked!","Helper Residue") print("New helper residue is",options.helper) end else if NeedHelper and math.abs(options.res2 - options.helper) <= 1 then ErrorMessage({"Helper is too close to Residue 2. Swap Residue 1","and Residue 2 or pick a new helper."},"Helper Residue") return DATAERROR end end return rc end -- ========================= Begin Main repeat rc = MainMenu() if rc == CANCEL then print("User cancel.") end if rc == CANCEL or rc == FATAL then return end until rc == OKAY Helper = options.helper NewBand = AddBetweenSegments(options.res1,options.res2,options.atom1,options.atom2) if NewBand == HELPERFAIL then ErrorMessage({"Couldn't band between the helper residue and the target.", "Check the error message in the output window and","message the script author.", "(perhaps atom number too high?)"},title) OptionsDump() return end if NewBand == ADDFAIL then ErrorMessage({"Couldn't make the desired band", "Check the error message in the output window and","message the script author.", "(perhaps atom number to high?)"},title) OptionsDump() return end if NewBand == 0 then ErrorMessage({"Something went wrong, the band couldn't be made."},title) OptionsDump() return end band.SetStrength(NewBand,options.str) band.SetGoalLength(NewBand,options.goal) print(string.format("New band #%d\nCreated from res %d, atom %d to res %d, atom %d,", NewBand,options.res1,options.atom1,options.res2,options.atom2)) print(string.format("with strength %.2f, goal length %.2f, and current length %.2f", options.str,options.goal,band.GetLength(NewBand))) if band.GetLength(NewBand) == 0.0 then ErrorMessage("You've made a zero length band! I'm deleting it.","Warning: Black Hole Ahead") band.Delete(NewBand) end print "Done."

Comments


brow42 Lv 1

Currently, Foldit will not make the following bands with LUA:

  • Band the default (c-alpha) atom on adjacent residues,
  • Band two atoms on the same residue.

This script circumvents this restriction by making a temporary band to one of the atoms, and then using AddToBandEndpoint to make the desired band, and provides some error handling so that illegal residue/atom pair don't crash the script.

For other situations than the above, you can use the regular AddBetweenSegments or scripts like bandsome https://fold.it/portal/recipe/43861 .