From: daid Date: Mon, 21 Oct 2013 08:52:15 +0000 (+0200) Subject: Add imageToMesh tool. X-Git-Tag: 13.11.2~46 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=cura.git;a=commitdiff_plain;h=2e2c06edc94ab171d895db727b47b83e14d49e37 Add imageToMesh tool. --- diff --git a/Cura/gui/sceneView.py b/Cura/gui/sceneView.py index d9956f4e..4d2d9a62 100644 --- a/Cura/gui/sceneView.py +++ b/Cura/gui/sceneView.py @@ -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 index 00000000..22b177c5 --- /dev/null +++ b/Cura/gui/tools/imageToMesh.py @@ -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 diff --git a/Cura/util/drawingLoader/drawing.py b/Cura/util/drawingLoader/drawing.py index 509aeffa..c8871a28 100644 --- a/Cura/util/drawingLoader/drawing.py +++ b/Cura/util/drawingLoader/drawing.py @@ -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