X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=Cura%2Futil%2FobjectScene.py;h=b229a70923edc67a640ad0298f224c3754245b80;hb=e24262f503b7deb1d779dd3be7ef9edb14927a53;hp=c2944e2580d4f33f6687af49b9b143d4d4e30269;hpb=dd971fcef39a1ebaa9b26add9be485b1d84f43c6;p=cura.git diff --git a/Cura/util/objectScene.py b/Cura/util/objectScene.py index c2944e25..b229a709 100644 --- a/Cura/util/objectScene.py +++ b/Cura/util/objectScene.py @@ -1,15 +1,18 @@ +__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 @@ -17,14 +20,37 @@ class _objectOrderFinder(object): 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] @@ -35,80 +61,115 @@ class _objectOrderFinder(object): 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: @@ -136,65 +197,55 @@ class Scene(object): 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))