chiark / gitweb /
d013d5973f09757c0a59946c7443057cdd4eae46
[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 mesh(object):
12         def __init__(self):
13                 self.vertexes = None
14                 self.matrix = numpy.matrix([[1,0,0], [0,1,0], [0,0,1]], numpy.float32);
15                 self.vertexCount = 0
16
17         def addVertex(self, x, y, z):
18                 n = self.vertexCount
19                 self.vertexes[n][0] = x
20                 self.vertexes[n][1] = y
21                 self.vertexes[n][2] = z
22                 self.vertexCount += 1
23         
24         def _prepareVertexCount(self, vertexNumber):
25                 #Set the amount of faces before loading data in them. This way we can create the numpy arrays before we fill them.
26                 self.vertexes = numpy.zeros((vertexNumber, 3), numpy.float32)
27                 self.normal = numpy.zeros((vertexNumber, 3), numpy.float32)
28                 self.vertexCount = 0
29
30         def _postProcessAfterLoad(self):
31                 self.processMatrix()
32                 self._calculateNormals()
33
34         def processMatrix(self):
35                 transformedVertexes = (numpy.matrix(self.vertexes, copy = False) * self.matrix).getA()
36                 self.transformedMin = transformedVertexes.min(0)
37                 self.transformedMax = transformedVertexes.max(0)
38                 self.transformedSize = self.transformedMax - self.transformedMin
39
40                 #Calculate the boundery circle
41                 center = self.transformedMin + self.transformedSize / 2.0
42                 self.bounderyCircleSize = round(math.sqrt(numpy.max(((transformedVertexes[::,0] - center[0]) * (transformedVertexes[::,0] - center[0])) + ((transformedVertexes[::,1] - center[1]) * (transformedVertexes[::,1] - center[1])))), 3)
43
44         def getMaximum(self):
45                 return self.transformedMax
46         def getMinimum(self):
47                 return self.transformedMin
48         def getSize(self):
49                 return self.transformedSize
50
51         def _calculateNormals(self):
52                 #Calculate the normals
53                 tris = self.vertexes.reshape(self.vertexCount / 3, 3, 3)
54                 normals = numpy.cross( tris[::,1 ] - tris[::,0]  , tris[::,2 ] - tris[::,0] )
55                 lens = numpy.sqrt( normals[:,0]**2 + normals[:,1]**2 + normals[:,2]**2 )
56                 normals[:,0] /= lens
57                 normals[:,1] /= lens
58                 normals[:,2] /= lens
59                 
60                 n = numpy.zeros((self.vertexCount / 3, 9), numpy.float32)
61                 n[:,0:3] = normals
62                 n[:,3:6] = normals
63                 n[:,6:9] = normals
64                 self.normal = n.reshape(self.vertexCount, 3)
65                 self.invNormal = -self.normal
66
67         def splitToParts(self, callback = None):
68                 t0 = time.time()
69
70                 #print "%f: " % (time.time() - t0), "Splitting a model with %d vertexes." % (len(self.vertexes))
71                 removeDict = {}
72                 tree = util3d.AABBTree()
73                 off = numpy.array([0.0001,0.0001,0.0001])
74                 for idx in xrange(0, self.vertexCount):
75                         v = self.vertexes[idx]
76                         e = util3d.AABB(v-off, v+off)
77                         q = tree.query(e)
78                         if len(q) < 1:
79                                 e.idx = idx
80                                 tree.insert(e)
81                         else:
82                                 removeDict[idx] = q[0].idx
83                         if callback is not None and (idx % 100) == 0:
84                                 callback(idx)
85                 #print "%f: " % (time.time() - t0), "Marked %d duplicate vertexes for removal." % (len(removeDict))
86
87                 faceList = []
88                 for idx in xrange(0, self.vertexCount, 3):
89                         f = [idx, idx + 1, idx + 2]
90                         if removeDict.has_key(f[0]):
91                                 f[0] = removeDict[f[0]]
92                         if removeDict.has_key(f[1]):
93                                 f[1] = removeDict[f[1]]
94                         if removeDict.has_key(f[2]):
95                                 f[2] = removeDict[f[2]]
96                         faceList.append(f)
97                 
98                 #print "%f: " % (time.time() - t0), "Building face lists after vertex removal."
99                 vertexFaceList = []
100                 for idx in xrange(0, self.vertexCount):
101                         vertexFaceList.append([])
102                 for idx in xrange(0, len(faceList)):
103                         f = faceList[idx]
104                         vertexFaceList[f[0]].append(idx)
105                         vertexFaceList[f[1]].append(idx)
106                         vertexFaceList[f[2]].append(idx)
107                 
108                 #print "%f: " % (time.time() - t0), "Building parts."
109                 self._vertexFaceList = vertexFaceList
110                 self._faceList = faceList
111                 partList = []
112                 doneSet = set()
113                 for idx in xrange(0, len(faceList)):
114                         if not idx in doneSet:
115                                 partList.append(self._createPartFromFacewalk(idx, doneSet))
116                 #print "%f: " % (time.time() - t0), "Split into %d parts" % (len(partList))
117                 self._vertexFaceList = None
118                 self._faceList = None
119                 return partList
120
121         def _createPartFromFacewalk(self, startFaceIdx, doneSet):
122                 m = mesh()
123                 m._prepareVertexCount(self.vertexCount)
124                 todoList = [startFaceIdx]
125                 doneSet.add(startFaceIdx)
126                 while len(todoList) > 0:
127                         faceIdx = todoList.pop()
128                         self._partAddFacewalk(m, faceIdx, doneSet, todoList)
129                 return m
130
131         def _partAddFacewalk(self, part, faceIdx, doneSet, todoList):
132                 f = self._faceList[faceIdx]
133                 v0 = self.vertexes[f[0]]
134                 v1 = self.vertexes[f[1]]
135                 v2 = self.vertexes[f[2]]
136                 part.addVertex(v0[0], v0[1], v0[2])
137                 part.addVertex(v1[0], v1[1], v1[2])
138                 part.addVertex(v2[0], v2[1], v2[2])
139                 for f1 in self._vertexFaceList[f[0]]:
140                         if f1 not in doneSet:
141                                 todoList.append(f1)
142                                 doneSet.add(f1)
143                 for f1 in self._vertexFaceList[f[1]]:
144                         if f1 not in doneSet:
145                                 todoList.append(f1)
146                                 doneSet.add(f1)
147                 for f1 in self._vertexFaceList[f[2]]:
148                         if f1 not in doneSet:
149                                 todoList.append(f1)
150                                 doneSet.add(f1)
151