HitChecker.py 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. from typing import List, Dict
  2. from cura.Scene.CuraSceneNode import CuraSceneNode
  3. class HitChecker:
  4. """Checks if nodes can be printed without causing any collisions and interference"""
  5. def __init__(self, nodes: List[CuraSceneNode]) -> None:
  6. self._hit_map = self._buildHitMap(nodes)
  7. def anyTwoNodesBlockEachOther(self, nodes: List[CuraSceneNode]) -> bool:
  8. """Returns True if any 2 nodes block each other"""
  9. for a in nodes:
  10. for b in nodes:
  11. if self._hit_map[a][b] and self._hit_map[b][a]:
  12. return True
  13. return False
  14. def canPrintBefore(self, node: CuraSceneNode, other_nodes: List[CuraSceneNode]) -> bool:
  15. """Returns True if node doesn't block other_nodes and can be printed before them"""
  16. no_hits = all(not self._hit_map[node][other_node] for other_node in other_nodes)
  17. return no_hits
  18. def canPrintAfter(self, node: CuraSceneNode, other_nodes: List[CuraSceneNode]) -> bool:
  19. """Returns True if node doesn't hit other nodes and can be printed after them"""
  20. no_hits = all(not self._hit_map[other_node][node] for other_node in other_nodes)
  21. return no_hits
  22. def calculateScore(self, a: CuraSceneNode, b: CuraSceneNode) -> int:
  23. """Calculate score simply sums the number of other objects it 'blocks'
  24. :param a: node
  25. :param b: node
  26. :return: sum of the number of other objects
  27. """
  28. score_a = sum(self._hit_map[a].values())
  29. score_b = sum(self._hit_map[b].values())
  30. return score_a - score_b
  31. def canPrintNodesInProvidedOrder(self, ordered_nodes: List[CuraSceneNode]) -> bool:
  32. """Returns True If nodes don't have any hits in provided order"""
  33. for node_index, node in enumerate(ordered_nodes):
  34. nodes_before = ordered_nodes[:node_index - 1] if node_index - 1 >= 0 else []
  35. nodes_after = ordered_nodes[node_index + 1:] if node_index + 1 < len(ordered_nodes) else []
  36. if not self.canPrintBefore(node, nodes_after) or not self.canPrintAfter(node, nodes_before):
  37. return False
  38. return True
  39. @staticmethod
  40. def _buildHitMap(nodes: List[CuraSceneNode]) -> Dict[CuraSceneNode, CuraSceneNode]:
  41. """Pre-computes all hits between all objects
  42. :nodes: nodes that need to be checked for collisions
  43. :return: dictionary where hit_map[node1][node2] is False if there node1 can be printed before node2
  44. """
  45. hit_map = {j: {i: HitChecker._checkHit(j, i) for i in nodes} for j in nodes}
  46. return hit_map
  47. @staticmethod
  48. def _checkHit(a: CuraSceneNode, b: CuraSceneNode) -> bool:
  49. """Checks if a can be printed before b
  50. :param a: node
  51. :param b: node
  52. :return: False if a can be printed before b
  53. """
  54. if a == b:
  55. return False
  56. a_hit_hull = a.callDecoration("getConvexHullBoundary")
  57. b_hit_hull = b.callDecoration("getConvexHullHeadFull")
  58. overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
  59. if overlap:
  60. return True
  61. # Adhesion areas must never overlap, regardless of printing order
  62. # This would cause over-extrusion
  63. a_hit_hull = a.callDecoration("getAdhesionArea")
  64. b_hit_hull = b.callDecoration("getAdhesionArea")
  65. overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
  66. if overlap:
  67. return True
  68. else:
  69. return False