Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow controlling traverse flow #27887

Open
wlinna opened this issue Mar 8, 2024 · 0 comments
Open

Allow controlling traverse flow #27887

wlinna opened this issue Mar 8, 2024 · 0 comments

Comments

@wlinna
Copy link
Contributor

wlinna commented Mar 8, 2024

Description

Right now there's no way to prevent traverse from traversing the whole tree (save for throwing an exception). However, often it would be useful to prevent processing the child nodes, or even rest of the hierarchy.

Solution

Make traverse and the rest of the family respect special return values. The most important (in my opinion) would be the ability to ignore the children. For example:

// Somewhere in Three.js code. Could be replaced with an enum-style definition 
const IGNORE_CHILDREN = new Symbol('ignore_children');
const HALT_TRAVERSE = new Symbol('halt_traverse');

// I didn't check this for mistakes, but the idea should become clear
	traverse( callback ) {

		const controlFlowResult = callback( this );

                if (controlFlowResult === IGNORE_CHILDREN || controlFlowResult === HALT_TRAVERSE ) {

                      return controlFlowResult;

                } 

		const children = this.children;

		for ( let i = 0, l = children.length; i < l; i ++ ) {

			const childFlowResult = children[ i ].traverse( callback );

                        if (childFlowResult === HALT_TRAVERSE) {

                             return HALT_TRAVERSE;

                        }

		}

	}

Examples how a user might use it:

Find a specific node with some property. Then stop.

// 
let oneSpecimenB;
myModel.traverse(node => {
           if (node.userData.isB) {
               oneSpecimentB = node;
                return HALT_TRAVERSE;
           }
});

Run code for all nodes except for all the descendants (and the node itself) of the nodes we want to ignore

myModel.traverse(node => {
           if (node.userData.ignoreChildren) {
                // Optionally run operations here before returning
                return IGNORE_CHILDREN;
           }

          // Do operations for others
});

Alternatives

For users, the alternatives are

  • Set an outside variable to a value from the callback to not do anything. For example
let ingoreChildren = false;

myModel.traverse(node => {
           if (node.userData.ignoreChildren) {
               ignoreChildren = true;
                return;
           }

          // Do operations for others
});

This will fail after all the descendants have been traversed and the execution returns to the siblings / parent.

  • Use visibility in conjunction with traverseVisible. The user would run the risk of messing up visibility and it's not convenient anyway, because it would force the user to traverse the hierarchy first to change it.

  • getObjectByProperty only works for the direct properties of nodes, but not for userData for example. And since it's based on a simple equality comparison, it's generally less useful than a predicate based search.

  • Implement your own traverse. The user would just duplicate the functionality of traverse

Additional context

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants