chiark / gitweb /
Merge branch 'master' into SteamEngine
[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):
11                 self._scene = scene
12                 self._offset = offset - numpy.array([0.1,0.1])
13                 self._objs = scene.objects()
14                 self._leftToRight = True
15                 self._frontToBack = True
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                 self._todo = [_objectOrder([], initialList)]
24                 while len(self._todo) > 0:
25                         current = self._todo.pop()
26                         for addIdx in current.todo:
27                                 if not self._checkHitFor(addIdx, current.order):
28                                         todoList = current.todo[:]
29                                         todoList.remove(addIdx)
30                                         order = current.order[:] + [addIdx]
31                                         if len(todoList) == 0:
32                                                 self._todo = None
33                                                 self.order = order
34                                                 return
35                                         self._todo.append(_objectOrder(order, todoList))
36                 self.order = None
37
38         def _checkHitFor(self, addIdx, others):
39                 for idx in others:
40                         if self._checkHit(addIdx, idx):
41                                 return True
42                 return False
43
44         def _checkHit(self, addIdx, idx):
45                 addPos = self._scene._objectList[addIdx].getPosition()
46                 addSize = self._scene._objectList[addIdx].getSize()
47                 pos = self._scene._objectList[idx].getPosition()
48                 size = self._scene._objectList[idx].getSize()
49
50                 if self._leftToRight:
51                         if addPos[0] + addSize[0] / 2 + self._offset[0] <= pos[0] - size[0] / 2:
52                                 return False
53                 else:
54                         if addPos[0] - addSize[0] / 2 - self._offset[0] <= pos[0] + size[0] / 2:
55                                 return False
56
57                 if self._frontToBack:
58                         if addPos[1] - addSize[1] / 2 - self._offset[1] >= pos[1] + size[1] / 2:
59                                 return False
60                 else:
61                         if addPos[1] + addSize[1] / 2 + self._offset[1] >= pos[1] - size[1] / 2:
62                                 return False
63
64                 return True
65
66 class Scene(object):
67         def __init__(self):
68                 self._objectList = []
69                 self._sizeOffsets = numpy.array([0.0,0.0], numpy.float32)
70                 self._machineSize = numpy.array([100,100,100], numpy.float32)
71                 self._headOffsets = numpy.array([18.0,18.0], numpy.float32)
72
73         def setMachineSize(self, machineSize):
74                 self._machineSize = machineSize
75
76         def setSizeOffsets(self, sizeOffsets):
77                 self._sizeOffsets = sizeOffsets
78
79         def getObjectExtend(self):
80                 return self._sizeOffsets + self._headOffsets
81
82         def objects(self):
83                 return self._objectList
84
85         def add(self, obj):
86                 self._findFreePositionFor(obj)
87                 self._objectList.append(obj)
88                 self.pushFree()
89
90         def remove(self, obj):
91                 self._objectList.remove(obj)
92
93         def merge(self, obj1, obj2):
94                 self.remove(obj2)
95                 obj1._meshList += obj2._meshList
96                 obj1.processMatrix()
97                 obj1.setPosition((obj1.getPosition() + obj2.getPosition()) / 2)
98                 self.pushFree()
99
100         def pushFree(self):
101                 n = 1000
102                 while self._pushFree():
103                         n -= 1
104                         if n < 0:
105                                 return
106
107         def arrangeAll(self):
108                 oldList = self._objectList
109                 self._objectList = []
110                 for obj in oldList:
111                         obj.setPosition(numpy.array([0,0], numpy.float32))
112                         self.add(obj)
113
114         def centerAll(self):
115                 minPos = numpy.array([9999999,9999999], numpy.float32)
116                 maxPos = numpy.array([-9999999,-9999999], numpy.float32)
117                 for obj in self._objectList:
118                         pos = obj.getPosition()
119                         size = obj.getSize()
120                         minPos[0] = min(minPos[0], pos[0] - size[0] / 2)
121                         minPos[1] = min(minPos[1], pos[1] - size[1] / 2)
122                         maxPos[0] = max(maxPos[0], pos[0] + size[0] / 2)
123                         maxPos[1] = max(maxPos[1], pos[1] + size[1] / 2)
124                 offset = -(maxPos + minPos) / 2
125                 for obj in self._objectList:
126                         obj.setPosition(obj.getPosition() + offset)
127
128         def printOrder(self):
129                 order = _objectOrderFinder(self, self._headOffsets + self._sizeOffsets).order
130                 if order is None:
131                         print "ODD! Cannot find out proper printing order!!!"
132                         for obj in self._objectList:
133                                 print obj.getPosition(), obj.getSize()
134                 return order
135
136         def _pushFree(self):
137                 for a in self._objectList:
138                         for b in self._objectList:
139                                 if not self._checkHit(a, b):
140                                         continue
141                                 posDiff = a.getPosition() - b.getPosition()
142                                 if posDiff[0] == 0.0 and posDiff[1] == 0.0:
143                                         posDiff[1] = 1.0
144                                 if abs(posDiff[0]) > abs(posDiff[1]):
145                                         axis = 0
146                                 else:
147                                         axis = 1
148                                 aPos = a.getPosition()
149                                 bPos = b.getPosition()
150                                 center = (aPos[axis] + bPos[axis]) / 2
151                                 distance = (a.getSize()[axis] + b.getSize()[axis]) / 2 + 0.1 + self._sizeOffsets[axis] + self._headOffsets[axis]
152                                 if posDiff[axis] < 0:
153                                         distance = -distance
154                                 aPos[axis] = center + distance / 2
155                                 bPos[axis] = center - distance / 2
156                                 a.setPosition(aPos)
157                                 b.setPosition(bPos)
158                                 return True
159                 return False
160
161         def _checkHit(self, a, b):
162                 if a == b:
163                         return False
164                 posDiff = a.getPosition() - b.getPosition()
165                 if abs(posDiff[0]) < (a.getSize()[0] + b.getSize()[0]) / 2 + self._sizeOffsets[0] + self._headOffsets[0]:
166                         if abs(posDiff[1]) < (a.getSize()[1] + b.getSize()[1]) / 2 + self._sizeOffsets[1] + self._headOffsets[1]:
167                                 return True
168                 return False
169
170         def checkPlatform(self, obj):
171                 p = obj.getPosition()
172                 s = obj.getSize()[0:2] / 2 + self._sizeOffsets
173                 if p[0] - s[0] < -self._machineSize[0] / 2:
174                         return False
175                 if p[0] + s[0] > self._machineSize[0] / 2:
176                         return False
177                 if p[1] - s[1] < -self._machineSize[1] / 2:
178                         return False
179                 if p[1] + s[1] > self._machineSize[1] / 2:
180                         return False
181                 return True
182
183         def _findFreePositionFor(self, obj):
184                 posList = []
185                 for a in self._objectList:
186                         p = a.getPosition()
187                         s = (a.getSize()[0:2] + obj.getSize()[0:2]) / 2 + self._sizeOffsets + self._headOffsets
188                         posList.append(p + s * ( 1.0, 1.0))
189                         posList.append(p + s * ( 0.0, 1.0))
190                         posList.append(p + s * (-1.0, 1.0))
191                         posList.append(p + s * ( 1.0, 0.0))
192                         posList.append(p + s * (-1.0, 0.0))
193                         posList.append(p + s * ( 1.0,-1.0))
194                         posList.append(p + s * ( 0.0,-1.0))
195                         posList.append(p + s * (-1.0,-1.0))
196
197                 best = None
198                 bestDist = None
199                 for p in posList:
200                         obj.setPosition(p)
201                         ok = True
202                         for a in self._objectList:
203                                 if self._checkHit(a, obj):
204                                         ok = False
205                                         break
206                         if not ok:
207                                 continue
208                         dist = numpy.linalg.norm(p)
209                         if not self.checkPlatform(obj):
210                                 dist *= 3
211                         if best is None or dist < bestDist:
212                                 best = p
213                                 bestDist = dist
214                 if best is not None:
215                         obj.setPosition(best)