chiark / gitweb /
Prevent infinite loop
[cura.git] / Cura / gui / tools / imageToMesh.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2
3 import wx
4 import numpy
5
6 from Cura.util import printableObject
7
8 def supportedExtensions():
9         return ['.bmp', '.jpg', '.jpeg', '.png']
10
11 class convertImageDialog(wx.Dialog):
12         def __init__(self, parent, filename):
13                 super(convertImageDialog, self).__init__(None, title="Convert image...")
14                 wx.EVT_CLOSE(self, self.OnClose)
15                 self.parent = parent
16                 self.filename = filename
17
18                 image = wx.Image(filename)
19                 w, h = image.GetWidth() - 1, image.GetHeight() - 1
20                 self.aspectRatio = float(w) / float(h)
21
22                 p = wx.Panel(self)
23                 self.SetSizer(wx.BoxSizer())
24                 self.GetSizer().Add(p, 1, flag=wx.EXPAND)
25
26                 s = wx.GridBagSizer(2, 2)
27                 p.SetSizer(s)
28                 s.Add(wx.StaticText(p, -1, _('Height (mm)')), pos=(0, 0), flag=wx.LEFT|wx.TOP|wx.RIGHT, border=5)
29                 self.heightInput = wx.TextCtrl(p, -1, '10.0')
30                 s.Add(self.heightInput, pos=(0, 1), flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=5)
31
32                 s.Add(wx.StaticText(p, -1, _('Base (mm)')), pos=(1, 0), flag=wx.LEFT|wx.TOP|wx.RIGHT, border=5)
33                 self.baseHeightInput = wx.TextCtrl(p, -1, '1.0')
34                 s.Add(self.baseHeightInput, pos=(1, 1), flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=5)
35
36                 s.Add(wx.StaticText(p, -1, _('Width (mm)')), pos=(2, 0), flag=wx.LEFT|wx.TOP|wx.RIGHT, border=5)
37                 self.widthInput = wx.TextCtrl(p, -1, str(w * 0.3))
38                 s.Add(self.widthInput, pos=(2, 1), flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=5)
39
40                 s.Add(wx.StaticText(p, -1, _('Depth (mm)')), pos=(3, 0), flag=wx.LEFT|wx.TOP|wx.RIGHT, border=5)
41                 self.depthInput = wx.TextCtrl(p, -1, str(h * 0.3))
42                 s.Add(self.depthInput, pos=(3, 1), flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=5)
43
44                 options = [_('Darker is higher'), _('Lighter is higher')]
45                 self.invertInput = wx.ComboBox(p, -1, options[0], choices=options, style=wx.CB_DROPDOWN|wx.CB_READONLY)
46                 s.Add(self.invertInput, pos=(4, 1), flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=5)
47                 self.invertInput.SetSelection(0)
48
49                 options = [_('No smoothing'), _('Light smoothing'), _('Heavy smoothing')]
50                 self.smoothInput = wx.ComboBox(p, -1, options[0], choices=options, style=wx.CB_DROPDOWN|wx.CB_READONLY)
51                 s.Add(self.smoothInput, pos=(5, 1), flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=5)
52                 self.smoothInput.SetSelection(0)
53
54                 self.okButton = wx.Button(p, -1, 'Ok')
55                 s.Add(self.okButton, pos=(6, 1), flag=wx.ALL, border=5)
56
57                 self.okButton.Bind(wx.EVT_BUTTON, self.OnOkClick)
58                 self.widthInput.Bind(wx.EVT_TEXT, self.OnWidthEnter)
59                 self.depthInput.Bind(wx.EVT_TEXT, self.OnDepthEnter)
60
61                 self.Fit()
62                 self.Centre()
63
64         def OnClose(self, e):
65                 self.Destroy()
66
67         def OnOkClick(self, e):
68                 self.Close()
69                 height = float(self.heightInput.GetValue())
70                 width = float(self.widthInput.GetValue())
71                 blur = self.smoothInput.GetSelection()
72                 blur *= blur
73                 invert = self.invertInput.GetSelection() == 0
74                 baseHeight = float(self.baseHeightInput.GetValue())
75
76                 obj = convertImage(self.filename, height, width, blur, invert, baseHeight)
77                 self.parent._scene.add(obj)
78                 self.parent._scene.centerAll()
79                 self.parent.sceneUpdated()
80
81         def OnWidthEnter(self, e):
82                 try:
83                         w = float(self.widthInput.GetValue())
84                 except ValueError:
85                         return
86                 h = w / self.aspectRatio
87                 new_h = str(h)
88                 old_h = str(self.depthInput.GetValue())
89                 if new_h != old_h:
90                         self.depthInput.SetValue(new_h)
91
92         def OnDepthEnter(self, e):
93                 try:
94                         h = float(self.depthInput.GetValue())
95                 except ValueError:
96                         return
97                 w = h * self.aspectRatio
98                 new_w = str(w)
99                 old_w = str(self.widthInput.GetValue())
100                 if new_w != old_w:
101                         self.widthInput.SetValue(new_w)
102
103 def convertImage(filename, height=20.0, width=100.0, blur=0, invert=False, baseHeight=1.0):
104         image = wx.Image(filename)
105         image.ConvertToGreyscale()
106         if image.GetHeight() > 512:
107                 image.Rescale(image.GetWidth() * 512 / image.GetHeight(), 512, wx.IMAGE_QUALITY_HIGH)
108         if image.GetWidth() > 512:
109                 image.Rescale(512, image.GetHeight() * 512 / image.GetWidth(), wx.IMAGE_QUALITY_HIGH)
110         if blur > 0:
111                 image = image.Blur(blur)
112         z = numpy.fromstring(image.GetData(), numpy.uint8)
113         z = numpy.array(z[::3], numpy.float32)  #Only get the R values (as we are grayscale), and convert to float values
114         if invert:
115                 z = 255 - z
116         pMin, pMax = numpy.min(z), numpy.max(z)
117         if pMax == pMin:
118                 pMax += 1.0
119         z = ((z - pMin) * height / (pMax - pMin)) + baseHeight
120
121         w, h = image.GetWidth(), image.GetHeight()
122         scale = width / (image.GetWidth() - 1)
123         n = w * h
124         y, x = numpy.mgrid[0:h,0:w]
125         x = numpy.array(x, numpy.float32, copy=False) * scale
126         y = numpy.array(y, numpy.float32, copy=False) *-scale
127         v0 = numpy.concatenate((x.reshape((n, 1)), y.reshape((n, 1)), z.reshape((n, 1))), 1)
128         v0 = v0.reshape((h, w, 3))
129         v1 = v0[0:-1,0:-1,:]
130         v2 = v0[0:-1,1:,:]
131         v3 = v0[1:,0:-1,:]
132         v4 = v0[1:,1:,:]
133
134         obj = printableObject.printableObject(filename)
135         m = obj._addMesh()
136         m._prepareFaceCount((w-1) * (h-1) * 2 + 2 + (w-1)*4 + (h-1)*4)
137         m.vertexes = numpy.array(numpy.concatenate((v1,v3,v2,v2,v3,v4), 2).reshape(((w-1) * (h-1) * 6, 3)), numpy.float32, copy=False)
138         m.vertexes = numpy.concatenate((m.vertexes, numpy.zeros(((2+(w-1)*4+(h-1)*4)*3, 3), numpy.float32)))
139         m.vertexCount = (w-1) * (h-1) * 6
140         x = (w-1)* scale
141         y = (h-1)*-scale
142         m._addFace(0,0,0, x,0,0, 0,y,0)
143         m._addFace(x,y,0, 0,y,0, x,0,0)
144         for n in xrange(0, w-1):
145                 x = n* scale
146                 i = w*h-w+n
147                 m._addFace(x+scale,0,0, x,0,0, x,0,z[n])
148                 m._addFace(x+scale,0,0, x,0,z[n], x+scale,0,z[n+1])
149                 m._addFace(x+scale,y,0, x,y,z[i], x,y,0)
150                 m._addFace(x+scale,y,0, x+scale,y,z[i+1], x,y,z[i])
151
152         x = (w-1)*scale
153         for n in xrange(0, h-1):
154                 y = n*-scale
155                 i = n*w+w-1
156                 m._addFace(0,y-scale,0, 0,y,z[n*w], 0,y,0)
157                 m._addFace(0,y-scale,0, 0,y-scale,z[n*w+w], 0,y,z[n*w])
158                 m._addFace(x,y-scale,0, x,y,0, x,y,z[i])
159                 m._addFace(x,y-scale,0, x,y,z[i], x,y-scale,z[i+w])
160         obj._postProcessAfterLoad()
161         return obj