From: daid303 Date: Tue, 16 Apr 2013 10:20:08 +0000 (+0200) Subject: Add object split function. Optimize printing order calculator for shorter runtime. X-Git-Tag: 13.05~100 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=4374dd9b6f2a8ef571d8bb983eb5889ce4e94cfc;p=cura.git Add object split function. Optimize printing order calculator for shorter runtime. --- diff --git a/Cura/gui/sceneView.py b/Cura/gui/sceneView.py index b8c882a0..c9394c79 100644 --- a/Cura/gui/sceneView.py +++ b/Cura/gui/sceneView.py @@ -231,6 +231,16 @@ class SceneView(openglGui.glGuiPanel): self._scene.centerAll() self.sceneUpdated() + def OnSplitObject(self, e): + if self._selectedObj is None: + return + self._scene.remove(self._selectedObj) + for obj in self._selectedObj.split(): + self._scene.add(obj) + self._scene.centerAll() + self._selectObject(None) + self.sceneUpdated() + def OnMergeObjects(self, e): if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj: return @@ -363,8 +373,9 @@ class SceneView(openglGui.glGuiPanel): self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._selectedObj), menu.Append(-1, 'Delete')) if self._selectedObj == self._focusObj: self.Bind(wx.EVT_MENU, self.OnDuplicateObject, menu.Append(-1, 'Duplicate')) + self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split')) if self._selectedObj != self._focusObj and self._focusObj is not None: - self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Merge')) + self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge')) if menu.MenuItemCount > 0: self.PopupMenu(menu) menu.Destroy() diff --git a/Cura/util/mesh.py b/Cura/util/mesh.py index a82f8336..294954a1 100644 --- a/Cura/util/mesh.py +++ b/Cura/util/mesh.py @@ -194,6 +194,16 @@ class printableObject(object): if scale > 0: self.applyMatrix(numpy.matrix([[scale,0,0],[0,scale,0],[0,0,scale]], numpy.float64)) + def split(self): + ret = [] + for oriMesh in self._meshList: + for m in oriMesh.split(): + obj = printableObject() + obj._meshList.append(m) + obj._postProcessAfterLoad() + ret.append(obj) + return ret + class mesh(object): def __init__(self): self.vertexes = None @@ -237,87 +247,62 @@ class mesh(object): self.normal = n.reshape(self.vertexCount, 3) self.invNormal = -self.normal - def splitToParts(self, callback = None): - t0 = time.time() + def _vertexHash(self, idx): + v = self.vertexes[idx] + return int(v[0] * 100) | int(v[1] * 100) << 8 | int(v[2] * 100) << 16 + + def _idxFromHash(self, map, idx): + vHash = self._vertexHash(idx) + for i in map[vHash]: + if numpy.linalg.norm(self.vertexes[i] - self.vertexes[idx]) < 0.001: + return i + + def split(self): + vertexMap = {} - #print "%f: " % (time.time() - t0), "Splitting a model with %d vertexes." % (len(self.vertexes)) - removeDict = {} - tree = util3d.AABBTree() - off = numpy.array([0.0001,0.0001,0.0001]) + vertexToFace = [] for idx in xrange(0, self.vertexCount): - v = self.vertexes[idx] - e = util3d.AABB(v-off, v+off) - q = tree.query(e) - if len(q) < 1: - e.idx = idx - tree.insert(e) - else: - removeDict[idx] = q[0].idx - if callback is not None and (idx % 100) == 0: - callback(idx) - #print "%f: " % (time.time() - t0), "Marked %d duplicate vertexes for removal." % (len(removeDict)) + print idx, self.vertexCount + vHash = self._vertexHash(idx) + if vHash not in vertexMap: + vertexMap[vHash] = [] + vertexMap[vHash].append(idx) + vertexToFace.append([]) faceList = [] for idx in xrange(0, self.vertexCount, 3): - f = [idx, idx + 1, idx + 2] - if removeDict.has_key(f[0]): - f[0] = removeDict[f[0]] - if removeDict.has_key(f[1]): - f[1] = removeDict[f[1]] - if removeDict.has_key(f[2]): - f[2] = removeDict[f[2]] + print idx, self.vertexCount + f = [self._idxFromHash(vertexMap, idx), self._idxFromHash(vertexMap, idx+1), self._idxFromHash(vertexMap, idx+2)] + vertexToFace[f[0]].append(idx / 3) + vertexToFace[f[1]].append(idx / 3) + vertexToFace[f[2]].append(idx / 3) faceList.append(f) - - #print "%f: " % (time.time() - t0), "Building face lists after vertex removal." - vertexFaceList = [] - for idx in xrange(0, self.vertexCount): - vertexFaceList.append([]) - for idx in xrange(0, len(faceList)): - f = faceList[idx] - vertexFaceList[f[0]].append(idx) - vertexFaceList[f[1]].append(idx) - vertexFaceList[f[2]].append(idx) - - #print "%f: " % (time.time() - t0), "Building parts." - self._vertexFaceList = vertexFaceList - self._faceList = faceList - partList = [] + + ret = [] doneSet = set() for idx in xrange(0, len(faceList)): - if not idx in doneSet: - partList.append(self._createPartFromFacewalk(idx, doneSet)) - #print "%f: " % (time.time() - t0), "Split into %d parts" % (len(partList)) - self._vertexFaceList = None - self._faceList = None - return partList - - def _createPartFromFacewalk(self, startFaceIdx, doneSet): - m = mesh() - m._prepareVertexCount(self.vertexCount) - todoList = [startFaceIdx] - doneSet.add(startFaceIdx) - while len(todoList) > 0: - faceIdx = todoList.pop() - self._partAddFacewalk(m, faceIdx, doneSet, todoList) - return m - - def _partAddFacewalk(self, part, faceIdx, doneSet, todoList): - f = self._faceList[faceIdx] - v0 = self.vertexes[f[0]] - v1 = self.vertexes[f[1]] - v2 = self.vertexes[f[2]] - part.addVertex(v0[0], v0[1], v0[2]) - part.addVertex(v1[0], v1[1], v1[2]) - part.addVertex(v2[0], v2[1], v2[2]) - for f1 in self._vertexFaceList[f[0]]: - if f1 not in doneSet: - todoList.append(f1) - doneSet.add(f1) - for f1 in self._vertexFaceList[f[1]]: - if f1 not in doneSet: - todoList.append(f1) - doneSet.add(f1) - for f1 in self._vertexFaceList[f[2]]: - if f1 not in doneSet: - todoList.append(f1) - doneSet.add(f1) + if idx in doneSet: + continue + doneSet.add(idx) + todoList = [idx] + meshFaceList = [] + while len(todoList) > 0: + idx = todoList.pop() + meshFaceList.append(idx) + for n in xrange(0, 3): + for i in vertexToFace[faceList[idx][n]]: + if not i in doneSet: + doneSet.add(i) + todoList.append(i) + + m = mesh() + m._prepareFaceCount(len(meshFaceList)) + for idx in meshFaceList: + m.vertexes[m.vertexCount] = self.vertexes[faceList[idx][0]] + m.vertexCount += 1 + m.vertexes[m.vertexCount] = self.vertexes[faceList[idx][1]] + m.vertexCount += 1 + m.vertexes[m.vertexCount] = self.vertexes[faceList[idx][2]] + m.vertexCount += 1 + ret.append(m) + return ret diff --git a/Cura/util/objectScene.py b/Cura/util/objectScene.py index c2944e25..0338367a 100644 --- a/Cura/util/objectScene.py +++ b/Cura/util/objectScene.py @@ -20,9 +20,19 @@ class _objectOrderFinder(object): if len(initialList) == 0: self.order = [] return + + self._hitMap = [None] * (max(initialList)+1) + for a in initialList: + self._hitMap[a] = [False] * (max(initialList)+1) + for b in initialList: + self._hitMap[a][b] = self._checkHit(a, b) + + initialList.sort(self._objIdxCmp) + self._todo = [_objectOrder([], initialList)] while len(self._todo) > 0: current = self._todo.pop() + #print len(self._todo), len(current.order), len(initialList) for addIdx in current.todo: if not self._checkHitFor(addIdx, current.order): todoList = current.todo[:] @@ -35,9 +45,14 @@ class _objectOrderFinder(object): self._todo.append(_objectOrder(order, todoList)) self.order = None + def _objIdxCmp(self, a, b): + scoreA = sum(self._hitMap[a]) + scoreB = sum(self._hitMap[b]) + return scoreA - scoreB + def _checkHitFor(self, addIdx, others): for idx in others: - if self._checkHit(addIdx, idx): + if self._hitMap[addIdx][idx]: return True return False diff --git a/Cura/util/sliceEngine.py b/Cura/util/sliceEngine.py index 4866b4ec..9583172b 100644 --- a/Cura/util/sliceEngine.py +++ b/Cura/util/sliceEngine.py @@ -100,7 +100,8 @@ class Slicer(object): except: pass else: - print '#', line.strip() + #print '#', line.strip() + pass line = self._process.stdout.readline() for line in self._process.stderr: print line.strip()