chiark / gitweb /
Added saving as STL. Still need to add AMF.
authordaid303 <daid303@gmail.com>
Fri, 26 Apr 2013 08:45:23 +0000 (10:45 +0200)
committerdaid303 <daid303@gmail.com>
Fri, 26 Apr 2013 08:45:23 +0000 (10:45 +0200)
Cura/gui/mainWindow.py
Cura/gui/preview3d.py
Cura/gui/projectPlanner.py
Cura/gui/sceneView.py
Cura/gui/tools/batchRun.py
Cura/gui/tools/minecraftImport.py
Cura/gui/util/openglGui.py
Cura/util/mesh.py
Cura/util/meshLoader.py
Cura/util/meshLoaders/stl.py

index f2790865da1d35b67edbf00a7516d4d88f451caf..a32ee36524b46d76f36ba2047f438a21ec9b749f 100644 (file)
@@ -29,7 +29,7 @@ class mainWindow(wx.Frame):
 
                wx.EVT_CLOSE(self, self.OnClose)
 
-               self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions()))
+               self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.loadSupportedExtensions()))
 
                self.normalModeOnlyItems = []
 
@@ -51,9 +51,13 @@ class mainWindow(wx.Frame):
                self.menubar = wx.MenuBar()
                self.fileMenu = wx.Menu()
                i = self.fileMenu.Append(-1, 'Load model file...\tCTRL+L')
-               self.Bind(wx.EVT_MENU, lambda e: self.scene.ShowLoadModel(1), i)
+               self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
+               i = self.fileMenu.Append(-1, 'Save model...\tCTRL+S')
+               self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
+
+               self.fileMenu.AppendSeparator()
                i = self.fileMenu.Append(-1, 'Print...\tCTRL+P')
-               self.Bind(wx.EVT_MENU, lambda e: self.scene.ShowPrintWindow(), i)
+               self.Bind(wx.EVT_MENU, lambda e: self.scene.showPrintWindow(), i)
                i = self.fileMenu.Append(-1, 'Save GCode...')
                self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
 
index e5ac2cad7a1ce2668f6d08207d8e772f275a6721..4a25ea31c1705272220aad475b6e5a48bbd07722 100644 (file)
@@ -343,7 +343,7 @@ class previewPanel(wx.Panel):
                        if wx.TheClipboard.GetData(data):
                                data = data.GetText()
                                if re.match('^http://.*/.*$', data):
-                                       if data.endswith(tuple(meshLoader.supportedExtensions())):
+                                       if data.endswith(tuple(meshLoader.loadSupportedExtensions())):
                                                #Got an url on the clipboard with a model file.
                                                pass
                        wx.TheClipboard.Close()
index 190e131994dfd7c19b2fb009c1a0c904acecf5b8..626e006d754831279645d68b41252a72074d80df 100644 (file)
@@ -122,7 +122,7 @@ class projectPlanner(wx.Frame):
                self.SetSizer(wx.BoxSizer(wx.VERTICAL))
                self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
 
-               self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions()))
+               self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.loadSupportedExtensions()))
 
                self.list = []
                self.selection = None
index 0d6cb3e737e8e8cf71e9cc748e81c275ce3902cb..ccbfd033443a9e7d0e80c20fcf52788646f72264 100644 (file)
@@ -56,8 +56,8 @@ class SceneView(openglGui.glGuiPanel):
                self._projMatrix = None
                self.tempMatrix = None
 
-               self.openFileButton      = openglGui.glButton(self, 4, 'Load', (0,0), self.ShowLoadModel)
-               self.printButton         = openglGui.glButton(self, 6, 'Print', (1,0), self.ShowPrintWindow)
+               self.openFileButton      = openglGui.glButton(self, 4, 'Load', (0,0), self.showLoadModel)
+               self.printButton         = openglGui.glButton(self, 6, 'Print', (1,0), self.showPrintWindow)
                self.printButton.setDisabled(True)
 
                group = []
@@ -111,10 +111,10 @@ class SceneView(openglGui.glGuiPanel):
                self.updateToolButtons()
                self.updateProfileToControls()
 
-       def ShowLoadModel(self, button):
+       def showLoadModel(self, button = 1):
                if button == 1:
                        dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
-                       dlg.SetWildcard(meshLoader.wildcardFilter())
+                       dlg.SetWildcard(meshLoader.loadWildcardFilter())
                        if dlg.ShowModal() != wx.ID_OK:
                                dlg.Destroy()
                                return
@@ -126,7 +126,19 @@ class SceneView(openglGui.glGuiPanel):
                        self.GetParent().GetParent().GetParent().addToModelMRU(filename)
                        self.loadScene([filename])
 
