Last active
October 25, 2023 21:07
-
-
Save BigRoy/3680460f69fd59a640f00bce593fba79 to your computer and use it in GitHub Desktop.
Houdini attach nodes to each other - make one follow the other
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import hou | |
import json | |
class Attachments: | |
user_data_key = "attachednodes" | |
@staticmethod | |
def attach_node(node, node_to_follow): | |
follow_event_types = (hou.nodeEventType.NameChanged,) | |
assert node != node_to_follow, "Node cannot attach to itself" | |
assert node.parent().path() == node_to_follow.parent().path(), \ | |
"Nodes must be siblings" | |
followers_json = node.userData(Attachments.user_data_key) | |
if followers_json: | |
followers = json.loads(followers_json) | |
else: | |
followers = [] | |
offset = node.position() - node_to_follow.position() | |
followers.append({ | |
"name": node.name(), | |
"offset": [offset.x(), offset.y()] | |
}) | |
node.setUserData(Attachments.user_data_key, json.dumps(followers)) | |
node_to_follow.addEventCallback( | |
follow_event_types, Attachments._update_attachments_callback | |
) | |
@staticmethod | |
def detach_node(node, node_to_follow): | |
assert node != node_to_follow, "Node cannot detach itself" | |
assert node.parent().path() == node_to_follow.parent().path(), \ | |
"Nodes must be siblings" | |
followers = [ | |
follower for follower in Attachments.iter_followers(node_to_follow) | |
if follower["name"] != node.name() | |
] | |
if followers: | |
node.setUserData(Attachments.user_data_key, json.dumps(followers)) | |
else: | |
node.destroyUserData(Attachments.user_data_key, must_exist=False) | |
for event_type, callback in node_to_follow.eventCallbacks(): | |
if callback is Attachments._update_attachments_callback: | |
node_to_follow.removeEventCallback( | |
(event_type,), callback | |
) | |
@staticmethod | |
def iter_followers(node): | |
followers = node.userData(Attachments.user_data_key) | |
if not followers: | |
return | |
followers = json.loads(followers) | |
parent = node.parent() | |
for follower_data in followers: | |
follower_node_name = follower_data["name"] | |
follower_node = parent.node(follower_node_name) | |
if not follower_node: | |
continue | |
yield follower_data | |
@staticmethod | |
def _update_attachments_callback(self, **kwargs): | |
node = kwargs["node"] | |
follow_event_types = (hou.nodeEventType.NameChanged,) | |
followers = list(Attachments.iter_followers(node)) | |
if followers: | |
# Let's update the attached nodes | |
position = node.position() | |
parent = node.parent() | |
for follower_data in followers: | |
follower_node = parent.node(follower_data["name"]) | |
offset = hou.Vector2(*follower_data["offset"]) | |
follower_position = position + offset | |
follower_node.setPosition(follower_position) | |
# Let's remove any data that might have been present for a follower | |
# that is not accurate currently now from the user data | |
node.setUserData(Attachments.user_data_key, json.dumps(followers)) | |
else: | |
# If nothing should follow the node, delete the callback and the | |
# metadata key | |
node.destroyUserData(Attachments.user_data_key, must_exist=False) | |
node.removeEventCallback( | |
follow_event_types, Attachments._update_attachments_callback | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment