+__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
import random
import numpy
+from Cura.util import profile
+from Cura.util import polygon
+
class _objectOrder(object):
def __init__(self, order, todo):
self.order = order
self.todo = todo
class _objectOrderFinder(object):
- def __init__(self, scene, offset, leftToRight, frontToBack, gantryHeight):
+ def __init__(self, scene, leftToRight, frontToBack, gantryHeight):
self._scene = scene
- self._offset = offset - numpy.array([0.1,0.1])
self._objs = scene.objects()
self._leftToRight = leftToRight
self._frontToBack = frontToBack
for n in xrange(0, len(self._objs)):
if scene.checkPlatform(self._objs[n]):
initialList.append(n)
+ for n in initialList:
+ if self._objs[n].getSize()[2] > gantryHeight and len(initialList) > 1:
+ self.order = None
+ return
if len(initialList) == 0:
self.order = []
return
+
+ self._hitMap = [None] * (max(initialList)+1)
+ for a in initialList:
+ self._hitMap[a] = [False] * (max(initialList)+1)
+ for b in initialList:
+ self._hitMap[a][b] = self._checkHit(a, b)
+
+ #Check if we have 2 files that overlap so that they can never be printed one at a time.
+ for a in initialList:
+ for b in initialList:
+ if a != b and self._hitMap[a][b] and self._hitMap[b][a]:
+ self.order = None
+ return
+
+ initialList.sort(self._objIdxCmp)
+
+ n = 0
self._todo = [_objectOrder([], initialList)]
while len(self._todo) > 0:
+ n += 1
current = self._todo.pop()
+ #print len(self._todo), len(current.order), len(initialList), current.order
for addIdx in current.todo:
- if not self._checkHitFor(addIdx, current.order):
+ if not self._checkHitFor(addIdx, current.order) and not self._checkBlocks(addIdx, current.todo):
todoList = current.todo[:]
todoList.remove(addIdx)
order = current.order[:] + [addIdx]
self._todo.append(_objectOrder(order, todoList))
self.order = None
+ def _objIdxCmp(self, a, b):
+ scoreA = sum(self._hitMap[a])
+ scoreB = sum(self._hitMap[b])
+ return scoreA - scoreB
+
def _checkHitFor(self, addIdx, others):
for idx in others:
- if self._checkHit(addIdx, idx):
+ if self._hitMap[addIdx][idx]:
return True
return False
- def _checkHit(self, addIdx, idx):
- addPos = self._scene._objectList[addIdx].getPosition()
- addSize = self._scene._objectList[addIdx].getSize()
- pos = self._scene._objectList[idx].getPosition()
- size = self._scene._objectList[idx].getSize()
-
- if self._leftToRight:
- if addPos[0] - addSize[0] / 2 - self._offset[0] <= pos[0] + size[0] / 2:
- return False
- else:
- if addPos[0] + addSize[0] / 2 + self._offset[0] <= pos[0] - size[0] / 2:
- return False
-
- if self._frontToBack:
- if addPos[1] - addSize[1] / 2 - self._offset[1] >= pos[1] + size[1] / 2:
- return False
- else:
- if addPos[1] + addSize[1] / 2 + self._offset[1] >= pos[1] - size[1] / 2:
- return False
+ def _checkBlocks(self, addIdx, others):
+ for idx in others:
+ if addIdx != idx and self._hitMap[idx][addIdx]:
+ return True
+ return False
- return True
+ #Check if printing one object will cause printhead colission with other object.
+ def _checkHit(self, addIdx, idx):
+ obj = self._scene._objectList[idx]
+ addObj = self._scene._objectList[addIdx]
+ return polygon.polygonCollision(obj._boundaryHull + obj.getPosition(), addObj._headAreaHull + addObj.getPosition())
class Scene(object):
def __init__(self):
self._objectList = []
self._sizeOffsets = numpy.array([0.0,0.0], numpy.float32)
self._machineSize = numpy.array([100,100,100], numpy.float32)
- self._headOffsets = numpy.array([18.0,18.0], numpy.float32)
+ self._headSizeOffsets = numpy.array([18.0,18.0], numpy.float32)
+ self._minExtruderCount = None
+ self._extruderOffset = [numpy.array([0,0], numpy.float32)] * 4
+
+ #Print order variables
self._leftToRight = False
self._frontToBack = True
self._gantryHeight = 60
+ self._oneAtATime = True
+
+ # update the physical machine dimensions
+ def updateMachineDimensions(self):
+ self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
+ self._machinePolygons = profile.getMachineSizePolygons()
+ self.setHeadSize(profile.getMachineSettingFloat('extruder_head_size_min_x'), profile.getMachineSettingFloat('extruder_head_size_max_x'), profile.getMachineSettingFloat('extruder_head_size_min_y'), profile.getMachineSettingFloat('extruder_head_size_max_y'), profile.getMachineSettingFloat('extruder_head_size_height'))
+
+ # Size offsets are offsets caused by brim, skirt, etc.
+ def updateSizeOffsets(self, force=False):
+ newOffsets = numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32)
+ minExtruderCount = profile.minimalExtruderCount()
+ if not force and numpy.array_equal(self._sizeOffsets, newOffsets) and self._minExtruderCount == minExtruderCount:
+ return
+ self._sizeOffsets = newOffsets
+ self._minExtruderCount = minExtruderCount
- def setMachineSize(self, machineSize):
- self._machineSize = machineSize
+ extends = [numpy.array([[-newOffsets[0],-newOffsets[1]],[ newOffsets[0],-newOffsets[1]],[ newOffsets[0], newOffsets[1]],[-newOffsets[0], newOffsets[1]]], numpy.float32)]
+ for n in xrange(1, 4):
+ headOffset = numpy.array([[0, 0], [-profile.getMachineSettingFloat('extruder_offset_x%d' % (n)), -profile.getMachineSettingFloat('extruder_offset_y%d' % (n))]], numpy.float32)
+ extends.append(polygon.minkowskiHull(extends[n-1], headOffset))
+ if minExtruderCount > 1:
+ extends[0] = extends[1]
- def setSizeOffsets(self, sizeOffsets):
- self._sizeOffsets = sizeOffsets
+ for obj in self._objectList:
+ obj.setPrintAreaExtends(extends[len(obj._meshList) - 1])
+ #size of the printing head.
def setHeadSize(self, xMin, xMax, yMin, yMax, gantryHeight):
self._leftToRight = xMin < xMax
self._frontToBack = yMin < yMax
- self._headOffsets[0] = min(xMin, xMax)
- self._headOffsets[1] = min(yMin, yMax)
+ self._headSizeOffsets[0] = min(xMin, xMax)
+ self._headSizeOffsets[1] = min(yMin, yMax)
self._gantryHeight = gantryHeight
+ self._oneAtATime = self._gantryHeight > 0
+
+ headArea = numpy.array([[-xMin,-yMin],[ xMax,-yMin],[ xMax, yMax],[-xMin, yMax]], numpy.float32)
+
+ for obj in self._objectList:
+ obj.setHeadArea(headArea, self._headSizeOffsets)
- def getObjectExtend(self):
- return self._sizeOffsets + self._headOffsets
+ def setExtruderOffset(self, extruderNr, offsetX, offsetY):
+ self._extruderOffset[extruderNr] = numpy.array([offsetX, offsetY], numpy.float32)
def objects(self):
return self._objectList
+ #Add new object to print area
def add(self, obj):
+ if numpy.max(obj.getSize()[0:2]) > numpy.max(self._machineSize[0:2]) * 2.5:
+ scale = numpy.max(self._machineSize[0:2]) * 2.5 / numpy.max(obj.getSize()[0:2])
+ matrix = [[scale,0,0], [0, scale, 0], [0, 0, scale]]
+ obj.applyMatrix(numpy.matrix(matrix, numpy.float64))
self._findFreePositionFor(obj)
self._objectList.append(obj)
+ self.updateSizeOffsets(True)
self.pushFree()
def remove(self, obj):
self._objectList.remove(obj)
+ #Dual(multiple) extrusion merge
def merge(self, obj1, obj2):
self.remove(obj2)
obj1._meshList += obj2._meshList
+ for m in obj2._meshList:
+ m._obj = obj1
obj1.processMatrix()
obj1.setPosition((obj1.getPosition() + obj2.getPosition()) / 2)
self.pushFree()
def pushFree(self):
- n = 1000
+ n = 10
while self._pushFree():
n -= 1
if n < 0:
obj.setPosition(obj.getPosition() + offset)
def printOrder(self):
- order = _objectOrderFinder(self, self._headOffsets + self._sizeOffsets, self._leftToRight, self._frontToBack, self._gantryHeight).order
- if order is None:
- print "ODD! Cannot find out proper printing order!!!"
- for obj in self._objectList:
- print obj.getPosition(), obj.getSize()
+ if self._oneAtATime:
+ order = _objectOrderFinder(self, self._leftToRight, self._frontToBack, self._gantryHeight).order
+ else:
+ order = None
return order
def _pushFree(self):
for a in self._objectList:
for b in self._objectList:
- if not self._checkHit(a, b):
+ if a == b or not self.checkPlatform(a) or not self.checkPlatform(b):
continue
- posDiff = a.getPosition() - b.getPosition()
- if posDiff[0] == 0.0 and posDiff[1] == 0.0:
- posDiff[1] = 1.0
- if abs(posDiff[0]) > abs(posDiff[1]):
- axis = 0
+ if self._oneAtATime:
+ v = polygon.polygonCollisionPushVector(a._headAreaMinHull + a.getPosition(), b._boundaryHull + b.getPosition())
else:
- axis = 1
- aPos = a.getPosition()
- bPos = b.getPosition()
- center = (aPos[axis] + bPos[axis]) / 2
- distance = (a.getSize()[axis] + b.getSize()[axis]) / 2 + 0.1 + self._sizeOffsets[axis] + self._headOffsets[axis]
- if posDiff[axis] < 0:
- distance = -distance
- aPos[axis] = center + distance / 2
- bPos[axis] = center - distance / 2
- a.setPosition(aPos)
- b.setPosition(bPos)
+ v = polygon.polygonCollisionPushVector(a._boundaryHull + a.getPosition(), b._boundaryHull + b.getPosition())
+ if type(v) is bool:
+ continue
+ a.setPosition(a.getPosition() + v * 0.4)
+ b.setPosition(b.getPosition() - v * 0.6)
return True
return False
+ #Check if two objects are hitting each-other (+ head space).
def _checkHit(self, a, b):
if a == b:
return False
- posDiff = a.getPosition() - b.getPosition()
- if abs(posDiff[0]) < (a.getSize()[0] + b.getSize()[0]) / 2 + self._sizeOffsets[0] + self._headOffsets[0]:
- if abs(posDiff[1]) < (a.getSize()[1] + b.getSize()[1]) / 2 + self._sizeOffsets[1] + self._headOffsets[1]:
- return True
- return False
+ if self._oneAtATime:
+ return polygon.polygonCollision(a._headAreaMinHull + a.getPosition(), b._boundaryHull + b.getPosition())
+ else:
+ return polygon.polygonCollision(a._boundaryHull + a.getPosition(), b._boundaryHull + b.getPosition())
def checkPlatform(self, obj):
- p = obj.getPosition()
- s = obj.getSize()[0:2] / 2 + self._sizeOffsets
- if p[0] - s[0] < -self._machineSize[0] / 2:
- return False
- if p[0] + s[0] > self._machineSize[0] / 2:
- return False
- if p[1] - s[1] < -self._machineSize[1] / 2:
- return False
- if p[1] + s[1] > self._machineSize[1] / 2:
+ area = obj._printAreaHull + obj.getPosition()
+ if not polygon.fullInside(area, self._machinePolygons[0]):
return False
+ #Check the "no go zones"
+ for poly in self._machinePolygons[1:]:
+ if polygon.polygonCollision(poly, area):
+ return False
return True
def _findFreePositionFor(self, obj):
posList = []
for a in self._objectList:
p = a.getPosition()
- s = (a.getSize()[0:2] + obj.getSize()[0:2]) / 2 + self._sizeOffsets + self._headOffsets
+ if self._oneAtATime:
+ s = (a.getSize()[0:2] + obj.getSize()[0:2]) / 2 + self._sizeOffsets + self._headSizeOffsets + numpy.array([3,3], numpy.float32)
+ else:
+ s = (a.getSize()[0:2] + obj.getSize()[0:2]) / 2 + numpy.array([3,3], numpy.float32)
posList.append(p + s * ( 1.0, 1.0))
posList.append(p + s * ( 0.0, 1.0))
posList.append(p + s * (-1.0, 1.0))