Warning on setting up infinite parenting loops

Hello :slight_smile:

I know that doing such thing is non sense :

sphere.parent = ground;
ground.parent = sphere;// Forbidden !

But eventually, within a complex architecture with a user involved, it can happen.

Problem is that the Forbidden line will never be seen as problematic. The only traceback is the Javascript call stack exceeded (Playground) :

RangeError: Maximum call stack size exceeded
    at e._syncParentEnabledState (node.ts:595:86)
    at node.ts:599:19
    at Array.forEach (<anonymous>)
    at e._syncParentEnabledState (node.ts:598:28)
    at node.ts:599:19
    at Array.forEach (<anonymous>)
    at e._syncParentEnabledState (node.ts:598:28)
    at node.ts:599:19
    at Array.forEach (<anonymous>)
    at e._syncParentEnabledState (node.ts:598:28)

I think it would be nice to add a check for infinite loop in the parent setter. That way, the line of code which actually broke the code will be pointed out in the traceback


Also, please note that the exact same issue happens for GUI (Playground) :

rect2.addControl(rect1);
rect1.addControl(rect2);// Forbidden !
2 Likes

and this example is a recursion dilema, too:

shpere1.parent = sphere2;
sphere2.parent = sphere3;
sphere3.parent = sphere1; // back to 1

and this is a no-go:

sphere1.parent = sphere1; :smiley:

2 Likes

Did just a quick google. But when you want to cover indirect parents I think you will end up with detecting loops in a directed graph (O(E+V)). Might be better for userland. I do stuff like that in my custom “MeshController” with e.g. setParent(child, parent, keepOffset). I could easily extend this in order to cover recursion errors.

But the simple 1-layer case would be nice :slightly_smiling_face:

Yes, I would anyway setup a recursive check accross child’s children :

The above example is already N-layer :slight_smile:


EDIT:
Building the loop traceback within the recursion :grin:

Screenshot from 2025-04-02 14-05-29


EDIT 2:
Should I rename this topic “ASCII art in BabylonJS logs” ? :grin:

Screenshot from 2025-04-02 14-13-12

1 Like

Oh, oops, I didnt check your code. :flushed: You only check the node to be parented. But still, what does this make then O(N)? Is it really necessary to introduce this cost? Maybe a “performance” flag?

N.B. I am thinking about code where setParent runs in the render loop and you have like a million meshes.

@Joe_Kerr I’m not sure I fully got your question.

  • Goal is to check if child is already a parent of parent node
  • I run the check function on it
  • It calls itself on its children (recursive)

To me there is almost no cost, even in a complex structure.

  • I don’t run over all meshes, only the children of the mesh being set as child
  • Does it really happens to setParent on millions of meshes on every frame ? ^^
function setParent(child, parent){
    if(child===parent){
        console.warn("setParent("+child.name+", "+parent.name+") : cannot parent to itself");
        return;
    }
    let ok=true;
    function check(c){
        c.getChildren().forEach((cc)=>{
            if(!ok)return;
            if(cc===parent){
                ok =false;
                console.warn("setParent("+child.name+", "+parent.name+") : parenting loop");
            } else {
                check(cc);
            }
        })
    }
    check(child);
    if(ok){
        child.parent = parent;
    }
}

Ok, this is the code. Fair enough.