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