1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
5 from Cura.util import profile
6 from Cura.util import polygon
8 class _objectOrder(object):
9 def __init__(self, order, todo):
13 class _objectOrderFinder(object):
14 def __init__(self, scene, offset, leftToRight, frontToBack, gantryHeight):
16 self._offset = offset - numpy.array([0.1,0.1])
17 self._objs = scene.objects()
18 self._leftToRight = leftToRight
19 self._frontToBack = frontToBack
21 for n in xrange(0, len(self._objs)):
22 if scene.checkPlatform(self._objs[n]):
25 if self._objs[n].getSize()[2] > gantryHeight and len(initialList) > 1:
28 if len(initialList) == 0:
33 self._hitMap = [None] * (max(initialList)+1)
35 self._hitMap[a] = [False] * (max(initialList)+1)
37 self._hitMap[a][b] = self._checkHit(a, b)
39 initialList.sort(self._objIdxCmp)
42 self._todo = [_objectOrder([], initialList)]
43 while len(self._todo) > 0:
45 current = self._todo.pop()
46 #print len(self._todo), len(current.order), len(initialList), current.order
47 for addIdx in current.todo:
48 if not self._checkHitFor(addIdx, current.order) and not self._checkBlocks(addIdx, current.todo):
49 todoList = current.todo[:]
50 todoList.remove(addIdx)
51 order = current.order[:] + [addIdx]
52 if len(todoList) == 0:
56 self._todo.append(_objectOrder(order, todoList))
59 def _objIdxCmp(self, a, b):
60 scoreA = sum(self._hitMap[a])
61 scoreB = sum(self._hitMap[b])
62 return scoreA - scoreB
64 def _checkHitFor(self, addIdx, others):
66 if self._hitMap[addIdx][idx]:
70 def _checkBlocks(self, addIdx, others):
72 if addIdx != idx and self._hitMap[idx][addIdx]:
76 #Check if printing one object will cause printhead colission with other object.
77 def _checkHit(self, addIdx, idx):
78 obj = self._scene._objectList[idx]
79 addObj = self._scene._objectList[addIdx]
80 return polygon.polygonCollision(obj._headAreaHull + obj.getPosition(), addObj._boundaryHull + addObj.getPosition())
85 self._sizeOffsets = numpy.array([0.0,0.0], numpy.float32)
86 self._machineSize = numpy.array([100,100,100], numpy.float32)
87 self._headSizeOffsets = numpy.array([18.0,18.0], numpy.float32)
88 self._extruderOffset = [numpy.array([0,0], numpy.float32)] * 4
90 #Print order variables
91 self._leftToRight = False
92 self._frontToBack = True
93 self._gantryHeight = 60
94 self._oneAtATime = True
96 # Physical (square) machine size.
97 def setMachineSize(self, machineSize):
98 self._machineSize = machineSize
100 # Size offsets are offsets caused by brim, skirt, etc.
101 def updateSizeOffsets(self, force=False):
102 newOffsets = numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32)
103 if not force and numpy.array_equal(self._sizeOffsets, newOffsets):
105 self._sizeOffsets = newOffsets
107 extends = numpy.array([[-newOffsets[0],-newOffsets[1]],[ newOffsets[0],-newOffsets[1]],[ newOffsets[0], newOffsets[1]],[-newOffsets[0], newOffsets[1]]], numpy.float32)
108 for obj in self._objectList:
109 obj.setPrintAreaExtends(extends)
111 #size of the printing head.
112 def setHeadSize(self, xMin, xMax, yMin, yMax, gantryHeight):
113 self._leftToRight = xMin < xMax
114 self._frontToBack = yMin < yMax
115 self._headSizeOffsets[0] = min(xMin, xMax)
116 self._headSizeOffsets[1] = min(yMin, yMax)
117 self._gantryHeight = gantryHeight
118 self._oneAtATime = self._gantryHeight > 0
120 headArea = numpy.array([[-xMin,-yMin],[ xMax,-yMin],[ xMax, yMax],[-xMin, yMax]], numpy.float32)
121 for obj in self._objectList:
122 obj.setHeadArea(headArea)
124 def setExtruderOffset(self, extruderNr, offsetX, offsetY):
125 self._extruderOffset[extruderNr] = numpy.array([offsetX, offsetY], numpy.float32)
127 def getObjectExtend(self):
128 return self._sizeOffsets + self._headSizeOffsets
131 return self._objectList
133 #Add new object to print area
135 self._findFreePositionFor(obj)
136 self._objectList.append(obj)
138 if numpy.max(obj.getSize()[0:2]) > numpy.max(self._machineSize[0:2]) * 2.5:
139 scale = numpy.max(self._machineSize[0:2]) * 2.5 / numpy.max(obj.getSize()[0:2])
140 matrix = [[scale,0,0], [0, scale, 0], [0, 0, scale]]
141 obj.applyMatrix(numpy.matrix(matrix, numpy.float64))
142 self.updateSizeOffsets(True)
144 def remove(self, obj):
145 self._objectList.remove(obj)
147 #Dual(multiple) extrusion merge
148 def merge(self, obj1, obj2):
150 obj1._meshList += obj2._meshList
151 for m in obj2._meshList:
154 obj1.setPosition((obj1.getPosition() + obj2.getPosition()) / 2)
160 while self._pushFree():
165 def arrangeAll(self):
166 oldList = self._objectList
167 self._objectList = []
169 obj.setPosition(numpy.array([0,0], numpy.float32))
173 minPos = numpy.array([9999999,9999999], numpy.float32)
174 maxPos = numpy.array([-9999999,-9999999], numpy.float32)
175 for obj in self._objectList:
176 pos = obj.getPosition()
178 minPos[0] = min(minPos[0], pos[0] - size[0] / 2)
179 minPos[1] = min(minPos[1], pos[1] - size[1] / 2)
180 maxPos[0] = max(maxPos[0], pos[0] + size[0] / 2)
181 maxPos[1] = max(maxPos[1], pos[1] + size[1] / 2)
182 offset = -(maxPos + minPos) / 2
183 for obj in self._objectList:
184 obj.setPosition(obj.getPosition() + offset)
186 def printOrder(self):
188 order = _objectOrderFinder(self, self._headSizeOffsets + self._sizeOffsets, self._leftToRight, self._frontToBack, self._gantryHeight).order
194 for a in self._objectList:
195 for b in self._objectList:
196 if not self._checkHit(a, b):
198 posDiff = a.getPosition() - b.getPosition()
199 if posDiff[0] == 0.0 and posDiff[1] == 0.0:
201 if abs(posDiff[0]) > abs(posDiff[1]):
205 aPos = a.getPosition()
206 bPos = b.getPosition()
207 center = (aPos[axis] + bPos[axis]) / 2
208 distance = (a.getSize()[axis] + b.getSize()[axis]) / 2 + 0.1 + self._sizeOffsets[axis] + self._headSizeOffsets[axis]
209 if posDiff[axis] < 0:
211 aPos[axis] = center + distance / 2
212 bPos[axis] = center - distance / 2
218 #Check if two objects are hitting each-other (+ head space).
219 def _checkHit(self, a, b):
223 return polygon.polygonCollision(a._headAreaHull + a.getPosition(), b._boundaryHull + b.getPosition())
225 return polygon.polygonCollision(a._boundaryHull + a.getPosition(), b._boundaryHull + b.getPosition())
227 def checkPlatform(self, obj):
228 p = obj.getPosition()
229 s = obj.getSize()[0:2] / 2 + self._sizeOffsets
234 extruderCount = len(obj._meshList)
235 if profile.getProfileSetting('support_dual_extrusion') == 'Second extruder' and profile.getProfileSetting('support') != 'None':
236 extruderCount = max(extruderCount, 2)
237 for n in xrange(1, extruderCount):
238 if offsetLeft < self._extruderOffset[n][0]:
239 offsetLeft = self._extruderOffset[n][0]
240 if offsetRight > self._extruderOffset[n][0]:
241 offsetRight = self._extruderOffset[n][0]
242 if offsetFront < self._extruderOffset[n][1]:
243 offsetFront = self._extruderOffset[n][1]
244 if offsetBack > self._extruderOffset[n][1]:
245 offsetBack = self._extruderOffset[n][1]
246 boundLeft = -self._machineSize[0] / 2 + offsetLeft
247 boundRight = self._machineSize[0] / 2 + offsetRight
248 boundFront = -self._machineSize[1] / 2 + offsetFront
249 boundBack = self._machineSize[1] / 2 + offsetBack
250 if p[0] - s[0] < boundLeft:
252 if p[0] + s[0] > boundRight:
254 if p[1] - s[1] < boundFront:
256 if p[1] + s[1] > boundBack:
259 #Do clip Check for UM2.
260 machine = profile.getMachineSetting('machine_type')
261 if machine == "ultimaker2":
262 #lowerRight clip check
263 if p[0] - s[0] < boundLeft + 25 and p[1] - s[1] < boundFront + 10:
266 if p[0] - s[0] < boundLeft + 25 and p[1] + s[1] > boundBack - 10:
269 if p[0] + s[0] > boundRight - 25 and p[1] - s[1] < boundFront + 10:
272 if p[0] + s[0] > boundRight - 25 and p[1] + s[1] > boundBack - 10:
276 def _findFreePositionFor(self, obj):
278 for a in self._objectList:
280 s = (a.getSize()[0:2] + obj.getSize()[0:2]) / 2 + self._sizeOffsets + self._headSizeOffsets
281 posList.append(p + s * ( 1.0, 1.0))
282 posList.append(p + s * ( 0.0, 1.0))
283 posList.append(p + s * (-1.0, 1.0))
284 posList.append(p + s * ( 1.0, 0.0))
285 posList.append(p + s * (-1.0, 0.0))
286 posList.append(p + s * ( 1.0,-1.0))
287 posList.append(p + s * ( 0.0,-1.0))
288 posList.append(p + s * (-1.0,-1.0))
295 for a in self._objectList:
296 if self._checkHit(a, obj):
301 dist = numpy.linalg.norm(p)
302 if not self.checkPlatform(obj):
304 if best is None or dist < bestDist:
308 obj.setPosition(best)