-       def ShowPrintWindow(self, button):
+       def showSaveModel(self):
+               if len(self._scene.objects()) < 1:
+                       return
+               dlg=wx.FileDialog(self, 'Save 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
+               dlg.SetWildcard(meshLoader.saveWildcardFilter())
+               if dlg.ShowModal() != wx.ID_OK:
+                       dlg.Destroy()
+                       return
+               filename = dlg.GetPath()
+               dlg.Destroy()
+               meshLoader.saveMeshes(filename, self._scene.objects())
+
+       def showPrintWindow(self, button):
                if button == 1:
                        if machineCom.machineIsConnected():
                                printWindow.printFile(self._slicer.getGCodeFilename())
@@ -238,6 +250,7 @@ class SceneView(openglGui.glGuiPanel):
                self._selectedObj.resetRotation()
                self._scene.pushFree()
                self._selectObject(self._selectedObj)
+               self.sceneUpdated()
 
        def OnLayFlat(self, button):
                if self._selectedObj is None:
@@ -245,11 +258,15 @@ class SceneView(openglGui.glGuiPanel):
                self._selectedObj.layFlat()
                self._scene.pushFree()
                self._selectObject(self._selectedObj)
+               self.sceneUpdated()
 
        def OnScaleReset(self, button):
                if self._selectedObj is None:
                        return
                self._selectedObj.resetScale()
+               self._selectObject(self._selectedObj)
+               self.updateProfileToControls()
+               self.sceneUpdated()
 
        def OnScaleMax(self, button):
                if self._selectedObj is None:
index ea4790bd501e3346a5dc0dd6a240261309e23f91..c020346828406bd10a3eb8e6f802845798364ac4 100644 (file)
@@ -14,7 +14,7 @@ class batchRunWindow(wx.Frame):
                
                self.list = []
                
-               self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions()))
+               self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.loadSupportedExtensions()))
                
                wx.EVT_CLOSE(self, self.OnClose)
                self.panel = wx.Panel(self, -1)
index ea230f10ab29f3956dce7fc81d252f59c4dc2612..3023fba18fb64a40751263d890712a2d745db8f2 100644 (file)
@@ -211,7 +211,9 @@ class minecraftImportWindow(wx.Frame):
                                                        faceCount += 1
                                                if y == sy - 1 or not self.isSolid[blocks[x, y + 1, z]]:
                                                        faceCount += 1
-               m = mesh.mesh()
+
+               obj = mesh.printableObject()
+               m = obj._addMesh()
                m._prepareFaceCount(faceCount * 2)
                for x in xrange(0, sx):
                        for y in xrange(0, sy):
@@ -247,6 +249,5 @@ class minecraftImportWindow(wx.Frame):
 
                                                        m._addFace(x+1, y+1, z+1, x+1, y+1, z, x, y+1, z+1)
 
-               stlFilename = os.path.join(os.path.dirname(self.level.filename), 'export.stl')
-               stl.saveAsSTL(m, stlFilename)
-               self.GetParent().scene.loadScene([stlFilename])
+               obj._postProcessAfterLoad()
+               self.GetParent().scene._scene.add(obj)
index e94c83aff7d9791949205fffdcbf8b03b561b551..adbbf001e356e7aceb65ff01e51ec4d58da65a8b 100644 (file)
@@ -470,10 +470,11 @@ class glButton(glGuiControl):
                        glColor4ub(255,255,255,255)
                        opengl.glDrawStringCenter(self._tooltip)
                glPopMatrix()
-               if self._progressBar is not None:
+               progress = self._progressBar
+               if progress is not None:
                        glColor4ub(255,255,255,192)
                        opengl.glDrawTexturedQuad(pos[0]-bs/2, pos[1]+bs/2, bs, bs / 4, 0)
-                       opengl.glDrawTexturedQuad(pos[0]-bs/2, pos[1]+bs/2, bs * self._progressBar, bs / 4, 0)
+                       opengl.glDrawTexturedQuad(pos[0]-bs/2, pos[1]+bs/2, bs * progress, bs / 4, 0)
                elif len(self._altTooltip) > 0:
                        glPushMatrix()
                        glTranslatef(pos[0], pos[1], 0)
index d2cb0988329e1144722c433e595dfdfbd01fc3d6..a5fd0f039bfbd0d5a4b5e44ae2b1c903bb98d8d5 100644 (file)
@@ -21,18 +21,21 @@ class printableObject(object):
        def copy(self):
                ret = printableObject()
                ret._matrix = self._matrix.copy()
