ArrangeObjectsJob.py 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. # Copyright (c) 2017 Ultimaker B.V.
  2. # Cura is released under the terms of the AGPLv3 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.GroupedOperation import GroupedOperation
  8. from UM.Message import Message
  9. from UM.i18n import i18nCatalog
  10. i18n_catalog = i18nCatalog("cura")
  11. from cura.ZOffsetDecorator import ZOffsetDecorator
  12. from cura.Arrange import Arrange
  13. from cura.ShapeArray import ShapeArray
  14. from typing import List
  15. class ArrangeObjectsJob(Job):
  16. def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset = 8):
  17. super().__init__()
  18. self._nodes = nodes
  19. self._fixed_nodes = fixed_nodes
  20. self._min_offset = min_offset
  21. def run(self):
  22. status_message = Message(i18n_catalog.i18nc("@info:status", "Finding new location for objects"), lifetime = 0, dismissable=False, progress = 0)
  23. status_message.show()
  24. arranger = Arrange.create(fixed_nodes = self._fixed_nodes)
  25. # Collect nodes to be placed
  26. nodes_arr = [] # fill with (size, node, offset_shape_arr, hull_shape_arr)
  27. for node in self._nodes:
  28. offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = self._min_offset)
  29. nodes_arr.append((offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr))
  30. # Sort the nodes with the biggest area first.
  31. nodes_arr.sort(key=lambda item: item[0])
  32. nodes_arr.reverse()
  33. # Place nodes one at a time
  34. start_priority = 0
  35. last_priority = start_priority
  36. last_size = None
  37. grouped_operation = GroupedOperation()
  38. for idx, (size, node, offset_shape_arr, hull_shape_arr) in enumerate(nodes_arr):
  39. # For performance reasons, we assume that when a location does not fit,
  40. # it will also not fit for the next object (while what can be untrue).
  41. # We also skip possibilities by slicing through the possibilities (step = 10)
  42. if last_size == size: # This optimization works if many of the objects have the same size
  43. start_priority = last_priority
  44. else:
  45. start_priority = 0
  46. best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority, step=10)
  47. x, y = best_spot.x, best_spot.y
  48. last_size = size
  49. last_priority = best_spot.priority
  50. if x is not None: # We could find a place
  51. arranger.place(x, y, hull_shape_arr) # take place before the next one
  52. node.removeDecorator(ZOffsetDecorator)
  53. if node.getBoundingBox():
  54. center_y = node.getWorldPosition().y - node.getBoundingBox().bottom
  55. else:
  56. center_y = 0
  57. grouped_operation.addOperation(SetTransformOperation(node, Vector(x, center_y, y)))
  58. status_message.setProgress((idx + 1) / len(nodes_arr) * 100)
  59. Job.yieldThread()
  60. grouped_operation.push()
  61. status_message.hide()