chiark / gitweb /
f6d83c7a726d822270cee5879fb86344dcc6df26
[cura.git] / Cura / util / mesh.py
1 from __future__ import absolute_import
2
3 import time
4 import math
5
6 import numpy
7 numpy.seterr(all='ignore')
8
9 class printableObject(object):
10         def __init__(self):
11                 self._meshList = []
12                 self._position = numpy.array([0.0, 0.0])
13                 self._matrix = numpy.matrix([[1,0,0],[0,1,0],[0,0,1]], numpy.float64)
14                 self._transformedMin = None
15                 self._transformedMax = None
16                 self._transformedSize = None
17                 self._boundaryCircleSize = None
18                 self._drawOffset = None
19                 self._loadAnim = None
20
21         def copy(self):
22                 ret = printableObject()
23                 ret._matrix = self._matrix.copy()
24                 ret._meshList = self._meshList[:]
25                 ret._transformedMin = self._transformedMin
26                 ret._transformedMax = self._transformedMin
27                 ret._transformedSize = self._transformedSize
28                 ret._boundaryCircleSize = self._boundaryCircleSize
29                 ret._drawOffset = self._drawOffset
30                 for m in ret._meshList:
31                         m.vbo.incRef()
32                 return ret
33
34         def _addMesh(self):
35                 m = mesh()
36                 self._meshList.append(m)
37                 return m
38
39         def _postProcessAfterLoad(self):
40                 for m in self._meshList:
41                         m._calculateNormals()
42                 self.processMatrix()
43
44         def applyMatrix(self, m):
45                 self._matrix *= m
46                 self.processMatrix()
47
48         def processMatrix(self):
49                 self._transformedMin = numpy.array([999999999999,999999999999,999999999999], numpy.float64)
50                 self._transformedMax = numpy.array([-999999999999,-999999999999,-999999999999], numpy.float64)
51                 self._boundaryCircleSize = 0
52
53                 for m in self._meshList:
54                         transformedVertexes = (numpy.matrix(m.vertexes, copy = False) * numpy.matrix(self._matrix, numpy.float32)).getA()
55                         transformedMin = transformedVertexes.min(0)
56                         transformedMax = transformedVertexes.max(0)
57                         for n in xrange(0, 3):
58                                 self._transformedMin[n] = min(transformedMin[n], self._transformedMin[n])
59                                 self._transformedMax[n] = max(transformedMax[n], self._transformedMax[n])
60
61                         #Calculate the boundary circle
62                         transformedSize = transformedMax - transformedMin
63                         center = transformedMin + transformedSize / 2.0
64                         boundaryCircleSize = round(math.sqrt(numpy.max(((transformedVertexes[::,0] - center[0]) * (transformedVertexes[::,0] - center[0])) + ((transformedVertexes[::,1] - center[1]) * (transformedVertexes[::,1] - center[1])) + ((transformedVertexes[::,2] - center[2]) * (transformedVertexes[::,2] - center[2])))), 3)
65                         self._boundaryCircleSize = max(self._boundaryCircleSize, boundaryCircleSize)
66                 self._transformedSize = self._transformedMax - self._transformedMin
67                 self._drawOffset = (self._transformedMax + self._transformedMin) / 2
68                 self._drawOffset[2] = self._transformedMin[2]
69                 self._transformedMax -= self._drawOffset
70                 self._transformedMin -= self._drawOffset
71
72         def getPosition(self):
73                 return self._position
74         def setPosition(self, newPos):
75                 self._position = newPos
76         def getMatrix(self):
77                 return self._matrix
78
79         def getMaximum(self):
80                 return self._transformedMax
81         def getMinimum(self):
82                 return self._transformedMin
83         def getSize(self):
84                 return self._transformedSize
85         def getDrawOffset(self):
86                 return self._drawOffset
87         def getBoundaryCircle(self):
88                 return self._boundaryCircleSize
89
90         def mirror(self, axis):
91                 matrix = [[1,0,0], [0, 1, 0], [0, 0, 1]]
92                 matrix[axis][axis] = -1
93                 self.applyMatrix(numpy.matrix(matrix, numpy.float64))
94
95         def getScale(self):
96                 return numpy.array([
97                         numpy.linalg.norm(self._matrix[::,0].getA().flatten()),
98                         numpy.linalg.norm(self._matrix[::,1].getA().flatten()),
99                         numpy.linalg.norm(self._matrix[::,2].getA().flatten())], numpy.float64);
100
101         def setScale(self, scale, axis, uniform):
102                 currentScale = numpy.linalg.norm(self._matrix[::,axis].getA().flatten())
103                 scale /= currentScale
104                 if scale == 0:
105                         return
106                 if uniform:
107                         matrix = [[scale,0,0], [0, scale, 0], [0, 0, scale]]
108                 else:
109                         matrix = [[1.0,0,0], [0, 1.0, 0], [0, 0, 1.0]]
110                         matrix[axis][axis] = scale
111                 self.applyMatrix(numpy.matrix(matrix, numpy.float64))
112
113         def setSize(self, size, axis, uniform):
114                 scale = self.getSize()[axis]
115                 scale = size / scale
116                 if scale == 0:
117                         return
118                 if uniform:
119                         matrix = [[scale,0,0], [0, scale, 0], [0, 0, scale]]
120                 else:
121                         matrix = [[1,0,0], [0, 1, 0], [0, 0, 1]]
122                         matrix[axis][axis] = scale
123                 self.applyMatrix(numpy.matrix(matrix, numpy.float64))
124
125         def resetScale(self):
126                 x = 1/numpy.linalg.norm(self._matrix[::,0].getA().flatten())
127                 y = 1/numpy.linalg.norm(self._matrix[::,1].getA().flatten())
128                 z = 1/numpy.linalg.norm(self._matrix[::,2].getA().flatten())
129                 self.applyMatrix(numpy.matrix([[x,0,0],[0,y,0],[0,0,z]], numpy.float64))
130
131         def resetRotation(self):
132                 x = numpy.linalg.norm(self._matrix[::,0].getA().flatten())
133                 y = numpy.linalg.norm(self._matrix[::,1].getA().flatten())
134                 z = numpy.linalg.norm(self._matrix[::,2].getA().flatten())
135                 self._matrix = numpy.matrix([[x,0,0],[0,y,0],[0,0,z]], numpy.float64)
136                 self.processMatrix()
137
138         def layFlat(self):
139                 transformedVertexes = (numpy.matrix(self._meshList[0].vertexes, copy = False) * self._matrix).getA()
140                 minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
141                 dotMin = 1.0
142                 dotV = None
143                 for v in transformedVertexes:
144                         diff = v - minZvertex
145                         len = math.sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2])
146                         if len < 5:
147                                 continue
148                         dot = (diff[2] / len)
149                         if dotMin > dot:
150                                 dotMin = dot
151                                 dotV = diff
152                 if dotV is None:
153                         return
154                 rad = -math.atan2(dotV[1], dotV[0])
155                 self._matrix *= numpy.matrix([[math.cos(rad), math.sin(rad), 0], [-math.sin(rad), math.cos(rad), 0], [0,0,1]], numpy.float64)
156                 rad = -math.asin(dotMin)
157                 self._matrix *= numpy.matrix([[math.cos(rad), 0, math.sin(rad)], [0,1,0], [-math.sin(rad), 0, math.cos(rad)]], numpy.float64)
158
159
160                 transformedVertexes = (numpy.matrix(self._meshList[0].vertexes, copy = False) * self._matrix).getA()
161                 minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
162                 dotMin = 1.0
163                 dotV = None
164                 for v in transformedVertexes:
165                         diff = v - minZvertex
166                         len = math.sqrt(diff[1] * diff[1] + diff[2] * diff[2])
167                         if len < 5:
168                                 continue
169                         dot = (diff[2] / len)
170                         if dotMin > dot:
171                                 dotMin = dot
172                                 dotV = diff
173                 if dotV is None:
174                         return
175                 if dotV[1] < 0:
176                         rad = math.asin(dotMin)
177                 else:
178                         rad = -math.asin(dotMin)
179                 self.applyMatrix(numpy.matrix([[1,0,0], [0, math.cos(rad), math.sin(rad)], [0, -math.sin(rad), math.cos(rad)]], numpy.float64))
180
181         def scaleUpTo(self, size):
182                 vMin = self._transformedMin
183                 vMax = self._transformedMax
184
185                 scaleX1 = (size[0] / 2 - self._position[0]) / ((vMax[0] - vMin[0]) / 2)
186                 scaleY1 = (size[1] / 2 - self._position[1]) / ((vMax[1] - vMin[1]) / 2)
187                 scaleX2 = (self._position[0] + size[0] / 2) / ((vMax[0] - vMin[0]) / 2)
188                 scaleY2 = (self._position[1] + size[1] / 2) / ((vMax[1] - vMin[1]) / 2)
189                 scaleZ = size[2] / (vMax[2] - vMin[2])
190                 print scaleX1, scaleY1, scaleX2, scaleY2, scaleZ
191                 scale = min(scaleX1, scaleY1, scaleX2, scaleY2, scaleZ)
192                 if scale > 0:
193                         self.applyMatrix(numpy.matrix([[scale,0,0],[0,scale,0],[0,0,scale]], numpy.float64))
194
195         def split(self):
196                 ret = []
197                 for oriMesh in self._meshList:
198                         for m in oriMesh.split():
199                                 obj = printableObject()
200                                 obj._meshList.append(m)
201                                 obj._postProcessAfterLoad()
202                                 ret.append(obj)
203                 return ret
204
205 class mesh(object):
206         def __init__(self):
207                 self.vertexes = None
208                 self.vertexCount = 0
209                 self.vbo = None
210
211         def _addFace(self, x0, y0, z0, x1, y1, z1, x2, y2, z2):
212                 n = self.vertexCount
213                 self.vertexes[n][0] = x0
214                 self.vertexes[n][1] = y0
215                 self.vertexes[n][2] = z0
216                 n += 1
217                 self.vertexes[n][0] = x1
218                 self.vertexes[n][1] = y1
219                 self.vertexes[n][2] = z1
220                 n += 1
221                 self.vertexes[n][0] = x2
222                 self.vertexes[n][1] = y2
223                 self.vertexes[n][2] = z2
224                 self.vertexCount += 3
225         
226         def _prepareFaceCount(self, faceNumber):
227                 #Set the amount of faces before loading data in them. This way we can create the numpy arrays before we fill them.
228                 self.vertexes = numpy.zeros((faceNumber*3, 3), numpy.float32)
229                 self.normal = numpy.zeros((faceNumber*3, 3), numpy.float32)
230                 self.vertexCount = 0
231
232         def _calculateNormals(self):
233                 #Calculate the normals
234                 tris = self.vertexes.reshape(self.vertexCount / 3, 3, 3)
235                 normals = numpy.cross( tris[::,1 ] - tris[::,0]  , tris[::,2 ] - tris[::,0] )
236                 lens = numpy.sqrt( normals[:,0]**2 + normals[:,1]**2 + normals[:,2]**2 )
237                 normals[:,0] /= lens
238                 normals[:,1] /= lens
239                 normals[:,2] /= lens
240                 
241                 n = numpy.zeros((self.vertexCount / 3, 9), numpy.float32)
242                 n[:,0:3] = normals
243                 n[:,3:6] = normals
244                 n[:,6:9] = normals
245                 self.normal = n.reshape(self.vertexCount, 3)
246                 self.invNormal = -self.normal
247
248         def _vertexHash(self, idx):
249                 v = self.vertexes[idx]
250                 return int(v[0] * 100) | int(v[1] * 100) << 8 | int(v[2] * 100) << 16
251
252         def _idxFromHash(self, map, idx):
253                 vHash = self._vertexHash(idx)
254                 for i in map[vHash]:
255                         if numpy.linalg.norm(self.vertexes[i] - self.vertexes[idx]) < 0.001:
256                                 return i
257
258         def split(self):
259                 vertexMap = {}
260
261                 vertexToFace = []
262                 for idx in xrange(0, self.vertexCount):
263                         print idx, self.vertexCount
264                         vHash = self._vertexHash(idx)
265                         if vHash not in vertexMap:
266                                 vertexMap[vHash] = []
267                         vertexMap[vHash].append(idx)
268                         vertexToFace.append([])
269
270                 faceList = []
271                 for idx in xrange(0, self.vertexCount, 3):
272                         print idx, self.vertexCount
273                         f = [self._idxFromHash(vertexMap, idx), self._idxFromHash(vertexMap, idx+1), self._idxFromHash(vertexMap, idx+2)]
274                         vertexToFace[f[0]].append(idx / 3)
275                         vertexToFace[f[1]].append(idx / 3)
276                         vertexToFace[f[2]].append(idx / 3)
277                         faceList.append(f)
278
279                 ret = []
280                 doneSet = set()
281                 for idx in xrange(0, len(faceList)):
282                         if idx in doneSet:
283                                 continue
284                         doneSet.add(idx)
285                         todoList = [idx]
286                         meshFaceList = []
287                         while len(todoList) > 0:
288                                 idx = todoList.pop()
289                                 meshFaceList.append(idx)
290                                 for n in xrange(0, 3):
291                                         for i in vertexToFace[faceList[idx][n]]:
292                                                 if not i in doneSet:
293                                                         doneSet.add(i)
294                                                         todoList.append(i)
295
296                         m = mesh()
297                         m._prepareFaceCount(len(meshFaceList))
298                         for idx in meshFaceList:
299                                 m.vertexes[m.vertexCount] = self.vertexes[faceList[idx][0]]
300                                 m.vertexCount += 1
301                                 m.vertexes[m.vertexCount] = self.vertexes[faceList[idx][1]]
302                                 m.vertexCount += 1
303                                 m.vertexes[m.vertexCount] = self.vertexes[faceList[idx][2]]
304                                 m.vertexCount += 1
305                         ret.append(m)
306                 return ret