chiark / gitweb /
Add imageToMesh tool.
authordaid <daid303@gmail.com>
Mon, 21 Oct 2013 08:52:15 +0000 (10:52 +0200)
committerdaid <daid303@gmail.com>
Mon, 21 Oct 2013 08:52:15 +0000 (10:52 +0200)
Cura/gui/sceneView.py
Cura/gui/tools/imageToMesh.py [new file with mode: 0644]
Cura/util/drawingLoader/drawing.py

index d9956f4ede5bd4e609eb777ca252224da4c5398a..4d2d9a624db70cc475b1906f23420ae72537ef94 100644 (file)
@@ -28,6 +28,7 @@ from Cura.gui.util import previewTools
 from Cura.gui.util import opengl
 from Cura.gui.util import openglGui
 from Cura.gui.tools import youmagineGui
+from Cura.gui.tools import imageToMesh
 
 class SceneView(openglGui.glGuiPanel):
        def __init__(self, parent):
@@ -174,7 +175,7 @@ class SceneView(openglGui.glGuiPanel):
                                        if ext == '.ini':
                                                profile.loadProfile(filename)
                                                mainWindow.addToProfileMRU(filename)
-                                       elif ext in meshLoader.loadSupportedExtensions():
+                                       elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions():
                                                scene_filenames.append(filename)
                                                mainWindow.addToModelMRU(filename)
                                        else:
@@ -196,7 +197,7 @@ class SceneView(openglGui.glGuiPanel):
        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|wx.FD_MULTIPLE)
