MultiplyObjectsJob.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. # Copyright (c) 2017 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from UM.Job import Job
  4. from UM.Operations.GroupedOperation import GroupedOperation
  5. from UM.Message import Message
  6. from UM.i18n import i18nCatalog
  7. i18n_catalog = i18nCatalog("cura")
  8. from cura.Arranging.Arrange import Arrange
  9. from cura.Arranging.ShapeArray import ShapeArray
  10. from UM.Application import Application
  11. from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
  12. class MultiplyObjectsJob(Job):
  13. def __init__(self, objects, count, min_offset = 8):
  14. super().__init__()
  15. self._objects = objects
  16. self._count = count
  17. self._min_offset = min_offset
  18. def run(self):
  19. status_message = Message(i18n_catalog.i18nc("@info:status", "Multiplying and placing objects"), lifetime=0,
  20. dismissable=False, progress=0, title = i18n_catalog.i18nc("@info:title", "Placing Object"))
  21. status_message.show()
  22. scene = Application.getInstance().getController().getScene()
  23. total_progress = len(self._objects) * self._count
  24. current_progress = 0
  25. root = scene.getRoot()
  26. arranger = Arrange.create(scene_root=root)
  27. processed_nodes = []
  28. nodes = []
  29. for node in self._objects:
  30. # If object is part of a group, multiply group
  31. current_node = node
  32. while current_node.getParent() and (current_node.getParent().callDecoration("isGroup") or current_node.getParent().callDecoration("isSliceable")):
  33. current_node = current_node.getParent()
  34. if current_node in processed_nodes:
  35. continue
  36. processed_nodes.append(current_node)
  37. node_too_big = False
  38. if node.getBoundingBox().width < 300 or node.getBoundingBox().depth < 300:
  39. offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(current_node, min_offset=self._min_offset)
  40. else:
  41. node_too_big = True
  42. found_solution_for_all = True
  43. for i in range(self._count):
  44. # We do place the nodes one by one, as we want to yield in between.
  45. if not node_too_big:
  46. new_node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr)
  47. if node_too_big or not solution_found:
  48. found_solution_for_all = False
  49. new_location = new_node.getPosition()
  50. new_location = new_location.set(z = 100 - i * 20)
  51. new_node.setPosition(new_location)
  52. # Same build plate
  53. build_plate_number = current_node.callDecoration("getBuildPlateNumber")
  54. new_node.callDecoration("setBuildPlateNumber", build_plate_number)
  55. for child in new_node.getChildren():
  56. child.callDecoration("setBuildPlateNumber", build_plate_number)
  57. nodes.append(new_node)
  58. current_progress += 1
  59. status_message.setProgress((current_progress / total_progress) * 100)
  60. Job.yieldThread()
  61. Job.yieldThread()
  62. if nodes:
  63. op = GroupedOperation()
  64. for new_node in nodes:
  65. op.addOperation(AddSceneNodeOperation(new_node, current_node.getParent()))
  66. op.push()
  67. status_message.hide()
  68. if not found_solution_for_all:
  69. no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"), title = i18n_catalog.i18nc("@info:title", "Placing Object"))
  70. no_full_solution_message.show()