Icon representing a recipe

Recipe: Freeze Selected 1.0

created by LociOiling

Profile


Name
Freeze Selected 1.0
ID
103337
Shared with
Public
Parent
None
Children
Created on
March 22, 2020 at 20:39 PM UTC
Updated on
March 22, 2020 at 20:39 PM UTC
Description

Freezes selected segment, optionally adding backbone "pegs" (strong zero-length bands). Requires selection interface. From a suggestion by Keresto, with some code from Paul Dunn.

Best for


Code


-- -- Freeze Selected - freezes selected segments -- -- As suggested by Keresto on Foldit Discord, 22 March 2020 -- -- Optionally, adds a strong zero-length band to each segment, -- thanks to Paul Dunn for the original -- -- 1.0 - LociOiling - 22 March 2020 -- + slightly streamlined version of a complete recipe, see notes at end -- -- -- global variables -- Recipe = "Freeze Selected" Version = "1.0" ReVersion = Recipe .. " " .. Version frbb = true -- dialog option: freeze backbone frsc = false -- dialog option: freeze backbone pegbb = false -- dialog option: peg backbone with short zero-length band pegstr = 10 -- dialog option: peg strength -- -- end of global variables -- -- -- GetOpt - create a dialog, prompt user for options -- function GetOpt () -- -- create the dialog - "menu" can be any name -- local menu = dialog.CreateDialog ( ReVersion ) -- -- add fields to the dialog -- menu.frbb = dialog.AddCheckbox ( "freeze backbone", frbb ) menu.frsc = dialog.AddCheckbox ( "freeze sidechain", frsc ) menu.backbone = dialog.AddCheckbox ( "peg backbone", pegbb ) menu.strength = dialog.AddSlider ( "peg strength", pegstr, 0, 10, 1 ) -- -- add buttons to the dialog, and the value that's returned when they're clicked -- menu.Run = dialog.AddButton ( "Run", 1 ) menu.Cancel = dialog.AddButton ( "Cancel", 0 ) -- -- display the dialog -- local rc = dialog.Show ( menu ) -- -- if the user clicked "Run", retrieve the values using the "value" operator if rc == 1 then frbb = menu.frbb.value frsc = menu.frsc.value pegbb = menu.backbone.value pegstr = menu.strength.value end return rc > 0 end function main () Ident ( ReVersion ) -- identify the recipe and the puzzle print ( "--" ) -- -- call the GetOpt function to get the options, -- then go ahead if the user clicks "Run" -- if GetOpt () then print ( "Options:" ) print ( "freeze backbone = " .. tostring ( frbb ) ) -- use tostring for true/false boolean values print ( "freeze sidechain = " .. tostring ( frsc ) ) -- use tostring for true/false boolean values print ( "peg backbone = " .. tostring ( pegbb ) ) -- use tostring for true/false boolean values if pegbb then print ( "peg strength = " .. pegstr ) -- number and string don't need tostring end undo.SetUndo ( false ) -- allow user to undo in one step behavior.SetFiltersDisabled ( true ) -- turn off filters for speed lastseg = structure.GetCount() -- get the total number of segments local slcount = 0 -- number of selected segments local bbcount = 0 -- number of backbone freezes local sccount = 0 -- number of sidechain freezes local pegcount = 0 -- number of backbone pegs added -- -- for each segment -- for seg = 1, lastseg do -- -- if the segment is selected -- if selection.IsSelected ( seg ) then -- -- freeze the segment (don't worry about whether it's already frozen) -- -- the backbone and sidechain can be frozen separately -- freeze.Freeze ( seg, frbb, frsc ) -- -- check to see what's frozen -- local bbfrz, scfrz = freeze.IsFrozen ( seg ) if bbfrz then bbcount = bbcount + 1 end if scfrz then sccount = sccount + 1 end -- -- if we're added "pegs"... -- if pegbb then -- -- determine a couple of reference segments -- local segx = seg - 1 local segy = seg + 1 if segx < 1 then segx = lastseg end if segy > lastseg then segy = 1 end -- -- add a short (length 1e-10 angstrom) band -- local b = band.Add ( seg, segx, segy, 1e-10, 0, 0) if b ~= nil and b ~= 0 then band.SetGoalLength ( b, 0 ) -- this band wants to be zero angstroms long band.SetStrength ( b, pegstr ) -- set strength of band pegcount = pegcount + 1 end end end end -- -- print out the totals -- print ( slcount .. " segments selected" ) print ( bbcount .. " segment backbones frozen" ) print ( sccount .. " segment sidechains frozen" ) print ( pegcount .. " backbone pegs added" ) print ( band.GetCount () .. " total bands" ) end -- -- exit using the cleanup routine -- cleanup () end -- -- Ident - print identifying information at beginning and end of recipe -- function Ident ( slugline ) local function round ( ii ) return ii - ii % 0.001 end print ( slugline ) print ( "Puzzle: " .. puzzle.GetName () .. " (" .. puzzle.GetPuzzleID () .. ")" ) print ( "Track: " .. ui.GetTrackName () ) local luser = user.GetPlayerName () local scoretype = scoreboard.GetScoreType () local scort = "" if scoretype == 0 then scort = "soloist" elseif scoretype == 1 then scort = "evolver" elseif scoretype == 2 then scort = "all hands" elseif scoretype == 3 then scort = "no score" else scort = "unknown/error" end print ( "User: " .. luser ) print ( "Rank: " .. scoreboard.GetRank ( scoretype ) .. " (" .. scort .. ")" ) local sGroup = scoreboard.GetGroupScore () if sGroup ~= nil then print ( "Group rank / score: " .. scoreboard.GetGroupRank () .. " / " .. round ( 10 * ( 800 - sGroup ) ) ) end 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 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 Ident ( ReVersion .. " " .. reason ) if reason == "error" then print ( "Unexpected error detected" ) print ( "Error line: " .. line ) print ( "Error: \"" .. errmsg .. "\"" ) end -- -- turn on undo again -- undo.SetUndo ( true ) -- -- turn on filters again -- behavior.SetFiltersDisabled ( false ) end xpcall ( main, cleanup ) -- -- -- Freeze Selected - program notes -- -- Thanks to Keresto for the suggestion -- -- Thanks for Paul Dunn for the original "peg" logic -- -- Thanks to Bruno Kestemont and Jean-Bob for the cleanup logic -- that deciphers the error message -- -- As the name implies, Freeze Selected freezes any selected segments. -- -- This recipe really requires the selection interface, since you -- can't select segments in the original interface. -- -- Optionally, Freeze Selected adds a zero-length band to the backbone -- of each segment it freezes. -- -- Freezing and zero-length bands are useful during hand-folding. -- They can help keep the protein from changing shape too much -- during wiggle. -- -- This recipe also demonstrates a typical pattern, plus some -- bells and whistles I like to add. -- -- First, the recipe is bottom-up. The first statement that actually -- "runs" is the xpcall, located just above these comments: -- -- xpcall ( main, cleanup ) -- -- xpcall is built in to Lua, and provides error handling. -- -- The names "main" and "cleanup" can be any Lua name, but you -- must provide matching functions. -- -- xpcall is going to call the function "main", and if any errors -- occur, the function "cleanup" will catch the error. -- -- In this example, all the cleanup function does is reformat the -- Lua error message a little, making it easier to read. The cleanup -- can also reset things. In this case, cleanup turns filters back on, -- since they were likely turned off earlier in the recipe. -- -- The "main" function is where most of the action is. It first calls -- the "Ident" function, which just prints some information. Ident -- is in the "bells and whistles" category, but its output can help -- to reduce confusion. -- -- The next step is to call the "GetOpt" function, which displays a -- dialog, prompting the user for options. GetOpt is a typically -- dialog. It calls dialog.CreateDialog, creating a dialog instance -- called "menu". Again, "menu" can be any Lua name. Then GetOpt -- adds various fields to the dialog, and calls dialog.Show to display -- the dialog. -- -- In GetOpt, "menu" is actually a Lua table. Tables Are Everywhere -- in Lua. -- -- The dialog.Show returns different integer value, depending on which -- button the user clicked. In this case, the "Run" button returns 1, -- which means "go ahead and freeze". GetOpt updates the options in -- this case. -- -- Each of the options in "menu" is in turn a table, and each option -- table has a field "value", which contains the final value of the -- option. So a line like -- -- frbb = menu.frbb.value -- -- retrieves the value of the option frbb from the menu table, and -- stores it in the variable "frbb". Using the name "frbb" for both -- the lone variable and the menu table entry is just a convention, -- again any valid Lua name will do, but keeping them the same is -- less confusing. -- -- One other thing to note is scoping in Lua. The options variables -- like "frbb" are defined up near the top, with a comment indicating -- they are global variables. Actually any variable or function in -- Lua is global, unless you make it "local". This can cause problems -- as your recipe grows, so it's better to be conscious about using -- "local" unless you really really want a global. -- -- Back in main, after the call to GetOpt, these a very typical "for" -- loop, one you'll see in many recipes. It covers each segment in the -- puzzle, from 1 to structure.GetCount. -- -- In the for loop, the first step is to call select.IsSelected to see -- whether the segment ("seg") is selected. Again, the selection -- interface is required for anything to be selected at this point. -- -- If the segment is selected, the recipe calls freeze.Freeze to freeze it. -- -- Optionally, the recipe adds a "peg" at this point, using band.Add. -- -- The wiki explains the very complicated band.Add function: -- -- https://foldit.fandom.com/wiki/Foldit_Lua_Function_band.Add -- -- band.Add's complexity means it's likely to produce an error, which -- is where the cleanup function would come in. The values specified -- in this recipe are pretty safe, however, so not likely to fail. -- -- The same process of checking, freezing, and band is repeated for -- each segment. -- -- The recipe doesn't check to see if a selected segment is already -- frozen. If it's already frozen, it will stay frozen, and freezing -- again doesn't produce an error. -- -- If the user unchecks both "freeze backbone" and "freeze sidechains", -- then nothing gets frozen, but a band will still be added if -- the "peg" option is checked. -- -- The recipe also makes a couple of more obscure calls: -- -- undo.SetUndo ( false ) -- behavior.SetFiltersDisabled ( true ) -- -- The undo.SetUndo call temporarily turns off the undo "stack". -- The player will be able to undo everything the recipe does -- with a single undo (control-z). -- -- The recipe turns on undo again in the cleanup routine. -- -- The behavior.SetFiltersDisabled temporarily turns off any -- filters, to speed things up. Turning off filters also makes -- the score temporarily invalid. The user will see a red line -- through the score while the recipe is running. -- -- The cleanup routine turns filters on again at the end. -- -- The very last thing the "main" function does is call the -- "cleanup" function. This isn't required, but it saves having -- to do things like turning on undo and filters in two places. -- -- When main calls cleanup, it doesn't pass any arguments, so -- cleanup knows it's a normal end, and skips the error checking -- logic. --

Comments


LociOiling Lv 1

Thanks to Keresto for suggesting this recipe. Thanks to Paul Dunn for the basis of the "peg" part.

Freeze Selected will freeze any selected segments, and can also add "pegs" to the backbone. Pegs are strong, zero-length bands.

The recipe requires the selection interface, or else it won't find any selected segments to work with.

Freeze Sidechains freezes the backbone by default, and can also freeze the sidechains. If you uncheck both options, it won't freeze anything.

Freeze Sidechains optionally adds pegs. You can selected the strength of the pegs, from 0 to 10. Pegs are added even if both the freeze options were unchecked.

If you don't select any segments, or if you uncheck both freeze options and the peg option, the recipe doesn't do anything. But it does so gracefully.

This recipe is also intended as a demonstration of a common pattern, where a recipe loops through every segment doing something. Along the way, it adds some bells and whistles, such as use of the xpcall function to catch errors. (Nice, but not essential.)

The recipe also has extensive in-line comments, and a long rambling explanation of what's going on, down at the very end.