Merge branch '2.1' of https://github.com/Ultimaker/Cura into 2.1

This commit is contained in:
Tim Kuipers 2016-03-02 15:14:33 +01:00
commit 98652fea50
5 changed files with 51 additions and 24 deletions

View File

@ -9,8 +9,10 @@ class ConvexHullDecorator(SceneNodeDecorator):
# 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. # 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 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 # In case of printing all at once this is the same as the convex hull. For one at the time this is area with intersection of mirrored head
self._convex_hull_head = None self._convex_hull_head = 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 intersection of full head
self._convex_hull_head_full = None
self._convex_hull_node = None self._convex_hull_node = None
self._convex_hull_job = None self._convex_hull_job = None
@ -28,6 +30,11 @@ class ConvexHullDecorator(SceneNodeDecorator):
def getConvexHull(self): def getConvexHull(self):
return self._convex_hull return self._convex_hull
def getConvexHullHeadFull(self):
if not self._convex_hull_head_full:
return self.getConvexHull()
return self._convex_hull_head_full
def getConvexHullHead(self): def getConvexHullHead(self):
if not self._convex_hull_head: if not self._convex_hull_head:
return self.getConvexHull() return self.getConvexHull()
@ -40,7 +47,10 @@ class ConvexHullDecorator(SceneNodeDecorator):
def setConvexHullBoundary(self, hull): def setConvexHullBoundary(self, hull):
self._convex_hull_boundary = hull self._convex_hull_boundary = hull
def setConvexHullHeadFull(self, hull):
self._convex_hull_head_full = hull
def setConvexHullHead(self, hull): def setConvexHullHead(self, hull):
self._convex_hull_head = hull self._convex_hull_head = hull

View File

@ -55,12 +55,16 @@ class ConvexHullJob(Job):
# 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)) self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull))
head_and_fans = Polygon(numpy.array(profile.getSettingValue("machine_head_with_fans_polygon"), numpy.float32)) head_and_fans = Polygon(numpy.array(profile.getSettingValue("machine_head_with_fans_polygon"), numpy.float32))
# Full head hull is used to actually check the order.
full_head_hull = hull.getMinkowskiHull(head_and_fans)
self._node.callDecoration("setConvexHullHeadFull", full_head_hull)
mirrored = copy.deepcopy(head_and_fans) mirrored = copy.deepcopy(head_and_fans)
mirrored.mirror([0, 0], [0, 1]) #Mirror horizontally. mirrored.mirror([0, 0], [0, 1]) #Mirror horizontally.
mirrored.mirror([0, 0], [1, 0]) #Mirror vertically. mirrored.mirror([0, 0], [1, 0]) #Mirror vertically.
head_and_fans = head_and_fans.intersectionConvexHulls(mirrored) head_and_fans = head_and_fans.intersectionConvexHulls(mirrored)
head_hull = hull.getMinkowskiHull(head_and_fans) # Min head hull is used for the push free
self._node.callDecoration("setConvexHullHead", head_hull) min_head_hull = hull.getMinkowskiHull(head_and_fans)
self._node.callDecoration("setConvexHullHead", min_head_hull)
hull = hull.getMinkowskiHull(Polygon(numpy.array(profile.getSettingValue("machine_head_polygon"),numpy.float32))) hull = hull.getMinkowskiHull(Polygon(numpy.array(profile.getSettingValue("machine_head_polygon"),numpy.float32)))
else: else:
self._node.callDecoration("setConvexHullHead", None) self._node.callDecoration("setConvexHullHead", None)

View File

