MultiplyObjectsJob.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  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.Scene.SceneNode import SceneNode
  5. from UM.Math.Vector import Vector
  6. from UM.Operations.SetTransformOperation import SetTransformOperation
  7. from UM.Operations.TranslateOperation import TranslateOperation
  8. from UM.Operations.GroupedOperation import GroupedOperation
  9. from UM.Logger import Logger
  10. from UM.Message import Message
  11. from UM.i18n import i18nCatalog
  12. i18n_catalog = i18nCatalog("cura")
  13. from cura.ZOffsetDecorator import ZOffsetDecorator
  14. from cura.Arrange import Arrange
  15. from cura.ShapeArray import ShapeArray
  16. from typing import List
  17. from UM.Application import Application
  18. from UM.Scene.Selection import Selection
  19. from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
  20. class MultiplyObjectsJob(Job):
  21. def __init__(self, objects, count, min_offset = 8):
  22. super().__init__()
  23. self._objects = objects
  24. self._count = count
  25. self._min_offset = min_offset
  26. def run(self):
  27. status_message = Message(i18n_catalog.i18nc("@info:status", "Multiplying and placing objects"), lifetime=0,
  28. dismissable=False, progress=0, title = i18n_catalog.i18nc("@info:title", "Placing Object"))
  29. status_message.show()
  30. scene = Application.getInstance().getController().getScene()
  31. total_progress = len(self._objects) * self._count
  32. current_progress = 0
  33. root = scene.getRoot()
  34. arranger = Arrange.create(scene_root=root)
  35. nodes = []
  36. for node in self._objects:
  37. # If object is part of a group, multiply group
  38. current_node = node
  39. while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
  40. current_node = current_node.getParent()
  41. node_too_big = False
  42. if node.getBoundingBox().width < 300 or node.getBoundingBox().depth < 300:
  43. offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(current_node, min_offset=self._min_offset)
  44. else:
  45. node_too_big = True
  46. found_solution_for_all = True
  47. for i in range(self._count):
  48. # We do place the nodes one by one, as we want to yield in between.
  49. if not node_too_big:
  50. node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr)
  51. if node_too_big or not solution_found:
  52. found_solution_for_all = False
  53. new_location = node.getPosition()
  54. new_location = new_location.set(z = 100 - i * 20)
  55. node.setPosition(new_location)
  56. nodes.append(node)
  57. current_progress += 1
  58. status_message.setProgress((current_progress / total_progress) * 100)
  59. Job.yieldThread()
  60. Job.yieldThread()
  61. if nodes:
  62. op = GroupedOperation()
  63. for new_node in nodes:
  64. op.addOperation(AddSceneNodeOperation(new_node, current_node.getParent()))
  65. op.push()
  66. status_message.hide()
  67. if not found_solution_for_all:
  68. 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"))
  69. no_full_solution_message.show()