chiark / gitweb /
0ed57db21fb055a2ed82cfebe66cce5f4c5980b7
[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 from Cura.util import util3d
10
11 class printableObject(object):
12         def __init__(self):
13                 self._meshList = []
14                 self._position = numpy.array([0.0, 0.0])
15                 self._matrix = numpy.matrix([[1,0,0],[0,1,0],[0,0,1]], numpy.float64)
16                 self._transformedMin = None
17                 self._transformedMax = None
18                 self._boundaryCircleSize = None
19
20         def _addMesh(self):
21                 m = mesh()
22                 self._meshList.append(m)
23                 return m
24
25         def _postProcessAfterLoad(self):
26                 for m in self._meshList:
27                         m._calculateNormals()
28                 self.processMatrix()
29
30         def processMatrix(self):
31                 self._transformedMin = numpy.array([999999999999,999999999999,999999999999], numpy.float64)
32                 self._transformedMax = numpy.array([-999999999999,-999999999999,-999999999999], numpy.float64)
33                 self._boundaryCircleSize = 0
34
35                 for m in self._meshList:
36                         transformedVertexes = (numpy.matrix(m.vertexes, copy = False) * self._matrix).getA()
37                         transformedMin = transformedVertexes.min(0)
38                         transformedMax = transformedVertexes.max(0)
39                         for n in xrange(0, 3):
40                                 self._transformedMin[n] = min(transformedMin[n], self._transformedMin[n])
41                                 self._transformedMax[n] = max(transformedMax[n], self._transformedMax[n])
42
43                         #Calculate the boundary circle
44                         transformedSize = transformedMax - transformedMin
45                         center = transformedMin + transformedSize / 2.0
46                         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)
47                         self._boundaryCircleSize = max(self._boundaryCircleSize, boundaryCircleSize)
48                 self._transformedSize = self._transformedMax - self._transformedMin
49                 self._drawOffset = (self._transformedMax + self._transformedMin) / 2
50                 self._drawOffset[2] = self._transformedMin[2]
51                 self._transformedMax -= self._drawOffset
52                 self._transformedMin -= self._drawOffset
53
54         def getPosition(self):
55                 return self._position
56         def setPosition(self, newPos):
57                 self._position = newPos
58
59         def getMaximum(self):
60                 return self._transformedMax
61         def getMinimum(self):
62                 return self._transformedMin
63         def getSize(self):
64                 return self._transformedSize
65         def getDrawOffset(self):
66                 return self._drawOffset
67         def getBoundaryCircle(self):
68                 return self._boundaryCircleSize
69
70 class mesh(object):
71         def __init__(self):
72                 self.vertexes = None
73                 self.vertexCount = 0
74                 self.vbo = None
75
76         def _addFace(self, x0, y0, z0, x1, y1, z1, x2, y2, z2):
77                 n = self.vertexCount
78                 self.vertexes[n][0] = x0
79                 self.vertexes[n][1] = y0
80                 self.vertexes[n][2] = z0
81                 n += 1
82                 self.vertexes[n][0] = x1
83                 self.vertexes[n][1] = y1
84                 self.vertexes[n][2] = z1
85                 n += 1
86                 self.vertexes[n][0] = x2
87                 self.vertexes[n][1] = y2
88                 self.vertexes[n][2] = z2
89                 self.vertexCount += 3
90         
91         def _prepareFaceCount(self, faceNumber):
92                 #Set the amount of faces before loading data in them. This way we can create the numpy arrays before we fill them.
93                 self.vertexes = numpy.zeros((faceNumber*3, 3), numpy.float32)
94                 self.normal = numpy.zeros((faceNumber*3, 3), numpy.float32)
95                 self.vertexCount = 0
96
97         def _calculateNormals(self):
98                 #Calculate the normals
99                 tris = self.vertexes.reshape(self.vertexCount / 3, 3, 3)
100                 normals = numpy.cross( tris[::,1 ] - tris[::,0]  , tris[::,2 ] - tris[::,0] )
101                 lens = numpy.sqrt( normals[:,0]**2 + normals[:,1]**2 + normals[:,2]**2 )
102                 normals[:,0] /= lens
103                 normals[:,1] /= lens
104                 normals[:,2] /= lens
105                 
106                 n = numpy.zeros((self.vertexCount / 3, 9), numpy.float32)
107                 n[:,0:3] = normals
108                 n[:,3:6] = normals
109                 n[:,6:9] = normals
110                 self.normal = n.reshape(self.vertexCount, 3)
111                 self.invNormal = -self.normal
112
113         def splitToParts(self, callback = None):
114                 t0 = time.time()
115
116                 #print "%f: " % (time.time() - t0), "Splitting a model with %d vertexes." % (len(self.vertexes))
117                 removeDict = {}
118                 tree = util3d.AABBTree()
119                 off = numpy.array([0.0001,0.0001,0.0001])
120                 for idx in xrange(0, self.vertexCount):
121                         v = self.vertexes[idx]
122                         e = util3d.AABB(v-off, v+off)
123                         q = tree.query(e)
124                         if len(q) < 1:
125                                 e.idx = idx
126                                 tree.insert(e)
127                         else:
128                                 removeDict[idx] = q[0].idx
129                         if callback is not None and (idx % 100) == 0:
130                                 callback(idx)
131                 #print "%f: " % (time.time() - t0), "Marked %d duplicate vertexes for removal." % (len(removeDict))
132
133                 faceList = []
134                 for idx in xrange(0, self.vertexCount, 3):
135                         f = [idx, idx + 1, idx + 2]
136                         if removeDict.has_key(f[0]):
137                                 f[0] = removeDict[f[0]]
138                         if removeDict.has_key(f[1]):
139                                 f[1] = removeDict[f[1]]
140                         if removeDict.has_key(f[2]):
141                                 f[2] = removeDict[f[2]]
142                         faceList.append(f)
143                 
144                 #print "%f: " % (time.time() - t0), "Building face lists after vertex removal."
145                 vertexFaceList = []
146                 for idx in xrange(0, self.vertexCount):
147                         vertexFaceList.append([])
148                 for idx in xrange(0, len(faceList)):
149                         f = faceList[idx]
150                         vertexFaceList[f[0]].append(idx)
151                         vertexFaceList[f[1]].append(idx)
152                         vertexFaceList[f[2]].append(idx)
153                 
154                 #print "%f: " % (time.time() - t0), "Building parts."
155                 self._vertexFaceList = vertexFaceList
156                 self._faceList = faceList
157                 partList = []
158                 doneSet = set()
159                 for idx in xrange(0, len(faceList)):
160                         if not idx in doneSet:
161                                 partList.append(self._createPartFromFacewalk(idx, doneSet))
162                 #print "%f: " % (time.time() - t0), "Split into %d parts" % (len(partList))
163                 self._vertexFaceList = None
164                 self._faceList = None
165                 return partList
166
167         def _createPartFromFacewalk(self, startFaceIdx, doneSet):
168                 m = mesh()
169                 m._prepareVertexCount(self.vertexCount)
170                 todoList = [startFaceIdx]
171                 doneSet.add(startFaceIdx)
172                 while len(todoList) > 0:
173                         faceIdx = todoList.pop()
174                         self._partAddFacewalk(m, faceIdx, doneSet, todoList)
175                 return m
176
177         def _partAddFacewalk(self, part, faceIdx, doneSet, todoList):
178                 f = self._faceList[faceIdx]
179                 v0 = self.vertexes[f[0]]
180                 v1 = self.vertexes[f[1]]
181                 v2 = self.vertexes[f[2]]
182                 part.addVertex(v0[0], v0[1], v0[2])
183                 part.addVertex(v1[0], v1[1], v1[2])
184                 part.addVertex(v2[0], v2[1], v2[2])
185                 for f1 in self._vertexFaceList[f[0]]:
186                         if f1 not in doneSet:
187                                 todoList.append(f1)
188                                 doneSet.add(f1)
189                 for f1 in self._vertexFaceList[f[1]]:
190                         if f1 not in doneSet:
191                                 todoList.append(f1)
192                                 doneSet.add(f1)
193                 for f1 in self._vertexFaceList[f[2]]:
194                         if f1 not in doneSet:
195                                 todoList.append(f1)
196                                 doneSet.add(f1)