SetParentOperation.py 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. # Copyright (c) 2016 Ultimaker B.V.
  2. # Uranium is released under the terms of the LGPLv3 or higher.
  3. from typing import Optional
  4. from UM.Scene.SceneNode import SceneNode
  5. from UM.Operations import Operation
  6. class SetParentOperation(Operation.Operation):
  7. """An operation that parents a scene node to another scene node."""
  8. def __init__(self, node: SceneNode, parent_node: Optional[SceneNode]) -> None:
  9. """Initialises this SetParentOperation.
  10. :param node: The node which will be reparented.
  11. :param parent_node: The node which will be the parent.
  12. """
  13. super().__init__()
  14. self._node = node
  15. self._parent = parent_node
  16. self._old_parent = node.getParent() # To restore the previous parent in case of an undo.
  17. def undo(self) -> None:
  18. """Undoes the set-parent operation, restoring the old parent."""
  19. self._set_parent(self._old_parent)
  20. def redo(self) -> None:
  21. """Re-applies the set-parent operation."""
  22. self._set_parent(self._parent)
  23. def _set_parent(self, new_parent: Optional[SceneNode]) -> None:
  24. """Sets the parent of the node while applying transformations to the world-transform of the node stays the same.
  25. :param new_parent: The new parent. Note: this argument can be None, which would hide the node from the scene.
  26. """
  27. if new_parent:
  28. current_parent = self._node.getParent()
  29. if current_parent:
  30. # Special casing for groups that have been removed.
  31. # In that case we want to put them back where they belong before checking the depth difference.
  32. # If we don't, we always get 0.
  33. old_parent = new_parent.callDecoration("getOldParent")
  34. if old_parent:
  35. new_parent.callDecoration("getNode").setParent(old_parent)
  36. # Based on the depth difference, we need to do something different.
  37. depth_difference = current_parent.getDepth() - new_parent.getDepth()
  38. child_transformation = self._node.getLocalTransformation()
  39. if depth_difference > 0:
  40. parent_transformation = current_parent.getLocalTransformation()
  41. # A node in the chain was removed, so we need to squash the parent info into all the nodes, so positions remain the same.
  42. self._node.setTransformation(parent_transformation.multiply(child_transformation))
  43. else:
  44. # A node is inserted into the chain, so use the inverse of the parent to set the transformation of it's children.
  45. parent_transformation = new_parent.getLocalTransformation()
  46. result = parent_transformation.getInverse().multiply(child_transformation, copy = True)
  47. self._node.setTransformation(result)
  48. self._node.setParent(new_parent)
  49. def __repr__(self) -> str:
  50. """Returns a programmer-readable representation of this operation.
  51. :return: A programmer-readable representation of this operation.
  52. """
  53. return "SetParentOperation(node = {0}, parent_node={1})".format(self._node, self._parent)