Can't have a recursive function within another function


#1

I just can’t seem to wrap my head around this problem. I have a recursive function that goes through all hierarchies and collect their hierarchy depth, which works by itself. The problem however ia that when I try to call that recursive function within another function I get an error.

Here’s the recursive function by itself (which works):

    -- RECURSIVE FUNCTION: RETRIEVE ENTIRE HIERARCHY OF INPUT OBJECT WITH THE HIERARCHY-DEPTH INCLUDED
    function RetrieveHierarchyDepth currObj currHierarchyDepth &processedObjs &processedObjsDepth =
    (
        -- Store the current object
        if (classOf processedObjs == Array ) then ( append processedObjs currObj )
        else ( processedObjs = #(processedObjs) + #(currObj) )

        -- Store the current object depth
        if (classOf processedObjsDepth == Array ) then ( append processedObjsDepth currHierarchyDepth )
        else ( processedObjsDepth = #(processedObjsDepth) + #(currHierarchyDepth) )

        -- If the current object has any children
        if (currObj.children.count != 0) do
        (
            -- Execute function recursively to step further through the current hierarchy
            currObjChildren = (for obj in currObj.children collect obj)
            currHierarchyDepth = (currHierarchyDepth + 1)
            for currObj in currObjChildren do RetrieveHierarchyDepth currObj currHierarchyDepth &processedObjs &processedObjsDepth 
        )
    )

    -- Collect all rootnodes from the speficied input objects
    inputObjects = (objects as array)
    inputRootNodes = (for obj in inputObjects where (finditem inputObjects obj.parent == 0) collect obj)
    -- Retrieve hierarchies from all rootnodes recursively
    processedObjs=inputRootNodes[1]
    processedObjsDepth=1
    for obj in inputRootNodes do RetrieveHierarchyDepth obj 1 &processedObjs &processedObjsDepth

    -- Print information
    for itemIndex=1 to processedObjs.count do
    (
        print (processedObjs[itemIndex].name + " - " + processedObjsDepth[itemIndex] as string)
    )

However if I wrap the recursive function within another function, I get an error:

-- FUNCTION: SORT INPUT OBJECTS BY HIERARCHY DEPTH (DISREGARDING SEPARATE HIERARCHIES)
function SortByHierarchyDepth inputObjects =
(
    -- RECURSIVE FUNCTION: RETRIEVE ENTIRE HIERARCHY OF INPUT OBJECT WITH THE HIERARCHY-DEPTH INCLUDED
    function RetrieveHierarchyDepth currObj currHierarchyDepth &processedObjs &processedObjsDepth =
    (
        -- Store the current object
        if (classOf processedObjs == Array ) then ( append processedObjs currObj )
        else ( processedObjs = #(processedObjs) + #(currObj) )

        -- Store the current object depth
        if (classOf processedObjsDepth == Array ) then ( append processedObjsDepth currHierarchyDepth )
        else ( processedObjsDepth = #(processedObjsDepth) + #(currHierarchyDepth) )

        -- If the current object has any children
        if (currObj.children.count != 0) do
        (
            -- Execute function recursively to step further through the current hierarchy
            currObjChildren = (for obj in currObj.children collect obj)
            currHierarchyDepth = (currHierarchyDepth + 1)
            for currObj in currObjChildren do RetrieveHierarchyDepth currObj currHierarchyDepth &processedObjs &processedObjsDepth 
        )
    )

    -- Collect all rootnodes from the speficied input objects
    inputObjects = (objects as array)
    inputRootNodes = (for obj in inputObjects where (finditem inputObjects obj.parent == 0) collect obj)
    -- Retrieve hierarchies from all rootnodes recursively
    processedObjs=inputRootNodes[1]
    processedObjsDepth=1
    for obj in inputRootNodes do RetrieveHierarchyDepth obj 1 &processedObjs &processedObjsDepth

    -- Print information
    for itemIndex=1 to processedObjs.count do
    (
        print (processedObjs[itemIndex].name + " - " + processedObjsDepth[itemIndex] as string)
    )

    -- Return the result
    return #(processedObjs, processedObjsDepth)
)

Can anyone explain why this happens? And is there a workaround for this issue? I would just like a function that can be called that returns all hierarchies with the object’s hierarcy depth.


#2

That is because of “Scope of variables”. First solution could be to defining scope of the “RetrieveHierarchyDepth”.

function RetrieveHierarchyDepth currObj currHierarchyDepth &processedObjs &processedObjsDepth =
(
		local RetrieveHierarchyDepth

#3

What is the reason for defining the RetrieveHierarchyDepth function inside the SortByHierarchyDepth?
Usually defining one function inside another is used if the inner function doesn’t make a lot of sense without the top one. Your case is not like this.


#4
fn getHierarchyDistance node parent = 
(
	dist = 0
	while node != parent and (node = node.parent) != undefined do dist += 1
	dist
)	
fn getHierarchyDepth node = 
(
	dist = 0
	while (node = node.parent) != undefined do dist += 1
	dist
)	
fn getHierarchyLength node = 
(
	len = 0
	for child in (join #() node) do len = amax len (getHierarchyDistance child node)
	len 
)	

-- use dir == -1 for reverse sorting
fn sortByHierarchyDepth n1 n2 dir:1 = (getHierarchyDepth n1 - getHierarchyDepth n2)*dir
fn sortByHierarchyLength n1 n2 dir:1 = (getHierarchyLength n1 - getHierarchyLength n2)*dir

/*

delete objects

seed 2 
pp = for k=1 to 100 collect (point())
for k=1 to 100 do try(pp[random 1 100].parent = pp[random 1 100]) catch()

qsort pp sortByHierarchyDepth
getHierarchyDepth pp[pp.count]
qsort pp sortByHierarchyLength
getHierarchyLength pp[pp.count]

*/

function recursion doesn’t work very well in MXS. It’s slow and eats up memory (also has a recursion limit).
I recommend avoiding function recursion whenever possible.


#5

First of all, thank you for your responses :slight_smile:

Sadly that didn’t do the trick :pensive:

The reason for this is that I want to use the RetrieveHierarchyDepth function to manipulate the data returned from SortByHierarchyDepth (which of course won’t work as I can’t get the recursive function to return anything).

What I basically want is to retrieve all objects in the scene regardless of hierarchy, and retrieve the objects in order by the objects with the lowest hierarchy-depth. The reason for this is that when I perform different transform-operations on objects, it can cause problems if I do it on an object with children below it.

I’ve uploaded a test-scene (HierarchyTest.max ), and a script below that prints all objectnames with their respective hierarchy depth. However I would like to actually retrieve the objects in a variable so that I can work with them instead of just printing them.

Here’s the script that just prints the objects with their depths:

-- RECURSIVE FUNCTION: PRINT ENTIRE HIERARCHY OF OBJECTS WITH THE HIERARCHY-DEPTH INCLUDED
fn RetrieveHierarchyDepth currObj currHierarchyDepth  =
(
    -- Print the current object and itæs hierarchy depth
    print (currHierarchyDepth as string + " - " + currObj.name)
    -- Execute function recursively to step further through the current hierarchy
    if currObj.children.count != 0 do
    (
        processedObjs = for obj in currObj.children collect obj
        currHierarchyDepth = currHierarchyDepth + 1
        for obj in processedObjs do RetrieveHierarchyDepth obj currHierarchyDepth
    )
)

-- Collect all rootnodes from the scene
sceneRootNodes = (for obj in objects where (finditem (objects as array) obj.parent == 0) collect obj)
for obj in sceneRootNodes do RetrieveHierarchyDepth obj 1

If anyone could give me some further assistance on this it would be greatly appreciated!


#6

my methods above don’t work?

fn getRootNodes nodes: hasChildren:on = 
(
	if nodes == unsupplied do nodes = objects 
	for node in nodes where node.parent == undefined and (not hasChildren or node.children.count > 0) collect node
)
roots = getRootNodes()
qsort roots sortByHierarchyLength

#7

Whenever you respond I should do a better attempt at actually trying you code more thorough before replying. The ‘getHierarchyLength’ got me confused, but when I ran it without that it did exactly what I wanted.

fn getHierarchyDistance node parent = 
(
    dist = 0
    while node != parent and (node = node.parent) != undefined do dist += 1
    dist
)   
fn getHierarchyDepth node = 
(
    dist = 0
    while (node = node.parent) != undefined do dist += 1
    dist
)   

-- use dir == -1 for reverse sorting
fn sortByHierarchyDepth n1 n2 dir:1 = (getHierarchyDepth n1 - getHierarchyDepth n2)*dir

-- Collect all nodes in scene
sceneNodes = objects as array

qsort sceneNodes sortByHierarchyDepth
getHierarchyDepth sceneNodes[sceneNodes.count]

-- Print objects by hierarchy depth
for obj in sceneNodes do print obj.name

denisT, I seriously appreciate all the help you’ve given to all of us trying to find our way through maxscript! Thank you!


#8

you don’t need recursion … my gethierarchydepth function has to do what you need.

to apply transformation data in world coordinates you have to sort all your nodes by hierarchy depth where parents are going first.

if you apply transformation data in parent coordinates the order of nodes doesn’t matter.


#9

but… !

sometimes another order is needed. some constraint controllers need dependency order.
you can try to find on this forum the function I posted.
if you cannot find it, I will post it again


#10

I see, for the moment I will only need it for geometric objects, but if I were to search for your solution, do you remember any keywords I could use to find it?


#11

sortbydependency or sortnodesbydependency

fn sortByDependency n1 n2 dir:1 = (if refs.dependencyLoopTest n1 n2 then -1 else if refs.dependencyLoopTest n2 n1 then 1 else 0) * dir