@ -25,22 +25,23 @@ class OneAtATimeIterator(Iterator.Iterator):
return return
if node.callDecoration("getConvexHull"): if node.callDecoration("getConvexHull"):
node_list.append(node) node_list.append(node)
if len(node_list) < 2: if len(node_list) < 2:
self._node_stack = node_list[:] self._node_stack = node_list[:]
return return
# Copy the list
self._original_node_list = node_list[:] self._original_node_list = node_list[:]
## Initialise the hit map (pre-compute all hits between all objects) ## 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] self._hit_map = [[self._checkHit(i,j) 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! # 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 a in range(0,len(node_list)):
for b 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]: if a != b and self._hit_map[a][b] and self._hit_map[b][a]:
return return
# Sort the original list so that items that block the most other objects are at the beginning. # 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. # 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)) sorted(node_list, key = cmp_to_key(self._calculateScore))
@ -59,44 +60,46 @@ class OneAtATimeIterator(Iterator.Iterator):
# We have no more nodes to check, so quit looking. # We have no more nodes to check, so quit looking.
todo_node_list = None todo_node_list = None
self._node_stack = new_order self._node_stack = new_order
return return
todo_node_list.append(_ObjectOrder(new_order, new_todo_list)) todo_node_list.append(_ObjectOrder(new_order, new_todo_list))
self._node_stack = [] #No result found! self._node_stack = [] #No result found!
# Check if first object can be printed before the provided list (using the hit map) # Check if first object can be printed before the provided list (using the hit map)
def _checkHitMultiple(self, node, other_nodes): def _checkHitMultiple(self, node, other_nodes):
node_index = self._original_node_list.index(node) node_index = self._original_node_list.index(node)
for other_node in other_nodes: for other_node in other_nodes:
if self._hit_map[node_index][self._original_node_list.index(other_node)]: other_node_index = self._original_node_list.index(other_node)
if self._hit_map[node_index][other_node_index]:
return True return True
return False return False
def _checkBlockMultiple(self, node, other_nodes): def _checkBlockMultiple(self, node, other_nodes):
node_index = self._original_node_list.index(node) node_index = self._original_node_list.index(node)
for other_node in other_nodes: 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): other_node_index = self._original_node_list.index(other_node)
if self._hit_map[other_node_index][node_index] and node_index != other_node_index:
return True return True
return False return False
## Calculate score simply sums the number of other objects it 'blocks' ## Calculate score simply sums the number of other objects it 'blocks'
def _calculateScore(self, a, b): def _calculateScore(self, a, b):
score_a = sum(self._hit_map[self._original_node_list.index(a)]) score_a = sum(self._hit_map[self._original_node_list.index(a)])
score_b = sum(self._hit_map[self._original_node_list.index(b)]) score_b = sum(self._hit_map[self._original_node_list.index(b)])
return score_a - score_b return score_a - score_b
# Checks if A can be printed before B # Checks if A can be printed before B
def _checkHit(self, a, b): def _checkHit(self, a, b):
if a == b: if a == b:
return False return False
overlap = a.callDecoration("getConvexHullBoundary").intersectsPolygon(b.callDecoration("getConvexHullHead")) overlap = a.callDecoration("getConvexHullBoundary").intersectsPolygon(b.callDecoration("getConvexHullHeadFull"))
if overlap: if overlap:
return True return True
else: else:
return False return False
## Internal object used to keep track of a possible order in which to print objects. ## Internal object used to keep track of a possible order in which to print objects.
class _ObjectOrder(): class _ObjectOrder():
@ -107,4 +110,4 @@ class _ObjectOrder():
""" """
self.order = order self.order = order
self.todo = todo self.todo = todo

View File

@ -4,6 +4,7 @@
from UM.Application import Application #To get the machine manager to create the new profile in. from UM.Application import Application #To get the machine manager to create the new profile in.
from UM.Settings.Profile import Profile from UM.Settings.Profile import Profile
from UM.Settings.ProfileReader import ProfileReader from UM.Settings.ProfileReader import ProfileReader
from UM.Logger import Logger
import re #Regular expressions for parsing escape characters in the settings. import re #Regular expressions for parsing escape characters in the settings.
## A class that reads profile data from g-code files. ## A class that reads profile data from g-code files.
@ -40,6 +41,9 @@ class GCodeProfileReader(ProfileReader):
# specified file was no g-code or contained no parsable profile, \code # specified file was no g-code or contained no parsable profile, \code
# None \endcode is returned. # None \endcode is returned.
def read(self, file_name): def read(self, file_name):
if file_name.split(".")[-1] != "gcode":
return None
prefix = ";SETTING_" + str(GCodeProfileReader.version) + " " prefix = ";SETTING_" + str(GCodeProfileReader.version) + " "
prefix_length = len(prefix) prefix_length = len(prefix)
@ -62,6 +66,10 @@ class GCodeProfileReader(ProfileReader):
profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False) profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False)
try: try:
profile.unserialise(serialised) profile.unserialise(serialised)
except Exception as e: #Not a valid g-code file. profile.setType(None) #Force type to none so it's correctly added.
profile.setReadOnly(False)
profile.setDirty(True)
except Exception as e: #Not a valid g-comachine_instance_profilede file.
Logger.log("e", "Unable to serialise the profile: %s", str(e))
return None return None
return profile return profile

View File

@ -63,6 +63,8 @@ class LegacyProfileReader(ProfileReader):
# file could not be read or didn't contain a valid profile, \code None # file could not be read or didn't contain a valid profile, \code None
# \endcode is returned. # \endcode is returned.
def read(self, file_name): def read(self, file_name):
if file_name.split(".")[-1] != "ini":
return None
Logger.log("i", "Importing legacy profile from file " + file_name + ".") Logger.log("i", "Importing legacy profile from file " + file_name + ".")
profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False) #Create an empty profile. profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False) #Create an empty profile.
@ -122,5 +124,5 @@ class LegacyProfileReader(ProfileReader):
if len(profile.getChangedSettings()) == 0: if len(profile.getChangedSettings()) == 0:
Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.") Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.")
profile.setDirty(True)
return profile return profile