Code
--[[
* Sheet Straightener
* Original author: Brow42
* Version 0.2 2012/1/4 Brow 42
* Lua v2
* Dialog box for the options
* Can now use selection interface to set ranges
* Version 0.1 2011/12/2 Brow42
* Pushes end of sheets (or loops, or specified ranges) apart.
* Pushing continues until user manually stops the wiggle or
cancels the recipe.
* Original banding is preserved and restored on normal exit.
* Version 0.1 defaults are very aggressive.
* Feel free to freeze sections you don't want straightened.
* To properly straighten loops, either change their SS,
set a range, or freeze, cancel, and remove bands from
loops that must be unfrozen but not straightened.
--]]
-- =================== Begin Options ================
-- Set to false to disable the options dialog and go straight to straightening!
ShowOptions = true
-- 3.2 to 3.4 plus a little extra for tension, de novo is 3.55-3.58
-- Pick smaller for less destructive, but may cause contraction
NominalAADistance = 3.6
-- 0.1 to 10 depending on how much you really mean it
BandStrength = 3.0
-- Add start/stop indices of where you want to straighten, or leave commented out
--[[
regions= {
{3,10},{27,20}
}
--]]
AutoSelectType='E' -- E, H, or L if you didn't specify specific regions
-- Doesn't work with H, too much twist
-- Set to true if you prefer to use the selection interface
UseSelection = false
-- Method Priority: Ranges > Selection > Structure
-- True to disable pre-existing bands, false to leave them in their current state
DisableBands=false
nWiggle = 10
_debuglevel = 0
-- ================= End Options ====================
-- globals
version = 0.2
title = 'Sheet Straightener v '..tostring(version)
regions = regions or {} -- make sure regions is a table
nSeg = structure.GetCount()
function debugmsg(str,lvl) if (lvl or 1) <= _debuglevel then print(str) end end
-- Parse a string into regions
function Parse(str)
debugmsg('Entering Parse')
ranges = {}
local _start, _end, i, j
local pat1 = '%s*%d+'
local pat2 = '%s*,'
while true do
i,j = string.find(str,pat1)
if i == nil then break end
_start = string.sub(str,i,j)
str = string.sub(str,j+1)
i,j = string.find(str,pat2)
if i == nil then break end
str = string.sub(str,j+1)
i,j = string.find(str,pat1)
if i == nil then break end
_end = string.sub(str,i,j)
_start = tonumber(_start)
_end = tonumber(_end)
if _start == nil or _end == nil then break end
table.insert(ranges,{_start,_end})
end
debugmsg('Found '..tostring(#ranges)..' ranges.')
return ranges
end
-- Error dialog
function ErrorDialog(msg,button)
d = dialog.CreateDialog(title)
d.error = dialog.AddLabel(msg)
d.button = dialog.AddButton(button,0)
dialog.Show(d)
end
-- Options dialog
-- Return 1 okay, 0 cancel, -1 error
function Options()
debugmsg('Entering Options()')
local d = dialog.CreateDialog(title)
local regions_set = #regions > 0
local default_regions = ''
for iReg = 1,#regions do
if iReg > 1 then default_regions = default_regions..' ' end
default_regions = default_regions..regions[iReg][1]..','..regions[iReg][2]
end
d.label = dialog.AddLabel('Selection Method:')
d.sheet = dialog.AddCheckbox('Sheets',AutoSelectType=='E' and not regions_set and not UseSelection)
d.helix = dialog.AddCheckbox('Helices',AutoSelectType=='H' and not regions_set and not UseSelection)
d.loop = dialog.AddCheckbox('Loops',AutoSelectType=='L' and not regions_set and not UseSelection)
d.selected = dialog.AddCheckbox('Selected in Selelection Interface',UseSelection and not regions_set)
d.specified = dialog.AddCheckbox('Specified below start,stop <space> ',regions_set)
d.ranges = dialog.AddTextbox('Ranges:',default_regions)
d.label4 = dialog.AddLabel('Set as to how straight you REALLY want it:')
d.label5 = dialog.AddLabel('(Large values may hurt backbone)')
d.band = dialog.AddSlider('Band Strength:',BandStrength,0.1,10.0,1)
d.label2 = dialog.AddLabel('Typical is 3.2 to 3.4, de novo is 3.55 to 3.58')
d.label3 = dialog.AddLabel('Pick smaller for less destructive, but may actually bend the sheet!')
d.dist = dialog.AddSlider('AA Distance:',NominalAADistance,2.3,4.0,2)
d.disable = dialog.AddCheckbox('Disable Bands:',DisableBands)
d.button = dialog.AddButton('Start!',1)
d.quit = dialog.AddButton('Wait, no.',0)
local rc = dialog.Show(d)
if rc == 0 then return 0 end
local nSelected = 0
regions={}
UseSelection=false
SelectionFunction = IsMatchingStructure
-- parse and error check options
if d.sheet.value then
SelectionFunction = IsMatchingStructure
AutoSelectType = 'E'
nSelected = nSelected+1
end
if d.helix.value then
SelectionFunction = IsMatchingStructure
AutoSelectType = 'H'
nSelected = nSelected+1
end
if d.loop.value then
SelectionFunction = IsMatchingStructure
AutoSelectType = 'L'
nSelected = nSelected+1
end
if d.selected.value then
SelectionFunction = IsSelected
UseSelection = true
nSelected = nSelected+1
end
if d.specified.value then
regions = Parse(d.ranges.value)
nSelected = nSelected+1
if #regions == 0 then
ErrorDialog('Your range list was empty or not parsable.','Oops!')
return -1
end
end
NominalAADistance = d.dist.value
BandStrength = d.band.value
DisableBands = d.disable.value
if nSelected ~= 1 then
ErrorDialog('Pick exactly one method, please!','Oops!')
return -1
end
return 1
end
-- A selection function, true for all AAs that are the same as AutoSelectType
function IsMatchingStructure(iSeg)
debugmsg('IsMatchingStructure: '..tostring(iSeg)..structure.GetSecondaryStructure(iSeg)..AutoSelectType,2)
return structure.GetSecondaryStructure(iSeg) == AutoSelectType
end
-- A selection function, true for all selected AAs
function IsSelected(iSeg)
debugmsg('IsSelected: '..tostring(iSeg)..tostring(selection.IsSelected(iSeg)),2)
return selection.IsSelected(iSeg)
end
-- Find sequences satisfying the selection function
function IdentifyRanges(func)
debugmsg('Entering IdentifyRanges()')
local isActive= false
for iSeg=1,nSeg do
if func(iSeg) and not isActive then
isActive=true
iStart = iSeg
end
if isActive and not(func(iSeg)) then
isActive=false
table.insert(regions,{iStart,iSeg-1})
end
end
if isActive then
table.insert(regions,{iStart,nSeg})
end
debugmsg('Regions found: '..tostring(#regions))
end -- Identify Ranges
function math.round(x,n)
n = math.max(n or 0, 0)
return math.floor(x * 10^n+0.5)/10^n
end
-- =============== Begin Main Program ===================
if ShowOptions then
repeat
local rc = Options()
if rc == 0 then return end
until rc == 1
end
-- Disable existing bands
nBandOrig = band.GetCount()
if DisableBands then
band.DisableAll()
end
if #regions == 0 then
SelectionFunction = SelectionFunction or IsMatchingStructure
IdentifyRanges(SelectionFunction)
end
-- Create bands between sheet ends
debugmsg('Creating Bands')
iBand = nBandOrig
for iReg=1,#regions do
s1,s2 = regions[iReg][1], regions[iReg][2]
debugmsg(tostring(iReg)..' '..tostring(s1)..' '..tostring(s2))
if (s1 > s2) then s1,s2 = s2,s1 end -- Check on user input
if s1 < 1 then s1 = 1 end
if s2 > nSeg then s2 = nSeg end
if (s2 - s1 > 1) then -- Cannot band to self or adjacent AA
d = structure.GetDistance(s1,s2)
print('Region #',iBand-nBandOrig+1,' ',s1,' - ',s2, ': ',math.round(d/(s2-s1),2))
d = NominalAADistance * (s2-s1)
local old = iBand
iBand = band.AddBetweenSegments(s1,s2)
if iBand > old then -- make sure band got added
band.SetGoalLength(iBand,d)
band.SetStrength(iBand,BandStrength)
end
end
end
if iBand == nBandOrig then
print('No regions meeting your criteria were found.')
else
print()
print('Pushing apart to distance of ',NominalAADistance)
print('Hit Stop at any time (or Cancel to retain bands).')
print('Wiggling for',nWiggle,'iterations...')
structure.WiggleAll(nWiggle,true,true)
print('Stopped.')
end
-- Remove my bands
debugmsg('Removing Bands.')
nBand = band.GetCount()
for iBand = nBand,1+nBandOrig,-1 do band.Delete(iBand) end