-               ret._meshList = self._meshList[:]
-               ret._transformedMin = self._transformedMin
-               ret._transformedMax = self._transformedMin
-               ret._transformedSize = self._transformedSize
+               ret._transformedMin = self._transformedMin.copy()
+               ret._transformedMax = self._transformedMin.copy()
+               ret._transformedSize = self._transformedSize.copy()
                ret._boundaryCircleSize = self._boundaryCircleSize
-               ret._drawOffset = self._drawOffset
-               for m in ret._meshList:
-                       m.vbo.incRef()
+               ret._drawOffset = self._drawOffset.copy()
+               for m in self._meshList[:]:
+                       m2 = ret._addMesh()
+                       m2.vertexes = m.vertexes
+                       m2.vertexCount = m.vertexCount
+                       m2.vbo = m.vbo
+                       m2.vbo.incRef()
                return ret
 
        def _addMesh(self):
-               m = mesh()
+               m = mesh(self)
                self._meshList.append(m)
                return m
 
@@ -51,7 +54,7 @@ class printableObject(object):
                self._boundaryCircleSize = 0
 
                for m in self._meshList:
-                       transformedVertexes = (numpy.matrix(m.vertexes, copy = False) * numpy.matrix(self._matrix, numpy.float32)).getA()
+                       transformedVertexes = m.getTransformedVertexes()
                        transformedMin = transformedVertexes.min(0)
                        transformedMax = transformedVertexes.max(0)
                        for n in xrange(0, 3):
@@ -136,7 +139,7 @@ class printableObject(object):
                self.processMatrix()
 
        def layFlat(self):
-               transformedVertexes = (numpy.matrix(self._meshList[0].vertexes, copy = False) * self._matrix).getA()
+               transformedVertexes = self.getTransformedVertexes()
                minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
                dotMin = 1.0
                dotV = None
@@ -157,7 +160,7 @@ class printableObject(object):
                self._matrix *= numpy.matrix([[math.cos(rad), 0, math.sin(rad)], [0,1,0], [-math.sin(rad), 0, math.cos(rad)]], numpy.float64)
 
 
-               transformedVertexes = (numpy.matrix(self._meshList[0].vertexes, copy = False) * self._matrix).getA()
+               transformedVertexes = self.getTransformedVertexes()
                minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
                dotMin = 1.0
                dotV = None
@@ -194,19 +197,15 @@ class printableObject(object):
        def split(self, callback):
                ret = []
                for oriMesh in self._meshList:
-                       for m in oriMesh.split(callback):
-                               obj = printableObject()
-                               obj._meshList.append(m)
-                               obj._matrix = self._matrix.copy()
-                               obj._postProcessAfterLoad()
-                               ret.append(obj)
+                       ret += oriMesh.split(callback)
                return ret
 
 class mesh(object):
-       def __init__(self):
+       def __init__(self, obj):
                self.vertexes = None
                self.vertexCount = 0
                self.vbo = None
+               self._obj = obj
 
        def _addFace(self, x0, y0, z0, x1, y1, z1, x2, y2, z2):
                n = self.vertexCount
@@ -255,6 +254,16 @@ class mesh(object):
                        if numpy.linalg.norm(self.vertexes[i] - self.vertexes[idx]) < 0.001:
                                return i
 
+       def getTransformedVertexes(self, applyOffsets = False):
+               if applyOffsets:
+                       pos = self._obj._position.copy()
+                       pos.resize((3))
+                       pos[2] = self._obj.getSize()[2] / 2
+                       offset = self._obj._drawOffset.copy()
+                       offset[2] += self._obj.getSize()[2] / 2
+                       return (numpy.matrix(self.vertexes, copy = False) * numpy.matrix(self._obj._matrix, numpy.float32)).getA() - offset + pos
+               return (numpy.matrix(self.vertexes, copy = False) * numpy.matrix(self._obj._matrix, numpy.float32)).getA()
+
        def split(self, callback):
                vertexMap = {}
 
@@ -295,7 +304,9 @@ class mesh(object):
                                                        doneSet.add(i)
                                                        todoList.append(i)
 
-                       m = mesh()
+                       obj = printableObject()
+                       obj._matrix = self._obj._matrix.copy()
+                       m = obj._addMesh()
                        m._prepareFaceCount(len(meshFaceList))
                        for idx in meshFaceList:
                                m.vertexes[m.vertexCount] = self.vertexes[faceList[idx][0]]
@@ -304,5 +315,6 @@ class mesh(object):
                                m.vertexCount += 1
                                m.vertexes[m.vertexCount] = self.vertexes[faceList[idx][2]]
                                m.vertexCount += 1
