SupportEraser.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. # Copyright (c) 2018 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. import os
  4. import os.path
  5. from PyQt5.QtCore import Qt, QUrl
  6. from UM.Math.Vector import Vector
  7. from UM.Tool import Tool
  8. from UM.Application import Application
  9. from UM.Event import Event, MouseEvent
  10. from UM.Mesh.MeshBuilder import MeshBuilder
  11. from UM.Scene.Selection import Selection
  12. from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
  13. from cura.Scene.CuraSceneNode import CuraSceneNode
  14. from cura.PickingPass import PickingPass
  15. from UM.Operations.GroupedOperation import GroupedOperation
  16. from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
  17. from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
  18. from cura.Operations.SetParentOperation import SetParentOperation
  19. from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
  20. from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
  21. from UM.Scene.GroupDecorator import GroupDecorator
  22. from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
  23. from UM.Settings.SettingInstance import SettingInstance
  24. class SupportEraser(Tool):
  25. def __init__(self):
  26. super().__init__()
  27. self._shortcut_key = Qt.Key_G
  28. self._controller = Application.getInstance().getController()
  29. self._selection_pass = None
  30. Application.getInstance().globalContainerStackChanged.connect(self._updateEnabled)
  31. def event(self, event):
  32. super().event(event)
  33. if event.type == Event.MousePressEvent and self._controller.getToolsEnabled():
  34. if self._selection_pass is None:
  35. # The selection renderpass is used to identify objects in the current view
  36. self._selection_pass = Application.getInstance().getRenderer().getRenderPass("selection")
  37. picked_node = self._controller.getScene().findObject(self._selection_pass.getIdAtPosition(event.x, event.y))
  38. node_stack = picked_node.callDecoration("getStack")
  39. if node_stack:
  40. if node_stack.getProperty("anti_overhang_mesh", "value"):
  41. self._removeEraserMesh(picked_node)
  42. return
  43. elif node_stack.getProperty("support_mesh", "value") or node_stack.getProperty("infill_mesh", "value") or node_stack.getProperty("cutting_mesh", "value"):
  44. # Only "normal" meshes can have anti_overhang_meshes added to them
  45. return
  46. # Create a pass for picking a world-space location from the mouse location
  47. active_camera = self._controller.getScene().getActiveCamera()
  48. picking_pass = PickingPass(active_camera.getViewportWidth(), active_camera.getViewportHeight())
  49. picking_pass.render()
  50. picked_position = picking_pass.getPickedPosition(event.x, event.y)
  51. # Add the anti_overhang_mesh cube at the picked location
  52. self._createEraserMesh(picked_node, picked_position)
  53. def _createEraserMesh(self, parent: CuraSceneNode, position: Vector):
  54. node = CuraSceneNode()
  55. node.setName("Eraser")
  56. node.setSelectable(True)
  57. mesh = MeshBuilder()
  58. mesh.addCube(10,10,10)
  59. node.setMeshData(mesh.build())
  60. node.setPosition(position)
  61. active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
  62. node.addDecorator(SettingOverrideDecorator())
  63. node.addDecorator(BuildPlateDecorator(active_build_plate))
  64. node.addDecorator(SliceableObjectDecorator())
  65. stack = node.callDecoration("getStack") # created by SettingOverrideDecorator
  66. settings = stack.getTop()
  67. definition = stack.getSettingDefinition("anti_overhang_mesh")
  68. new_instance = SettingInstance(definition, settings)
  69. new_instance.setProperty("value", True)
  70. new_instance.resetState() # Ensure that the state is not seen as a user state.
  71. settings.addInstance(new_instance)
  72. root = self._controller.getScene().getRoot()
  73. op = GroupedOperation()
  74. # First add the node to the scene, so it gets the expected transform
  75. op.addOperation(AddSceneNodeOperation(node, root))
  76. # Determine the parent group the node should be put in
  77. if parent.getParent().callDecoration("isGroup"):
  78. group = parent.getParent()
  79. else:
  80. # Create a group-node
  81. group = CuraSceneNode()
  82. group.addDecorator(GroupDecorator())
  83. group.addDecorator(BuildPlateDecorator(active_build_plate))
  84. group.setParent(root)
  85. center = parent.getPosition()
  86. group.setPosition(center)
  87. group.setCenterPosition(center)
  88. op.addOperation(SetParentOperation(parent, group))
  89. op.addOperation(SetParentOperation(node, group))
  90. op.push()
  91. Application.getInstance().getController().getScene().sceneChanged.emit(node)
  92. def _removeEraserMesh(self, node: CuraSceneNode):
  93. op = RemoveSceneNodeOperation(node)
  94. op.push()
  95. Application.getInstance().getController().getScene().sceneChanged.emit(node)
  96. def _updateEnabled(self):
  97. plugin_enabled = False
  98. global_container_stack = Application.getInstance().getGlobalContainerStack()
  99. if global_container_stack:
  100. plugin_enabled = global_container_stack.getProperty("anti_overhang_mesh", "enabled")
  101. Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, plugin_enabled)