Code
--[[
Structure Changes V3.100 by Crashguard303
part of my "cardinal segment detection" used in GA+EO Remix
finds changes in secondary structures and
creates a segment list, where changes do occur.
can also:
-- freeze complete puzzle,
then unfreeze detected segments to use them as "ankles" or
-- band detected segments to use them as scaffolding nodes
you can use this script:
-- just for detection, or
-> to protect the puzzle from breaking apart
while wiggling and turning the CI to low values ;)
]]--
function structure_changes_append(structure_changes,seg_count)
-- scan secondary structures of all puzzle segments (1 to seg_count)
-- append segment indices with ss changes to table structure_changes
local structure_changes= structure_changes
local seg_count2= seg_count-1
local i=1
-- initialize segment iterator
-- (position 1 -> first segment of puzzle)
while i<seg_count2 do
-- with iterator i,
-- cycle through all puzzle segments (but not last)
local ss1=structure.GetSecondaryStructure(i)
-- check ss of segment with index i
local ss2=structure.GetSecondaryStructure(i+1)
-- check ss of segment behind index i
if ss1~=ss2
-- are ss different?
-- primary, we want to find segments with loop-ss at the end of helix or sheet sections
-- but sometimes helices are directly near (adjacent to) sheets
then if ss1=="L"
-- loop found at i? (-> other ss: behind i)
then table.insert(structure_changes,i)
elseif ss2=="L"
-- loop found directly after i? (-> other ss: at i)
then table.insert(structure_changes,(i+1))
i=i+1
elseif ss1=="H"
-- helix found at i? (-> other ss (but no loop): behind i)
then table.insert(structure_changes,i)
elseif ss2=="H"
-- helix found directly after i? (-> other ss (but no loop): at i)
then table.insert(structure_changes,(i+1))
i=i+1
end -- if ss1,ss2
end -- if ss1~=ss2
i=i+1
-- increase iterator
-- (next position -> next segment)
end -- while i<seg_count2
return structure_changes
end -- structure_changes_append()
function structure_changes_filter(structure_changes)
-- remove duplicate entries from table structure_changes
local structure_changes= structure_changes
local i=1
-- initialize element iterator
-- (position 1 -> first element of list)
while i<=(#structure_changes-1) do
-- with iterator i,
-- cycle through all (but not last) elements in list
if structure_changes[i]==structure_changes[i+1]
-- element at current position identical with direct next one?
then table.remove(structure_changes,(i+1))
-- remove next one
i=i-1
-- as element was removed,
-- we need to shift iterator
end -- if structure_changes[i]==structure_changes[i+1]
i=i+1
-- increase iterator
-- (next position -> next element)
end
return structure_changes
end -- structure_changes_filter()
function structure_changes_find(option)
-- detect structure changes
-- option.first=true: generally add first segment
-- option.last= true: generally add last segment
print("Detecting changes...")
local seg_count=structure.GetCount()
-- get number of segments
local structure_changes={}
-- initialize found list table
if option.first -- activated?
then table.insert(structure_changes,1)
-- add first segment to list
end -- if option.first
structure_changes= structure_changes_append(structure_changes,seg_count)
-- get segment numbers, where structure changes do occur
if option.last -- activated?
then table.insert(structure_changes,seg_count)
-- add last segment to list
end -- if option.last
structure_changes= structure_changes_filter(structure_changes)
-- remove duplicate entries
return structure_changes
end -- structure_changes_find()
function structure_changes_show(structure_changes,option)
-- show detected structure changes
-- if set, do some freezing or banding
-- option.freeze= true: freeze complete puzzle and unfreeze detected segments
-- option.sbands= true: band detected segments with each other (scaffolding)
-- option.freeze_invert= true: invert freezing
-- option.sbands_mode= "grid": scaffold all detected segments with each other
-- option.sbands_mode= "ring": scaffold all detected segments with next only
-- option.sbands_pitch =0: use scaffolding - fix seg's distances, how they are
-- option.sbands_pitch >0: use scaffolding - change seg's distances (expand puzzle)
-- option.sbands_pitch <0: use scaffolding - change seg's distances (compress puzzle)
-- option.sbands_strength: strength of scaffolding
print("\nFound changes:")
if option.freeze -- activated?
then if option.freeze_invert -- activated?
then freeze.UnfreezeAll()
-- unfreeze all segments
else freeze.FreezeAll()
-- freeze all segments
end -- if option.freeze_invert
end -- if option.freeze
local i
-- initialize element iterator
for i=1,#structure_changes do
-- by variable i, cycle through all elements in list
-- giving segments which have been found
print('#'..i..': idx'..structure_changes[i])
-- show segment number
if option.freeze -- activated?
then if option.freeze_invert -- activated?
then freeze.Freeze(structure_changes[i],true,true)
-- freeze segment number
else freeze.Unfreeze(structure_changes[i],true,true)
-- unfreeze segment number
end -- if option.freeze_invert
end -- if option.freeze
end -- for i
if option.sbands -- activated?
then print("\nAdding bands:")
if option.sbands_mode=="ring"
then sbands_apply_ring(structure_changes,option.sbands_pitch,option.sbands_strength)
else -- if option.sbands_mode~="ring" -> =="grid"
sbands_apply_grid(structure_changes,option.sbands_pitch,option.sbands_strength)
end -- if option.sbands_mode
end -- if option.bands
end -- structure_changes_show()
function sbands_apply_ring(structure_changes,sbands_pitch,sbands_strength)
-- using list structure_changes
local i
for i=1,#structure_changes do
-- by variable i (origin segment),
-- cycle through all elements in list structure_changes
-- giving segments which have been found
local j= i+1
-- target segment: next element to i in list
if j>#structure_changes
-- element index out of list range?
then j= 1
-- take first element
end -- if j
band.AddBetweenSegments2(structure_changes[i],structure_changes[j],sbands_pitch,sbands_strength)
-- band segment stored in element i to segment stored in element j
end -- for i
end -- function sbands_apply_ring
function sbands_apply_grid(structure_changes,sbands_pitch,sbands_strength)
-- using list structure_changes
local i
for i=1,(#structure_changes-1) do
-- by variable i (origin segment),
-- cycle from "first element" to "second last element" in list structure_changes
-- giving segments which have been found
local j
for j=(i+1),#structure_changes do
-- by variable j (target segment),
-- cycle from "element behind i" to "last element" in list structure_changes
-- giving other segments which have been found
band.AddBetweenSegments2(structure_changes[i],structure_changes[j],sbands_pitch,sbands_strength)
-- band segment stored in element i to segment stored in element j
end -- for j
end -- for i
end -- function sbands_apply_grid
function band.AddBetweenSegments2(i,j,sbands_pitch,sbands_strength)
-- i: origin segment index
-- j: target segment index
-- sbands_pitch: add this to measured distance, giving band length
-- sbands_strength: strength of bands
if (math.abs(j-i))>1
-- bands not directly near each other?
then local band_GetCount1= band.GetCount()
band.AddBetweenSegments(i,j)
-- band segment i to segment j
local band_GetCount2= band.GetCount()
if band_GetCount2>band_GetCount1
-- has band been added?
then local length= structure.GetDistance(i,j)+sbands_pitch
if length<0
then length= 0
-- to prevent errors
end -- if length
band.SetGoalLength(band_GetCount2,length)
-- set length of applied band to measured distance (scaffolding)
-- +/- sbands_pitch
band.SetStrength(band_GetCount2,sbands_strength)
-- set strength of applied band to sbands_strength
print('#'..band_GetCount2..': idx'..i..':idx'..j..' l:'..length..' s:'..sbands_strength)
end -- if band_GetCount2>
end -- if >1
end -- function band.AddBetweenSegments2
-- get list containing those segments where ss changes occur
-- and remove duplicate entries:
structure_changes= structure_changes_find({first=true;last=true})
-- first=true: generally add first segment
-- last= true: generally add last segment
-- show results:
structure_changes_show(structure_changes,
{freeze=true;
-- activate freezing?
freeze_invert=false;
-- invert freezing?
sbands=false;
-- activate bands?
sbands_mode="ring";
-- band mode?
sbands_pitch=0;
-- change segment distance by?
sbands_strength=1}
-- band strength?
)
-- you should activate either freeze or bands:
-- freeze= true: freeze complete puzzle and unfreeze detected segments (detected segments serve as "ankles")
-- freeze_invert= true: invert freezing
-- sbands= true: band detected segments with each other (detected segments serve as scaffolding nodes)
-- sbands_mode= "grid": scaffold all detected segments with each other
-- sbands_mode= "ring": scaffold all detected segments with next only
-- sbands_pitch: add this value to measured segment distance, giving band length
-- sbands_pitch =0: fix seg's distances, how they are
-- sbands_pitch >0: change seg's distances (expand puzzle)
-- sbands_pitch <0: change seg's distances (compress puzzle)
-- sbands_strength: strength of scaffolding