# Copyright (c) 2016 Ultimaker B.V. # Uranium is released under the terms of the LGPLv3 or higher. from typing import Optional from UM.Scene.SceneNode import SceneNode from UM.Operations import Operation class SetParentOperation(Operation.Operation): """An operation that parents a scene node to another scene node.""" def __init__(self, node: SceneNode, parent_node: Optional[SceneNode]) -> None: """Initialises this SetParentOperation. :param node: The node which will be reparented. :param parent_node: The node which will be the parent. """ super().__init__() self._node = node self._parent = parent_node self._old_parent = node.getParent() # To restore the previous parent in case of an undo. def undo(self) -> None: """Undoes the set-parent operation, restoring the old parent.""" self._set_parent(self._old_parent) def redo(self) -> None: """Re-applies the set-parent operation.""" self._set_parent(self._parent) def _set_parent(self, new_parent: Optional[SceneNode]) -> None: """Sets the parent of the node while applying transformations to the world-transform of the node stays the same. :param new_parent: The new parent. Note: this argument can be None, which would hide the node from the scene. """ if new_parent: current_parent = self._node.getParent() if current_parent: # Special casing for groups that have been removed. # In that case we want to put them back where they belong before checking the depth difference. # If we don't, we always get 0. old_parent = new_parent.callDecoration("getOldParent") if old_parent: new_parent.callDecoration("getNode").setParent(old_parent) # Based on the depth difference, we need to do something different. depth_difference = current_parent.getDepth() - new_parent.getDepth() child_transformation = self._node.getLocalTransformation() if depth_difference > 0: parent_transformation = current_parent.getLocalTransformation() # A node in the chain was removed, so we need to squash the parent info into all the nodes, so positions remain the same. self._node.setTransformation(parent_transformation.multiply(child_transformation)) else: # A node is inserted into the chain, so use the inverse of the parent to set the transformation of it's children. parent_transformation = new_parent.getLocalTransformation() result = parent_transformation.getInverse().multiply(child_transformation, copy = True) self._node.setTransformation(result) self._node.setParent(new_parent) def __repr__(self) -> str: """Returns a programmer-readable representation of this operation. :return: A programmer-readable representation of this operation. """ return "SetParentOperation(node = {0}, parent_node={1})".format(self._node, self._parent)