mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-04-22 21:59:37 +08:00
89 lines
3.4 KiB
Python
89 lines
3.4 KiB
Python
from typing import List, Dict
|
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
|
|
|
|
|
class HitChecker:
|
|
"""Checks if nodes can be printed without causing any collisions and interference"""
|
|
|
|
def __init__(self, nodes: List[CuraSceneNode]) -> None:
|
|
self._hit_map = self._buildHitMap(nodes)
|
|
|
|
def anyTwoNodesBlockEachOther(self, nodes: List[CuraSceneNode]) -> bool:
|
|
"""Returns True if any 2 nodes block each other"""
|
|
for a in nodes:
|
|
for b in nodes:
|
|
if self._hit_map[a][b] and self._hit_map[b][a]:
|
|
return True
|
|
return False
|
|
|
|
def canPrintBefore(self, node: CuraSceneNode, other_nodes: List[CuraSceneNode]) -> bool:
|
|
"""Returns True if node doesn't block other_nodes and can be printed before them"""
|
|
no_hits = all(not self._hit_map[node][other_node] for other_node in other_nodes)
|
|
return no_hits
|
|
|
|
def canPrintAfter(self, node: CuraSceneNode, other_nodes: List[CuraSceneNode]) -> bool:
|
|
"""Returns True if node doesn't hit other nodes and can be printed after them"""
|
|
no_hits = all(not self._hit_map[other_node][node] for other_node in other_nodes)
|
|
return no_hits
|
|
|
|
def calculateScore(self, a: CuraSceneNode, b: CuraSceneNode) -> int:
|
|
"""Calculate score simply sums the number of other objects it 'blocks'
|
|
|
|
:param a: node
|
|
:param b: node
|
|
:return: sum of the number of other objects
|
|
"""
|
|
|
|
score_a = sum(self._hit_map[a].values())
|
|
score_b = sum(self._hit_map[b].values())
|
|
return score_a - score_b
|
|
|
|
def canPrintNodesInProvidedOrder(self, ordered_nodes: List[CuraSceneNode]) -> bool:
|
|
"""Returns True If nodes don't have any hits in provided order"""
|
|
for node_index, node in enumerate(ordered_nodes):
|
|
nodes_before = ordered_nodes[:node_index - 1] if node_index - 1 >= 0 else []
|
|
nodes_after = ordered_nodes[node_index + 1:] if node_index + 1 < len(ordered_nodes) else []
|
|
if not self.canPrintBefore(node, nodes_after) or not self.canPrintAfter(node, nodes_before):
|
|
return False
|
|
return True
|
|
|
|
@staticmethod
|
|
def _buildHitMap(nodes: List[CuraSceneNode]) -> Dict[CuraSceneNode, CuraSceneNode]:
|
|
"""Pre-computes all hits between all objects
|
|
|
|
:nodes: nodes that need to be checked for collisions
|
|
:return: dictionary where hit_map[node1][node2] is False if there node1 can be printed before node2
|
|
"""
|
|
hit_map = {j: {i: HitChecker._checkHit(j, i) for i in nodes} for j in nodes}
|
|
return hit_map
|
|
|
|
@staticmethod
|
|
def _checkHit(a: CuraSceneNode, b: CuraSceneNode) -> bool:
|
|
"""Checks if a can be printed before b
|
|
|
|
:param a: node
|
|
:param b: node
|
|
:return: False if a can be printed before b
|
|
"""
|
|
|
|
if a == b:
|
|
return False
|
|
|
|
a_hit_hull = a.callDecoration("getConvexHullBoundary")
|
|
b_hit_hull = b.callDecoration("getConvexHullHeadFull")
|
|
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
|
|
|
|
if overlap:
|
|
return True
|
|
|
|
# Adhesion areas must never overlap, regardless of printing order
|
|
# This would cause over-extrusion
|
|
a_hit_hull = a.callDecoration("getAdhesionArea")
|
|
b_hit_hull = b.callDecoration("getAdhesionArea")
|
|
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
|
|
|
|
if overlap:
|
|
return True
|
|
else:
|
|
return False
|