-                       dlg.SetWildcard(meshLoader.loadWildcardFilter() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
+                       dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
                        if dlg.ShowModal() != wx.ID_OK:
                                dlg.Destroy()
                                return
@@ -254,6 +255,8 @@ class SceneView(openglGui.glGuiPanel):
                        self._slicer.submitSliceInfoOnline()
 
        def showSaveGCode(self):
+               if len(self._scene._objectList) < 1:
+                       return
                dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
                filename = self._scene._objectList[0].getName() + '.gcode'
                dlg.SetFilename(filename)
@@ -530,7 +533,12 @@ class SceneView(openglGui.glGuiPanel):
        def loadScene(self, fileList):
                for filename in fileList:
                        try:
-                               objList = meshLoader.loadMeshes(filename)
+                               ext = os.path.splitext(filename)[1].lower()
+                               if ext in imageToMesh.supportedExtensions():
+                                       imageToMesh.convertImageDialog(self, filename).Show()
+                                       objList = []
+                               else:
+                                       objList = meshLoader.loadMeshes(filename)
                        except:
                                traceback.print_exc()
                        else:
diff --git a/Cura/gui/tools/imageToMesh.py b/Cura/gui/tools/imageToMesh.py
new file mode 100644 (file)
index 0000000..22b177c
--- /dev/null
@@ -0,0 +1,151 @@
+__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
+
+import wx
+import numpy
+
+from Cura.util import mesh
+
+def supportedExtensions():
+       return ['.bmp', '.jpg', '.jpeg', '.png']
+
+def wildcardList():
+       return ';'.join(map(lambda s: '*' + s, supportedExtensions()))
+
+class convertImageDialog(wx.Dialog):
+       def __init__(self, parent, filename):
+               super(convertImageDialog, self).__init__(None, title="Convert image...")
+               wx.EVT_CLOSE(self, self.OnClose)
+               self.parent = parent
+               self.filename = filename
+
+               image = wx.Image(filename)
+               w, h = image.GetWidth() - 1, image.GetHeight() - 1
+               self.aspectRatio = float(w) / float(h)
+
+               p = wx.Panel(self)
+               self.SetSizer(wx.BoxSizer())
+               self.GetSizer().Add(p, 1, flag=wx.EXPAND)
+
+               s = wx.GridBagSizer(2, 2)
+               p.SetSizer(s)
+               s.Add(wx.StaticText(p, -1, _('Height (mm)')), pos=(0, 0), flag=wx.LEFT|wx.TOP|wx.RIGHT, border=5)
+               self.heightInput = wx.TextCtrl(p, -1, '10.0')
+               s.Add(self.heightInput, pos=(0, 1), flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=5)
+
+               s.Add(wx.StaticText(p, -1, _('Width (mm)')), pos=(1, 0), flag=wx.LEFT|wx.TOP|wx.RIGHT, border=5)
+               self.widthInput = wx.TextCtrl(p, -1, str(w * 0.3))
+               s.Add(self.widthInput, pos=(1, 1), flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=5)
+
+               s.Add(wx.StaticText(p, -1, _('Depth (mm)')), pos=(2, 0), flag=wx.LEFT|wx.TOP|wx.RIGHT, border=5)
+               self.depthInput = wx.TextCtrl(p, -1, str(h * 0.3))
+               s.Add(self.depthInput, pos=(2, 1), flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=5)
+
+               options = ['Darker is higher', 'Lighter is higher']
+               self.invertInput = wx.ComboBox(p, -1, options[0], choices=options, style=wx.CB_DROPDOWN|wx.CB_READONLY)
+               s.Add(self.invertInput, pos=(3, 1), flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=5)
+
+               options = ['No smoothing', 'Light smoothing', 'Heavy smoothing']
+               self.smoothInput = wx.ComboBox(p, -1, options[0], choices=options, style=wx.CB_DROPDOWN|wx.CB_READONLY)
+               s.Add(self.smoothInput, pos=(4, 1), flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=5)
+
+               self.okButton = wx.Button(p, -1, 'Ok')
+               s.Add(self.okButton, pos=(5, 1), flag=wx.ALL, border=5)
+
+               self.okButton.Bind(wx.EVT_BUTTON, self.OnOkClick)
+               self.widthInput.Bind(wx.EVT_TEXT, self.OnWidthEnter)
+               self.depthInput.Bind(wx.EVT_TEXT, self.OnDepthEnter)
+
+               self.Fit()
+               self.Centre()
+
+       def OnClose(self, e):
+               self.Destroy()
+
+       def OnOkClick(self, e):
+               self.Close()
+               height = float(self.heightInput.GetValue())
+               width = float(self.widthInput.GetValue())
+               blur = self.smoothInput.GetSelection()
+               blur *= blur
+               invert = self.invertInput.GetSelection() == 0
+
+               obj = convertImage(self.filename, height, width, blur, invert)
+               self.parent._scene.add(obj)
+               self.parent._scene.centerAll()
+               self.parent.sceneUpdated()
+
+       def OnWidthEnter(self, e):
+               try:
+                       w = float(self.widthInput.GetValue())
+               except ValueError:
+                       return
+               h = w / self.aspectRatio
+               self.depthInput.SetValue(str(h))
+
+       def OnDepthEnter(self, e):
+               try:
+                       h = float(self.depthInput.GetValue())
+               except ValueError:
+                       return
+               w = h * self.aspectRatio
+               self.widthInput.SetValue(str(w))
+
+def convertImage(filename, height=20.0, width=100.0, blur=0, invert=False, baseHeight=1.0):
+       image = wx.Image(filename)
+       image.ConvertToGreyscale()
+       if image.GetHeight() > 512:
+               image.Rescale(image.GetWidth() * 512 / image.GetHeight(), 512, wx.IMAGE_QUALITY_HIGH)
+       if image.GetWidth() > 512:
+               image.Rescale(512, image.GetHeight() * 512 / image.GetWidth(), wx.IMAGE_QUALITY_HIGH)
+       if blur > 0:
+               image = image.Blur(blur)
+       z = numpy.fromstring(image.GetData(), numpy.uint8)
+       z = numpy.array(z[::3], numpy.float32)  #Only get the R values (as we are grayscale), and convert to float values
+       pMin, pMax = numpy.min(z), numpy.max(z)
+       if pMax == pMin:
+               pMax += 1.0
+       if invert:
+               z = 255 - z
+       z = ((z - pMin) * height / (pMax - pMin)) + baseHeight
+
+       w, h = image.GetWidth(), image.GetHeight()
+       scale = width / (image.GetWidth() - 1)
+       n = w * h
+       y, x = numpy.mgrid[0:h,0:w]
+       x = numpy.array(x, numpy.float32, copy=False) * scale
+       y = numpy.array(y, numpy.float32, copy=False) *-scale
+       v0 = numpy.concatenate((x.reshape((n, 1)), y.reshape((n, 1)), z.reshape((n, 1))), 1)
+       v0 = v0.reshape((h, w, 3))
+       v1 = v0[0:-1,0:-1,:]
+       v2 = v0[0:-1,1:,:]
+       v3 = v0[1:,0:-1,:]
+       v4 = v0[1:,1:,:]
+
+       obj = mesh.printableObject(filename)
+       m = obj._addMesh()
+       m._prepareFaceCount((w-1) * (h-1) * 2 + 2 + (w-1)*4 + (h-1)*4)
+       m.vertexes = numpy.array(numpy.concatenate((v1,v3,v2,v2,v3,v4), 2).reshape(((w-1) * (h-1) * 6, 3)), numpy.float32, copy=False)
+       m.vertexes = numpy.concatenate((m.vertexes, numpy.zeros(((2+(w-1)*4+(h-1)*4)*3, 3), numpy.float32)))
+       m.vertexCount = (w-1) * (h-1) * 6
+       x = (w-1)* scale
+       y = (h-1)*-scale
+       m._addFace(0,0,0, x,0,0, 0,y,0)
+       m._addFace(x,y,0, 0,y,0, x,0,0)
+       for n in xrange(0, w-1):
+               x = n* scale
+               i = w*h-w+n
+               m._addFace(x+scale,0,0, x,0,0, x,0,z[n])
+               m._addFace(x+scale,0,0, x,0,z[n], x+scale,0,z[n+1])
+               m._addFace(x+scale,y,0, x,y,z[i], x,y,0)
+               m._addFace(x+scale,y,0, x+scale,y,z[i+1], x,y,z[i])
+
+       x = (w-1)*scale
+       for n in xrange(0, h-1):
+               y = n*-scale
+               i = n*w+w-1
+               m._addFace(0,y-scale,0, 0,y,z[n*w], 0,y,0)
+               m._addFace(0,y-scale,0, 0,y-scale,z[n*w+w], 0,y,z[n*w])
+               m._addFace(x,y-scale,0, x,y,0, x,y,z[i])
+               m._addFace(x,y-scale,0, x,y,z[i], x,y-scale,z[i+w])
+       obj._postProcessAfterLoad()
+       return obj
index 509aeffa0a82e1b14cff9a32dbd30cdc5537a70c..c8871a285119f30d837dbff1e22c31a074140c00 100644 (file)
@@ -59,6 +59,8 @@ class Path(object):
                        p1 = self._nodes[self._nodes.index(node) - 1].position
 
                p2 = node.position
+               if abs(p1 - p2) < 0.0001:
+                       return p1, 0.0, math.pi, complex(0.0, 0.0)
                rot = math.radians(node.rotation)
                r = node.radius