-                       ret.append(m)
+                       obj._postProcessAfterLoad()
+                       ret.append(obj)
                return ret
index 5be4280b4db0a5e1c34a5ec19bf71876f894b0f5..6f81cf3d1c11d2a92c0efcfb0a00b70ab881f26d 100644 (file)
@@ -5,11 +5,18 @@ from Cura.util.meshLoaders import obj
 from Cura.util.meshLoaders import dae
 from Cura.util.meshLoaders import amf
 
-def supportedExtensions():
+def loadSupportedExtensions():
        return ['.stl', '.obj', '.dae', '.amf']
 
-def wildcardFilter():
-       wildcardList = ';'.join(map(lambda s: '*' + s, supportedExtensions()))
+def saveSupportedExtensions():
+       return ['.amf', '.stl']
+
+def loadWildcardFilter():
+       wildcardList = ';'.join(map(lambda s: '*' + s, loadSupportedExtensions()))
+       return "Mesh files (%s)|%s;%s" % (wildcardList, wildcardList, wildcardList.upper())
+
+def saveWildcardFilter():
+       wildcardList = ';'.join(map(lambda s: '*' + s, saveSupportedExtensions()))
        return "Mesh files (%s)|%s;%s" % (wildcardList, wildcardList, wildcardList.upper())
 
 #loadMeshes loads 1 or more printableObjects from a file.
@@ -30,3 +37,13 @@ def loadMeshes(filename):
                return amf.loadScene(filename)
        print 'Error: Unknown model extension: %s' % (ext)
        return []
+
+def saveMeshes(filename, objects):
+       ext = filename[filename.rfind('.'):].lower()
+       if ext == '.stl':
+               stl.saveScene(filename, objects)
+               return
+       if ext == '.amf':
+               amf.saveScene(filename, objects)
+               return
+       print 'Error: Unknown model extension: %s' % (ext)
index ab9c935400a2f13db3d58b326836a117f1a539f4..de7061677a848b553a8d3763777b7b5de7c46f3f 100644 (file)
@@ -51,28 +51,28 @@ def loadScene(filename):
        obj._postProcessAfterLoad()
        return [obj]
 
-def saveAsSTL(mesh, filename):
+def saveScene(filename, objects):
        f = open(filename, 'wb')
        #Write the STL binary header. This can contain any info, except for "SOLID" at the start.
        f.write(("CURA BINARY STL EXPORT. " + time.strftime('%a %d %b %Y %H:%M:%S')).ljust(80, '\000'))
-       #Next follow 4 binary bytes containing the amount of faces, and then the face information.
-       f.write(struct.pack("<I", int(mesh.vertexCount / 3)))
-       for idx in xrange(0, mesh.vertexCount, 3):
-               v1 = mesh.vertexes[idx]
-               v2 = mesh.vertexes[idx+1]
-               v3 = mesh.vertexes[idx+2]
-               f.write(struct.pack("<fff", 0.0, 0.0, 0.0))
-               f.write(struct.pack("<fff", v1[0], v1[1], v1[2]))
-               f.write(struct.pack("<fff", v2[0], v2[1], v2[2]))
-               f.write(struct.pack("<fff", v3[0], v3[1], v3[2]))
-               f.write(struct.pack("<H", 0))
-       f.close()
 
-if __name__ == '__main__':
-       for filename in sys.argv[1:]:
-               m = stlModel().load(filename)
-               print("Loaded %d faces" % (m.vertexCount / 3))
-               parts = m.splitToParts()
-               for p in parts:
-                       saveAsSTL(p, "export_%i.stl" % parts.index(p))
+       vertexCount = 0
+       for obj in objects:
+               for m in obj._meshList:
+                       vertexCount += m.vertexCount
 
+       #Next follow 4 binary bytes containing the amount of faces, and then the face information.
+       f.write(struct.pack("<I", int(vertexCount / 3)))
+       for obj in objects:
+               for m in obj._meshList:
+                       vertexes = m.getTransformedVertexes(True)
+                       for idx in xrange(0, m.vertexCount, 3):
+                               v1 = vertexes[idx]
+                               v2 = vertexes[idx+1]
+                               v3 = vertexes[idx+2]
+                               f.write(struct.pack("<fff", 0.0, 0.0, 0.0))
+                               f.write(struct.pack("<fff", v1[0], v1[1], v1[2]))
+                               f.write(struct.pack("<fff", v2[0], v2[1], v2[2]))
+                               f.write(struct.pack("<fff", v3[0], v3[1], v3[2]))
+                               f.write(struct.pack("<H", 0))
+       f.close()