Recursive Function Problem

Recursive Function Problem

lee_j_keith
Advocate Advocate
946 Views
6 Replies
Message 1 of 7

Recursive Function Problem

lee_j_keith
Advocate
Advocate
Hi,

I have a function that calls itself in a for loop. That all works fine, except for the last loop. I'm looping through a hierarchy of dummy & mesh objects. I'm collapsing the mesh objects into a new mesh and then deleting the dummy nodes at the end of the collapsing operation. So on the first run of the function it calls itself, to handle the first node it finds in the for loop that has children. It appears that Maxscript does not finish executing the remainder of the function past this point, until every recursive iteration in the for loop has finished. That's Ok I guess. At the end of the function after this for loop, there is code that deletes the dummy objects that have been stored in an array. This block of code only needs to run after all of the recursive iterations are finished. The code block has an if statement that compares the number of iterations against the number of children the top level node has, and if they are equal, it executes the code. The problem is that this evaluates to true on the last iteration of the for loop, and so it executes, and then again once the original call to the function finishes executing. That is a problem, and I do not know how to resolve it. It's probably simple, and I don't see it because I've been staring at it all day, but if anyone has an idea let me know.

Below is a skeleton of what I am attempting.
thx

function collapseSelectedDummy theNode =
(
for c in nodeChildren do
(
--here I'm processing the nodes.
--if it is a mesh it gets collapsed
--if it is a dummy node then it calls this function again
--I'm also keeping a tally of the nodes processed with the variable dummyChildrenProcessed
)

--this code only needs to execute once, but it is getting called twice
--here I am deleting out the dummy nodes
--it compares the number of processed nodes against the number of children
--the selected scene node has
if dummyChildrenProcessed == selectedNodeNumChildren then
(
for i in dummiesToDelete do
(
delete i
)
)
0 Likes
947 Views
6 Replies
Replies (6)
Message 2 of 7

Steve_Curley
Mentor
Mentor
Is there any particular reason why you're creating a list of dummies to delete rather than simply deleting them once they're processed? I feel that the code is being complicated by doing so, and because we only have a rough outline of the code it will be difficult to determine just why it seems to be running twice - we don't know if the list is intended to be per-iteration or only supposed to happen when the initial invocation of the recursive function is about to end.

The following is very "bare bones" but does work as intended, recursing through (then deleting) any dummies it finds and converting everything else to a Mesh object. Assumes one or more dummies (in different hierarchies) are pre-selected.

(
fn recurse thisObj =
(
if thisObj.children.count > 0 then
(
for o = thisObj.children.count to 1 by -1 do
(
obj = thisObj.children
if classof obj == Dummy then
(
recurse obj
delete obj
)
else
(
convertToMesh obj
)
)
)
)

for obj in selection where classof obj == Dummy do
(
Recurse obj
delete obj
)
)


ps. You don't need a "sig sep" in your signature - the forum puts one there for you, and you missed the most important detail, namely your max version.

Max 2016 (SP1/EXT1)
Win7Pro x64 (SP1). i5-3570K @ 4.4GHz, 8Gb Ram, DX11.
nVidia GTX760 (2GB) (Driver 430.86).

0 Likes
Message 3 of 7

lee_j_keith
Advocate
Advocate
Hi Steve,

I tried deleting them as they were processed, but kept getting errors saying that I was trying to access scene nodes that were deleted. The intent of this script is to optimize imported CAD hierarchies. They are often very nested and deep. First you select a dummy node then run the script. It creates a mesh and adapts all of the selected dummy's properties. Then it loops thru all of the nested children of the dummy. For every mesh it finds, it attaches it to the new mesh; for every dummy it deletes it. The result is one mesh object from the entire hierarchy.

In it's current state it will throw this error: -- Runtime error: Attempt to access deleted scene object <<

That error is caused when ran on this type of hierarchy:

dummy1
--mesh1
--mesh2
--dummy2
----mesh3
----mesh4 (Deletion code block is executing after this, which is fine is the function never calls itself)

But when working with a hierarchy like this, it works.

dummy
--mesh

So what is happening is that on the first iteration of the function, it processes mesh1, mesh2, and when it gets to dummy2, it calls itself. Then the function processes mesh3 & mesh4. Upon processing mesh4, it exits the for loop started by the second call to the function and executes the deletion code block that follows. At that point all of the dummy objects I have stored in the array are deleted and the code returns back to the first iteration of the script, and since dummy2 has been delete, the runtime can't finish the loop because the node is gone. I need the deletion block to only run on the last iteration. I'll try and dumb down the script and post it, but does this give a clearer picture?

thx
0 Likes
Message 4 of 7

Anonymous
Not applicable
As you delete all dummies and end with one mesh, you can done this without recurse function, something like...
--select entire hierarchy
for n in selection do selectMore n.children

--delete all dummies in selection
delete(for n in selection where isKindOf n Dummy collect n)

--and then attach
objs = selection as array
for i = 2 to objs.count do meshop.attach objs objs
0 Likes
Message 5 of 7

Steve_Curley
Mentor
Mentor
@Keith - your script fails or mine? Below is the hierarchy I tested it on and although I'm simply converting the Spheres to Mesh rather than attaching them, the script does work. The important thing to note is that I'm working DOWN the list of children, not UP the list for the very reason you describe.

Anubis's method may be a lot easier, if it does what you need.


Max 2016 (SP1/EXT1)
Win7Pro x64 (SP1). i5-3570K @ 4.4GHz, 8Gb Ram, DX11.
nVidia GTX760 (2GB) (Driver 430.86).

0 Likes
Message 6 of 7

lee_j_keith
Advocate
Advocate
@Steve - mine is failing.

I'll ponder over you guy's input and see if I can adapt it. I'm probably making the script harder than it needs to be.

thx
0 Likes
Message 7 of 7

lee_j_keith
Advocate
Advocate
Ok, here is what I have so far. Steve, I integrated your script and it did fail because I needed to check for an editable mesh after the dummy check. That was causing the runtime error. I used the IsValidNode method to compensate and that works. It did eliminate the need to store the dummies in a list though 🙂

The code block that deletes the tmp mesh still gets executed twice unless I incorporate 2 checks. I understand why this happens, but see no clean way to prevent it. My fix is very clumsy to me, so if you see a better way let me know.

The only thing not working is if you select multiple dummy objects with children and run the script it will not process all of the selected nodes. Apparently the delete node call is messing with the selection array. Not sure yet how to fix that.


macroScript CollapseChildren category:"KL Tools" tooltip:"Collapse child nodes of selected parent" Icon:#("uvwunwraptools",18)
(
local tmpMesh
local tmpMeshCreated
local dummyChildrenProcessed
local selectedNodeNumChildren
local tmpMeshDeleted

function recurse theNode =
(
if tmpMeshCreated == false then
(
--Create empty mesh
tmpMesh = mesh length:25 width:25 lengthsegs:1 widthsegs:1
setFaceSelection tmpMesh #{1..2} name:#temp
tmpMesh.name = theNode.name
metadata = getUserPropBuffer theNode
setUserPropBuffer tmpMesh metadata

-- if not root object
if theNode.parent != undefined then
(
attachObjects theNode.parent tmpMesh
)
tmpMesh.pos = theNode.pos
tmpMesh.rotation.x_rotation = theNode.rotation.x_rotation
tmpMesh.rotation.y_rotation = theNode.rotation.y_rotation
tmpMesh.rotation.z_rotation = theNode.rotation.z_rotation
tmpMeshCreated = true
)

if theNode.children.count > 0 then
(
for o = theNode.children.count to 1 by -1 do
(
c = theNode.children
if classof c == Dummy then
(
dummyChildrenProcessed += 1
recurse c
delete c
)
if IsValidNode c then
(
if classOf c == Editable_mesh then
(
collapseStack c
clearSmoothing c
clearEdgeVisibility c
attach tmpMesh c
dummyChildrenProcessed += 1
)
)
)

)
--this check is needed is needed to ensure that this only executes after all
--nodes have been processed.
if dummyChildrenProcessed == selectedNodeNumChildren then
(
--because the above evaluates to true in the last 2 iterations
--we must have another check
if tmpMeshDeleted == false then
(
tmpMesh.selectedFaces = tmpMesh.Faces
meshOp.deleteFaces tmpMesh tmpMesh.selectedFaces
)
tmpMeshDeleted = true
)
)

function getChildCount theNode =
(
for c in theNode.children do
(
if classOf c == Dummy then
(
getChildCount c
selectedNodeNumChildren += 1
)

if classOf c == Editable_mesh then
(
selectedNodeNumChildren += 1
)
)
)

function doCollapse =
(
if selection.count == 0 then
(
print "nothing selected"
)
else
(
for obj in selection where classof obj == Dummy do
(
tmpMeshCreated = false
tmpMeshDeleted = false
dummyChildrenProcessed = 0
selectedNodeNumChildren = 0
getChildCount obj
print obj.name
recurse obj
--this is causing multiple selections not to work
delete obj
)
)
)

on execute do
(
doCollapse()
)
)
0 Likes