mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 23:17:32 -06:00
Changes required for printing one at a time
This commit is contained in:
parent
1ce7e78685
commit
78ebb13089
5 changed files with 142 additions and 9 deletions
|
@ -5,6 +5,13 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._convex_hull = None
|
self._convex_hull = None
|
||||||
|
|
||||||
|
# In case of printing all at once this is the same as the convex hull. For one at the time this is the area without the head.
|
||||||
|
self._convex_hull_boundary = None
|
||||||
|
|
||||||
|
# In case of printing all at once this is the same as the convex hull. For one at the time this is area with full head
|
||||||
|
self._convex_hull_head = None
|
||||||
|
|
||||||
self._convex_hull_node = None
|
self._convex_hull_node = None
|
||||||
self._convex_hull_job = None
|
self._convex_hull_job = None
|
||||||
settings = Application.getInstance().getActiveMachine()
|
settings = Application.getInstance().getActiveMachine()
|
||||||
|
@ -23,6 +30,22 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||||
def getConvexHull(self):
|
def getConvexHull(self):
|
||||||
return self._convex_hull
|
return self._convex_hull
|
||||||
|
|
||||||
|
def getConvexHullHead(self):
|
||||||
|
if not self._convex_hull_head:
|
||||||
|
return self.getConvexHull()
|
||||||
|
return self._convex_hull_head
|
||||||
|
|
||||||
|
def getConvexHullBoundary(self):
|
||||||
|
if not self._convex_hull_boundary:
|
||||||
|
return self.getConvexHull()
|
||||||
|
return self._convex_hull_boundary
|
||||||
|
|
||||||
|
def setConvexHullBoundary(self, hull):
|
||||||
|
self._convex_hull_boundary = hull
|
||||||
|
|
||||||
|
def setConvexHullHead(self, hull):
|
||||||
|
self._convex_hull_head = hull
|
||||||
|
|
||||||
def setConvexHull(self, hull):
|
def setConvexHull(self, hull):
|
||||||
self._convex_hull = hull
|
self._convex_hull = hull
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from UM.Application import Application
|
||||||
from UM.Math.Polygon import Polygon
|
from UM.Math.Polygon import Polygon
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
import copy
|
||||||
from . import ConvexHullNode
|
from . import ConvexHullNode
|
||||||
|
|
||||||
class ConvexHullJob(Job):
|
class ConvexHullJob(Job):
|
||||||
|
@ -49,6 +49,9 @@ class ConvexHullJob(Job):
|
||||||
|
|
||||||
if settings.getSettingValueByKey("print_sequence") == "One at a time" and not self._node.getParent().callDecoration("isGroup"):
|
if settings.getSettingValueByKey("print_sequence") == "One at a time" and not self._node.getParent().callDecoration("isGroup"):
|
||||||
# Printing one at a time and it's not an object in a group
|
# Printing one at a time and it's not an object in a group
|
||||||
|
self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull))
|
||||||
|
head_hull = hull.getMinkowskiHull(Polygon(numpy.array(settings.getSettingValueByKey("machine_head_with_fans_polygon"),numpy.float32)))
|
||||||
|
self._node.callDecoration("setConvexHullHead", head_hull)
|
||||||
hull = hull.getMinkowskiHull(Polygon(numpy.array(settings.getSettingValueByKey("machine_head_polygon"),numpy.float32)))
|
hull = hull.getMinkowskiHull(Polygon(numpy.array(settings.getSettingValueByKey("machine_head_polygon"),numpy.float32)))
|
||||||
hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot())
|
hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot())
|
||||||
self._node.callDecoration("setConvexHullNode", hull_node)
|
self._node.callDecoration("setConvexHullNode", hull_node)
|
||||||
|
|
102
cura/OneAtATimeIterator.py
Normal file
102
cura/OneAtATimeIterator.py
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
# Copyright (c) 2015 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the AGPLv3 or higher.
|
||||||
|
|
||||||
|
from UM.Scene.Iterator import Iterator
|
||||||
|
from functools import cmp_to_key
|
||||||
|
|
||||||
|
## Iterator that returns a list of nodes in the order that they need to be printed
|
||||||
|
# If there is no solution an empty list is returned.
|
||||||
|
# Take note that the list of nodes can have children (that may or may not contain mesh data)
|
||||||
|
class OneAtATimeIterator(Iterator.Iterator):
|
||||||
|
def __init__(self, scene_node):
|
||||||
|
super(OneAtATimeIterator, self).__init__(scene_node) # Call super to make multiple inheritence work.
|
||||||
|
self._hit_map = [[]]
|
||||||
|
self._original_node_list = []
|
||||||
|
|
||||||
|
def _fillStack(self):
|
||||||
|
node_list = []
|
||||||
|
for node in self._scene_node.getChildren():
|
||||||
|
if node.callDecoration("getConvexHull"):
|
||||||
|
node_list.append(node)
|
||||||
|
|
||||||
|
if len(node_list) < 2:
|
||||||
|
return node_list
|
||||||
|
|
||||||
|
self._original_node_list = node_list[:]
|
||||||
|
|
||||||
|
## Initialise the hit map (pre-compute all hits between all objects)
|
||||||
|
self._hit_map = [[self._checkHit(j,i) for i in node_list] for j in node_list]
|
||||||
|
|
||||||
|
# Check if we have to files that block eachother. If this is the case, there is no solution!
|
||||||
|
for a in range(0,len(node_list)):
|
||||||
|
for b in range(0,len(node_list)):
|
||||||
|
if a != b and self._hit_map[a][b] and self._hit_map[b][a]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# Sort the original list so that items that block the most other objects are at the beginning.
|
||||||
|
# This does not decrease the worst case running time, but should improve it in most cases.
|
||||||
|
sorted(node_list, key = cmp_to_key(self._calculateScore))
|
||||||
|
|
||||||
|
todo_node_list = [_objectOrder([], node_list)]
|
||||||
|
while len(todo_node_list) > 0:
|
||||||
|
current = todo_node_list.pop()
|
||||||
|
for node in current.todo:
|
||||||
|
# Check if the object can be placed with what we have and still allows for a solution in the future
|
||||||
|
if not self._checkHitMultiple(node, current.order) and not self._checkBlockMultiple(node, current.todo):
|
||||||
|
# We found a possible result. Create new todo & order list.
|
||||||
|
new_todo_list = current.todo[:]
|
||||||
|
new_todo_list.remove(node)
|
||||||
|
new_order = current.order[:] + [node]
|
||||||
|
if len(new_todo_list) == 0:
|
||||||
|
# We have no more nodes to check, so quit looking.
|
||||||
|
todo_node_list = None
|
||||||
|
self._node_stack = new_order
|
||||||
|
return
|
||||||
|
todo_node_list.append(_objectOrder(new_order, new_todo_list))
|
||||||
|
|
||||||
|
self._node_stack = [] #No result found!
|
||||||
|
|
||||||
|
|
||||||
|
# Check if first object can be printed before the provided list (using the hit map)
|
||||||
|
def _checkHitMultiple(self, node, other_nodes):
|
||||||
|
node_index = self._original_node_list.index(node)
|
||||||
|
for other_node in other_nodes:
|
||||||
|
if self._hit_map[node_index][self._original_node_list.index(other_node)]:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _checkBlockMultiple(self, node, other_nodes):
|
||||||
|
node_index = self._original_node_list.index(node)
|
||||||
|
for other_node in other_nodes:
|
||||||
|
if self._hit_map[self._original_node_list.index(other_node)][node_index] and node_index != self._original_node_list.index(other_node):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
## Calculate score simply sums the number of other objects it 'blocks'
|
||||||
|
def _calculateScore(self, a, b):
|
||||||
|
score_a = sum(self._hit_map[self._original_node_list.index(a)])
|
||||||
|
score_b = sum(self._hit_map[self._original_node_list.index(b)])
|
||||||
|
return score_a - score_b
|
||||||
|
|
||||||
|
# Checks if A can be printed before B
|
||||||
|
def _checkHit(self, a, b):
|
||||||
|
if a == b:
|
||||||
|
return False
|
||||||
|
|
||||||
|
overlap = a.callDecoration("getConvexHullBoundary").intersectsPolygon(b.callDecoration("getConvexHullHead"))
|
||||||
|
if overlap:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
## Internal object used to keep track of a possible order in which to print objects.
|
||||||
|
class _objectOrder():
|
||||||
|
def __init__(self, order, todo):
|
||||||
|
"""
|
||||||
|
:param order: List of indexes in which to print objects, ordered by printing order.
|
||||||
|
:param todo: List of indexes which are not yet inserted into the order list.
|
||||||
|
"""
|
||||||
|
self.order = order
|
||||||
|
self.todo = todo
|
||||||
|
|
|
@ -11,6 +11,7 @@ from UM.Signal import Signal
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Resources import Resources
|
from UM.Resources import Resources
|
||||||
|
|
||||||
|
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||||
from . import Cura_pb2
|
from . import Cura_pb2
|
||||||
from . import ProcessSlicedObjectListJob
|
from . import ProcessSlicedObjectListJob
|
||||||
from . import ProcessGCodeJob
|
from . import ProcessGCodeJob
|
||||||
|
@ -147,6 +148,10 @@ class CuraEngineBackend(Backend):
|
||||||
msg = Cura_pb2.ObjectList()
|
msg = Cura_pb2.ObjectList()
|
||||||
|
|
||||||
#TODO: All at once/one at a time mode
|
#TODO: All at once/one at a time mode
|
||||||
|
#print("Iterator time! ", OneAtATimeIterator(self._scene.getRoot()))
|
||||||
|
#for item in OneAtATimeIterator(self._scene.getRoot()):
|
||||||
|
# print(item)
|
||||||
|
|
||||||
center = Vector()
|
center = Vector()
|
||||||
for object in objects:
|
for object in objects:
|
||||||
center += object.getPosition()
|
center += object.getPosition()
|
||||||
|
|
|
@ -83,20 +83,20 @@
|
||||||
{
|
{
|
||||||
"default": [
|
"default": [
|
||||||
[
|
[
|
||||||
-15,
|
-20,
|
||||||
15
|
10
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
15,
|
10,
|
||||||
15
|
10
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
15,
|
10,
|
||||||
-15
|
-10
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
-15,
|
-20,
|
||||||
-15
|
-10
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue