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