OneAtATimeIterator.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. # Copyright (c) 2019 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from typing import List
  4. from UM.Scene.Iterator import Iterator
  5. from UM.Scene.SceneNode import SceneNode
  6. from functools import cmp_to_key
  7. from cura.HitChecker import HitChecker
  8. from cura.PrintOrderManager import PrintOrderManager
  9. from cura.Scene.CuraSceneNode import CuraSceneNode
  10. class OneAtATimeIterator(Iterator.Iterator):
  11. """Iterator that returns a list of nodes in the order that they need to be printed
  12. If there is no solution an empty list is returned.
  13. Take note that the list of nodes can have children (that may or may not contain mesh data)
  14. """
  15. def __init__(self, scene_node) -> None:
  16. super().__init__(scene_node) # Call super to make multiple inheritance work.
  17. def _fillStack(self) -> None:
  18. """Fills the ``_node_stack`` with a list of scene nodes that need to be printed in order. """
  19. node_list = []
  20. for node in self._scene_node.getChildren():
  21. if not issubclass(type(node), SceneNode):
  22. continue
  23. # Node can't be printed, so don't bother sending it.
  24. if getattr(node, "_outside_buildarea", False):
  25. continue
  26. if node.callDecoration("getConvexHull"):
  27. node_list.append(node)
  28. if len(node_list) < 2:
  29. self._node_stack = node_list[:]
  30. return
  31. hit_checker = HitChecker(node_list)
  32. if PrintOrderManager.isUserDefinedPrintOrderEnabled():
  33. self._node_stack = self._getNodesOrderedByUser(hit_checker, node_list)
  34. else:
  35. self._node_stack = self._getNodesOrderedAutomatically(hit_checker, node_list)
  36. # update print orders so that user can try to arrange the nodes automatically first
  37. # and if result is not satisfactory he/she can switch to manual mode and change it
  38. for index, node in enumerate(self._node_stack):
  39. node.printOrder = index + 1
  40. @staticmethod
  41. def _getNodesOrderedByUser(hit_checker: HitChecker, node_list: List[CuraSceneNode]) -> List[CuraSceneNode]:
  42. nodes_ordered_by_user = sorted(node_list, key=lambda n: n.printOrder)
  43. if hit_checker.canPrintNodesInProvidedOrder(nodes_ordered_by_user):
  44. return nodes_ordered_by_user
  45. return [] # No solution
  46. @staticmethod
  47. def _getNodesOrderedAutomatically(hit_checker: HitChecker, node_list: List[CuraSceneNode]) -> List[CuraSceneNode]:
  48. # Check if we have two files that block each other. If this is the case, there is no solution!
  49. if hit_checker.anyTwoNodesBlockEachOther(node_list):
  50. return [] # No solution
  51. # Sort the original list so that items that block the most other objects are at the beginning.
  52. # This does not decrease the worst case running time, but should improve it in most cases.
  53. node_list = sorted(node_list, key = cmp_to_key(hit_checker.calculateScore))
  54. todo_node_list = [_ObjectOrder([], node_list)]
  55. while len(todo_node_list) > 0:
  56. current = todo_node_list.pop()
  57. for node in current.todo:
  58. # Check if the object can be placed with what we have and still allows for a solution in the future
  59. if hit_checker.canPrintAfter(node, current.order) and hit_checker.canPrintBefore(node, current.todo):
  60. # We found a possible result. Create new todo & order list.
  61. new_todo_list = current.todo[:]
  62. new_todo_list.remove(node)
  63. new_order = current.order[:] + [node]
  64. if len(new_todo_list) == 0:
  65. # We have no more nodes to check, so quit looking.
  66. return new_order # Solution found!
  67. todo_node_list.append(_ObjectOrder(new_order, new_todo_list))
  68. return [] # No result found!
  69. class _ObjectOrder:
  70. """Internal object used to keep track of a possible order in which to print objects."""
  71. def __init__(self, order: List[SceneNode], todo: List[SceneNode]) -> None:
  72. """Creates the _ObjectOrder instance.
  73. :param order: List of indices in which to print objects, ordered by printing order.
  74. :param todo: List of indices which are not yet inserted into the order list.
  75. """
  76. self.order = order
  77. self.todo = todo