Page 1 of 1

seach through all child layers?

Posted: Fri Aug 05, 2016 2:22 pm
by rylleman
I only manage to loop through the first level of childs and manually down into those one level at a time by adding "if (currentlayer:IsGroupType()) then"-functions.

How can I search through all sublayers and their subgroups (and sublgroups to those, etc.) no matter how deep they go?

Re: seach through all child layers?

Posted: Fri Aug 05, 2016 3:58 pm
by Stan
You need a recursive function (a function that invokes itself).
Here's an example from one of my scripts:

Code: Select all

-- declare a table variable  self.LayersToChange

function SZ_RelativeLineWidth:ScanSelectedLayers(moho)
	for l = 0, moho.document:CountSelectedLayers()-1 do
		local layer = moho.document:GetSelectedLayer(l)
		if moho:LayerAsVector(layer) then
			table.insert(self.LayersToChange, moho.document:LayerAbsoluteID(layer))
		elseif layer:IsGroupType() then
			self:ScanGroup(moho, layer)
		end
	end
end

function SZ_RelativeLineWidth:ScanGroup(moho, group)
	local groupLayer = moho:LayerAsGroup(group)
	for i=0, groupLayer:CountLayers()-1 do
		local layer = group:Layer(i)
		if layer:IsGroupType() then
			self:ScanGroup(moho, layer) -- recursion
		elseif moho:LayerAsVector(layer) then
			table.insert(self.LayersToChange, moho.document:LayerAbsoluteID(layer))
		end
	end
end 

When you call the ScanSelectedLayers, it will scan all selected layers, and call the recursive method ScanGroup for all group layers, if there's any. As the result, the self.LayersToChange variable will contain absolute IDs of all nested vector layers.

Re: seach through all child layers?

Posted: Fri Aug 05, 2016 4:41 pm
by Stan
A more elegant solution would be to create a local recursive function. More info here: https://www.lua.org/pil/6.2.html

Re: seach through all child layers?

Posted: Fri Aug 05, 2016 7:33 pm
by synthsin75
Stan wrote:You need a recursive function (a function that invokes itself).
Here's an example from one of my scripts:

Code: Select all

-- declare a table variable  self.LayersToChange

function SZ_RelativeLineWidth:ScanSelectedLayers(moho)
	for l = 0, moho.document:CountSelectedLayers()-1 do
		local layer = moho.document:GetSelectedLayer(l)
		if moho:LayerAsVector(layer) then
			table.insert(self.LayersToChange, moho.document:LayerAbsoluteID(layer))
		elseif layer:IsGroupType() then
			self:ScanGroup(moho, layer)
		end
	end
end

function SZ_RelativeLineWidth:ScanGroup(moho, group)
	local groupLayer = moho:LayerAsGroup(group)
	for i=0, groupLayer:CountLayers()-1 do
		local layer = group:Layer(i)
		if layer:IsGroupType() then
			self:ScanGroup(moho, layer) -- recursion
		elseif moho:LayerAsVector(layer) then
			table.insert(self.LayersToChange, moho.document:LayerAbsoluteID(layer))
		end
	end
end 

When you call the ScanSelectedLayers, it will scan all selected layers, and call the recursive method ScanGroup for all group layers, if there's any. As the result, the self.LayersToChange variable will contain absolute IDs of all nested vector layers.

Stan, does a function recursively calling itself work better now days? I remember when I first tried that, it seemed to eat up a lot of memory for large projects (scanning the whole document). That is the simplest (and most readable) method, but I usually do this:

Code: Select all

	-- **************************************************
	-- Recursive search
	-- **************************************************
	local layers = {}
	local stack = {}
	local sp = 0
	for i=0, moho.document:CountLayers()-1 do
		local layer = moho.document:Layer(i)
		table.insert(layers, layer)
		local group = nil
		local layerID = 0
		while true do
			if (layer:IsGroupType()) then
				table.insert(stack, {group, layerID -1})
				sp = sp+1
				group = moho:LayerAsGroup(layer)
				layerID = group:CountLayers()
			end
			if (layerID > 0) then
				layerID = layerID -1
				layer = group:Layer--[[ByDepth]](layerID)
				table.insert(layers, layer)
			else
				layerID = -1
				while (sp > 0) do
					group, layerID = stack[sp][1], stack[sp][2]
					table.remove(stack)
					sp = sp -1
					if (layerID >= 0) then
						layer = group:Layer--[[ByDepth]](layerID)
						table.insert(layers, layer)
						break
					end
				end
			end
			if (layerID < 0) then
				break
			end
		end
	end
	-- **************************************************
Based on Fazek's method, from the old meshinstance script.

Re: seach through all child layers?

Posted: Fri Aug 05, 2016 8:57 pm
by Stan
I didn't have any issues using recursion. Well, maybe my projects weren't large enough... And, of course, I didn't use recursion inside frequently called methods like MouseMove or LayerScript. Anyway, I'm glad you have another solution that I wasn't aware of. Thank you, and many thanks to the genius of Fazek.
I love to have such technical discussions, we should do this more often!

Re: seach through all child layers?

Posted: Fri Aug 05, 2016 9:11 pm
by synthsin75
Yeah, sometimes I think the scripting forum could stand to have sub-forums. Where general scripting snippets like these have no logical place in the scripting documentation, it would be nice to have a forum dedicated to collecting them. Sticky topics would get too numerous, and a single long thread might be hard to search.

Every once in awhile we have these discussions on the AF, but usually about problems no one has solved yet.

Re: seach through all child layers?

Posted: Fri Aug 05, 2016 11:43 pm
by hayasidist
recursion seems to be fine for me too

- this scans the global table and creates an html file: http://www.kelleytown.com/forum/animato ... IC_ID=1431 (improvement on the "print globals" built-in script) - tested/validated against Moho 12

- this bakes all layer / bone etc motion into point motion, and set all the layer translations, rotations etc to 0 (and scale to 1)
http://www.kelleytown.com/forum/animato ... hichpage=2 **NOT verified for Moho12** This does the "all child layers" scan as per the OP.

Re: seach through all child layers?

Posted: Fri Aug 05, 2016 11:53 pm
by synthsin75
It was probably all the way back in v7 or 8 that a self-calling function would cause a memory leak, since the internal calls would never seem to get garbage collection done. Maybe that's been fixed in a newer version of Lua since then. I still suspect the routine I use may be less resource heavy.

Re: seach through all child layers?

Posted: Thu Aug 11, 2016 10:15 am
by GaryC
Recursive calls on whole documents can definitely get heavy for me using layer scripts on large files, it works a lot better if you're not storing the layers anywhere just execute the code you need to when you find layers. And generally be mindful of how much a recursive function ends up needing to run. Limit scope and save time where you can.