chiark / gitweb /
Fix the bug where the M140 for bed temperature is not added in the start code.
[cura.git] / Cura / util / objectScene.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2 import random
3 import numpy
4
5 class _objectOrder(object):
6         def __init__(self, order, todo):
7                 self.order = order
8                 self.todo = todo
9
10 class _objectOrderFinder(object):
11         def __init__(self, scene, offset, leftToRight, frontToBack, gantryHeight):
12                 self._scene = scene
13                 self._offset = offset - numpy.array([0.1,0.1])
14                 self._objs = scene.objects()
15                 self._leftToRight = leftToRight
16                 self._frontToBack = frontToBack
17                 initialList = []
18                 for n in xrange(0, len(self._objs)):
19                         if scene.checkPlatform(self._objs[n]):
20                                 initialList.append(n)
21                 for n in initialList:
22                         if self._objs[n].getSize()[2] > gantryHeight and len(initialList) > 1:
23                                 self.order = None
24                                 return
25                 if len(initialList) == 0:
26                         self.order = []
27                         return
28
29
30                 self._hitMap = [None] * (max(initialList)+1)
31                 for a in initialList:
32                         self._hitMap[a] = [False] * (max(initialList)+1)
33                         for b in initialList:
34                                 self._hitMap[a][b] = self._checkHit(a, b)
35
36                 initialList.sort(self._objIdxCmp)
37
38                 n = 0
39                 self._todo = [_objectOrder([], initialList)]
40                 while len(self._todo) > 0:
41                         n += 1
42                         current = self._todo.pop()
43                         #print len(self._todo), len(current.order), len(initialList), current.order
44                         for addIdx in current.todo:
45                                 if not self._checkHitFor(addIdx, current.order) and not self._checkBlocks(addIdx, current.todo):
46                                         todoList = current.todo[:]
47                                         todoList.remove(addIdx)
48                                         order = current.order[:] + [addIdx]
49                                         if len(todoList) == 0:
50                                                 self._todo = None
51                                                 self.order = order
52                                                 return
53                                         self._todo.append(_objectOrder(order, todoList))
54                 self.order = None
55
56         def _objIdxCmp(self, a, b):
57                 scoreA = sum(self._hitMap[a])
58                 scoreB = sum(self._hitMap[b])
59                 return scoreA - scoreB
60
61         def _checkHitFor(self, addIdx, others):
62                 for idx in others:
63                         if self._hitMap[addIdx][idx]:
64                                 return True
65                 return False
66
67         def _checkBlocks(self, addIdx, others):
68                 for idx in others:
69                         if addIdx != idx and self._hitMap[idx][addIdx]:
70                                 return True
71                 return False
72
73         def _checkHit(self, addIdx, idx):
74                 addPos = self._scene._objectList[addIdx].getPosition()
75                 addSize = self._scene._objectList[addIdx].getSize()
76                 pos = self._scene._objectList[idx].getPosition()
77                 size = self._scene._objectList[idx].getSize()
78
79                 if self._leftToRight:
80                         if addPos[0] - addSize[0] / 2 - self._offset[0] >= pos[0] + size[0] / 2:
81                                 return False
82                 else:
83                         if addPos[0] + addSize[0] / 2 + self._offset[0] <= pos[0] - size[0] / 2:
84                                 return False
85
86                 if self._frontToBack:
87                         if addPos[1] - addSize[1] / 2 - self._offset[1] >= pos[1] + size[1] / 2:
88                                 return False
89                 else:
90                         if addPos[1] + addSize[1] / 2 + self._offset[1] <= pos[1] - size[1] / 2:
91                                 return False
92
93                 return True
94
95 class Scene(object):
96         def __init__(self):
97                 self._objectList = []
98                 self._sizeOffsets = numpy.array([0.0,0.0], numpy.float32)
99                 self._machineSize = numpy.array([100,100,100], numpy.float32)
100                 self._headOffsets = numpy.array([18.0,18.0], numpy.float32)
101                 self._leftToRight = False
102                 self._frontToBack = True
103                 self._gantryHeight = 60
104
105         def setMachineSize(self, machineSize):
106                 self._machineSize = machineSize
107
108         def setSizeOffsets(self, sizeOffsets):
109                 self._sizeOffsets = sizeOffsets
110
111         def setHeadSize(self, xMin, xMax, yMin, yMax, gantryHeight):
112                 self._leftToRight = xMin < xMax
113                 self._frontToBack = yMin < yMax
114                 self._headOffsets[0] = min(xMin, xMax)
115                 self._headOffsets[1] = min(yMin, yMax)
116                 self._gantryHeight = gantryHeight
117
118         def getObjectExtend(self):
119                 return self._sizeOffsets + self._headOffsets
120
121         def objects(self):
122                 return self._objectList
123
124         def add(self, obj):
125                 self._findFreePositionFor(obj)
126                 self._objectList.append(obj)
127                 self.pushFree()
128                 if numpy.max(obj.getSize()[0:2]) > numpy.max(self._machineSize[0:2]) * 2.5:
129                         scale = numpy.max(self._machineSize[0:2]) * 2.5 / numpy.max(obj.getSize()[0:2])
130                         matrix = [[scale,0,0], [0, scale, 0], [0, 0, scale]]
131                         obj.applyMatrix(numpy.matrix(matrix, numpy.float64))
132
133         def remove(self, obj):
134                 self._objectList.remove(obj)
135
136         def merge(self, obj1, obj2):
137                 self.remove(obj2)
138                 obj1._meshList += obj2._meshList
139                 for m in obj2._meshList:
140                         m._obj = obj1
141                 obj1.processMatrix()
142                 obj1.setPosition((obj1.getPosition() + obj2.getPosition()) / 2)
143                 self.pushFree()
144
145         def pushFree(self):
146                 n = 1000
147                 while self._pushFree():
148                         n -= 1
149                         if n < 0:
150                                 return
151
152         def arrangeAll(self):
153                 oldList = self._objectList
154                 self._objectList = []
155                 for obj in oldList:
156                         obj.setPosition(numpy.array([0,0], numpy.float32))
157                         self.add(obj)
158
159         def centerAll(self):
160                 minPos = numpy.array([9999999,9999999], numpy.float32)
161                 maxPos = numpy.array([-9999999,-9999999], numpy.float32)
162                 for obj in self._objectList:
163                         pos = obj.getPosition()
164                         size = obj.getSize()
165                         minPos[0] = min(minPos[0], pos[0] - size[0] / 2)
166                         minPos[1] = min(minPos[1], pos[1] - size[1] / 2)
167                         maxPos[0] = max(maxPos[0], pos[0] + size[0] / 2)
168                         maxPos[1] = max(maxPos[1], pos[1] + size[1] / 2)
169                 offset = -(maxPos + minPos) / 2
170                 for obj in self._objectList:
171                         obj.setPosition(obj.getPosition() + offset)
172
173         def printOrder(self):
174                 order = _objectOrderFinder(self, self._headOffsets + self._sizeOffsets, self._leftToRight, self._frontToBack, self._gantryHeight).order
175                 return order
176
177         def _pushFree(self):
178                 for a in self._objectList:
179                         for b in self._objectList:
180                                 if not self._checkHit(a, b):
181                                         continue
182                                 posDiff = a.getPosition() - b.getPosition()
183                                 if posDiff[0] == 0.0 and posDiff[1] == 0.0:
184                                         posDiff[1] = 1.0
185                                 if abs(posDiff[0]) > abs(posDiff[1]):
186                                         axis = 0
187                                 else:
188                                         axis = 1
189                                 aPos = a.getPosition()
190                                 bPos = b.getPosition()
191                                 center = (aPos[axis] + bPos[axis]) / 2
192                                 distance = (a.getSize()[axis] + b.getSize()[axis]) / 2 + 0.1 + self._sizeOffsets[axis] + self._headOffsets[axis]
193                                 if posDiff[axis] < 0:
194                                         distance = -distance
195                                 aPos[axis] = center + distance / 2
196                                 bPos[axis] = center - distance / 2
197                                 a.setPosition(aPos)
198                                 b.setPosition(bPos)
199                                 return True
200                 return False
201
202         def _checkHit(self, a, b):
203                 if a == b:
204                         return False
205                 posDiff = a.getPosition() - b.getPosition()
206                 if abs(posDiff[0]) < (a.getSize()[0] + b.getSize()[0]) / 2 + self._sizeOffsets[0] + self._headOffsets[0]:
207                         if abs(posDiff[1]) < (a.getSize()[1] + b.getSize()[1]) / 2 + self._sizeOffsets[1] + self._headOffsets[1]:
208                                 return True
209                 return False
210
211         def checkPlatform(self, obj):
212                 p = obj.getPosition()
213                 s = obj.getSize()[0:2] / 2 + self._sizeOffsets
214                 if p[0] - s[0] < -self._machineSize[0] / 2:
215                         return False
216                 if p[0] + s[0] > self._machineSize[0] / 2:
217                         return False
218                 if p[1] - s[1] < -self._machineSize[1] / 2:
219                         return False
220                 if p[1] + s[1] > self._machineSize[1] / 2:
221                         return False
222                 return True
223
224         def _findFreePositionFor(self, obj):
225                 posList = []
226                 for a in self._objectList:
227                         p = a.getPosition()
228                         s = (a.getSize()[0:2] + obj.getSize()[0:2]) / 2 + self._sizeOffsets + self._headOffsets
229                         posList.append(p + s * ( 1.0, 1.0))
230                         posList.append(p + s * ( 0.0, 1.0))
231                         posList.append(p + s * (-1.0, 1.0))
232                         posList.append(p + s * ( 1.0, 0.0))
233                         posList.append(p + s * (-1.0, 0.0))
234                         posList.append(p + s * ( 1.0,-1.0))
235                         posList.append(p + s * ( 0.0,-1.0))
236                         posList.append(p + s * (-1.0,-1.0))
237
238                 best = None
239                 bestDist = None
240                 for p in posList:
241                         obj.setPosition(p)
242                         ok = True
243                         for a in self._objectList:
244                                 if self._checkHit(a, obj):
245                                         ok = False
246                                         break
247                         if not ok:
248                                 continue
249                         dist = numpy.linalg.norm(p)
250                         if not self.checkPlatform(obj):
251                                 dist *= 3
252                         if best is None or dist < bestDist:
253                                 best = p
254                                 bestDist = dist
255                 if best is not None:
256                         obj.setPosition(best)