chiark / gitweb /
e9421df80b3d2d83047f86a0db56a23963c0b201
[cura.git] / Cura / newui / preview3d.py
1 import sys\r
2 import math\r
3 import threading\r
4 import re\r
5 import time\r
6 import os\r
7 \r
8 from wx import glcanvas\r
9 import wx\r
10 try:\r
11         import OpenGL\r
12         OpenGL.ERROR_CHECKING = False\r
13         from OpenGL.GLU import *\r
14         from OpenGL.GL import *\r
15         hasOpenGLlibs = True\r
16 except:\r
17         print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"\r
18         hasOpenGLlibs = False\r
19 \r
20 from newui import profile\r
21 from newui import gcodeInterpreter\r
22 from newui import util3d\r
23 \r
24 from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret\r
25 from fabmetheus_utilities.vector3 import Vector3\r
26 \r
27 class previewPanel(wx.Panel):\r
28         def __init__(self, parent):\r
29                 wx.Panel.__init__(self, parent,-1)\r
30                 \r
31                 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW))\r
32                 self.SetMinSize((440,320))\r
33                 \r
34                 self.glCanvas = PreviewGLCanvas(self)\r
35                 self.init = 0\r
36                 self.triangleMesh = None\r
37                 self.gcode = None\r
38                 self.modelFilename = None\r
39                 self.loadingProgressAmount = 0\r
40                 self.loadThread = None\r
41                 self.machineSize = Vector3(float(profile.getPreference('machine_width')), float(profile.getPreference('machine_depth')), float(profile.getPreference('machine_height')))\r
42                 self.machineCenter = Vector3(0, 0, 0)\r
43                 \r
44                 self.toolbar = wx.ToolBar( self, -1 )\r
45                 self.toolbar.SetToolBitmapSize( ( 21, 21 ) )\r
46 \r
47                 button = wx.Button(self.toolbar, -1, "3D", size=(21*2,21))\r
48                 self.toolbar.AddControl(button)\r
49                 self.Bind(wx.EVT_BUTTON, self.On3DClick, button)\r
50                 \r
51                 button = wx.Button(self.toolbar, -1, "Top", size=(21*2,21))\r
52                 self.toolbar.AddControl(button)\r
53                 self.Bind(wx.EVT_BUTTON, self.OnTopClick, button)\r
54 \r
55                 self.viewSelect = wx.ComboBox(self.toolbar, -1, 'Model - Normal', choices=['Model - Normal', 'Model - Transparent', 'Model - X-Ray', 'GCode', 'Mixed'], style=wx.CB_DROPDOWN|wx.CB_READONLY)\r
56                 self.toolbar.AddControl(self.viewSelect)\r
57                 self.viewSelect.Bind(wx.EVT_COMBOBOX, self.OnViewChange)\r
58                 self.glCanvas.viewMode = self.viewSelect.GetValue()\r
59 \r
60                 self.layerSpin = wx.SpinCtrl(self.toolbar, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)\r
61                 self.toolbar.AddControl(self.layerSpin)\r
62                 self.Bind(wx.EVT_SPINCTRL, self.OnLayerNrChange, self.layerSpin)\r
63                 \r
64                 self.toolbar2 = wx.ToolBar( self, -1 )\r
65                 self.toolbar2.SetToolBitmapSize( ( 21, 21 ) )\r
66                 self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Flip'))\r
67 \r
68                 self.flipX = wx.CheckBox(self.toolbar2, -1, "X")\r
69                 self.flipX.SetValue(profile.getProfileSetting('flip_x') == 'True')\r
70                 self.toolbar2.AddControl(self.flipX)\r
71                 self.Bind(wx.EVT_CHECKBOX, self.OnFlipXClick, self.flipX)\r
72                 self.flipY = wx.CheckBox(self.toolbar2, -1, "Y")\r
73                 self.flipY.SetValue(profile.getProfileSetting('flip_y') == 'True')\r
74                 self.toolbar2.AddControl(self.flipY)\r
75                 self.Bind(wx.EVT_CHECKBOX, self.OnFlipYClick, self.flipY)\r
76                 self.flipZ = wx.CheckBox(self.toolbar2, -1, "Z")\r
77                 self.flipZ.SetValue(profile.getProfileSetting('flip_z') == 'True')\r
78                 self.toolbar2.AddControl(self.flipZ)\r
79                 self.Bind(wx.EVT_CHECKBOX, self.OnFlipZClick, self.flipZ)\r
80                 \r
81                 self.toolbar2.InsertSeparator(self.toolbar2.GetToolsCount())\r
82                 self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Scale'))\r
83                 self.scale = wx.TextCtrl(self.toolbar2, -1, profile.getProfileSetting('model_scale'), size=(21*2,21))\r
84                 self.toolbar2.AddControl(self.scale)\r
85                 self.Bind(wx.EVT_TEXT, self.OnScale, self.scale)\r
86 \r
87                 self.toolbar2.InsertSeparator(self.toolbar2.GetToolsCount())\r
88                 self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Copy'))\r
89                 self.mulXsub = wx.Button(self.toolbar2, -1, '-', size=(21,21))\r
90                 self.toolbar2.AddControl(self.mulXsub)\r
91                 self.Bind(wx.EVT_BUTTON, self.OnMulXSubClick, self.mulXsub)\r
92                 self.mulXadd = wx.Button(self.toolbar2, -1, '+', size=(21,21))\r
93                 self.toolbar2.AddControl(self.mulXadd)\r
94                 self.Bind(wx.EVT_BUTTON, self.OnMulXAddClick, self.mulXadd)\r
95 \r
96                 self.mulYsub = wx.Button(self.toolbar2, -1, '-', size=(21,21))\r
97                 self.toolbar2.AddControl(self.mulYsub)\r
98                 self.Bind(wx.EVT_BUTTON, self.OnMulYSubClick, self.mulYsub)\r
99                 self.mulYadd = wx.Button(self.toolbar2, -1, '+', size=(21,21))\r
100                 self.toolbar2.AddControl(self.mulYadd)\r
101                 self.Bind(wx.EVT_BUTTON, self.OnMulYAddClick, self.mulYadd)\r
102                 \r
103                 self.toolbar2.InsertSeparator(self.toolbar2.GetToolsCount())\r
104                 self.toolbar2.AddControl(wx.StaticText(self.toolbar2, -1, 'Rot'))\r
105                 self.rotate = wx.SpinCtrl(self.toolbar2, -1, profile.getProfileSetting('model_rotate_base'), size=(21*3,21), style=wx.SP_WRAP|wx.SP_ARROW_KEYS)\r
106                 self.rotate.SetRange(0, 360)\r
107                 self.toolbar2.AddControl(self.rotate)\r
108                 self.Bind(wx.EVT_SPINCTRL, self.OnRotate, self.rotate)\r
109                 \r
110                 self.toolbar2.Realize()\r
111                 self.updateToolbar()\r
112                 \r
113                 sizer = wx.BoxSizer(wx.VERTICAL)\r
114                 sizer.Add(self.toolbar, 0, flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, border=1)\r
115                 sizer.Add(self.glCanvas, 1, flag=wx.EXPAND)\r
116                 sizer.Add(self.toolbar2, 0, flag=wx.EXPAND|wx.BOTTOM|wx.LEFT|wx.RIGHT, border=1)\r
117                 self.SetSizer(sizer)\r
118         \r
119         def OnFlipXClick(self, e):\r
120                 profile.putProfileSetting('flip_x', str(self.flipX.GetValue()))\r
121                 self.updateModelTransform()\r
122                 \r
123         def OnFlipYClick(self, e):\r
124                 profile.putProfileSetting('flip_y', str(self.flipY.GetValue()))\r
125                 self.updateModelTransform()\r
126 \r
127         def OnFlipZClick(self, e):\r
128                 profile.putProfileSetting('flip_z', str(self.flipZ.GetValue()))\r
129                 self.updateModelTransform()\r
130 \r
131         def OnMulXAddClick(self, e):\r
132                 profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))+1)))\r
133                 self.updateModelTransform()\r
134 \r
135         def OnMulXSubClick(self, e):\r
136                 profile.putProfileSetting('model_multiply_x', str(max(1, int(profile.getProfileSetting('model_multiply_x'))-1)))\r
137                 self.updateModelTransform()\r
138 \r
139         def OnMulYAddClick(self, e):\r
140                 profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))+1)))\r
141                 self.updateModelTransform()\r
142 \r
143         def OnMulYSubClick(self, e):\r
144                 profile.putProfileSetting('model_multiply_y', str(max(1, int(profile.getProfileSetting('model_multiply_y'))-1)))\r
145                 self.updateModelTransform()\r
146 \r
147         def OnScale(self, e):\r
148                 try:\r
149                         scale = float(self.scale.GetValue())\r
150                 except:\r
151                         scale = 1.0\r
152                 profile.putProfileSetting('model_scale', str(scale))\r
153                 self.updateModelTransform()\r
154         \r
155         def OnRotate(self, e):\r
156                 profile.putProfileSetting('model_rotate_base', self.rotate.GetValue())\r
157                 self.updateModelTransform()\r
158 \r
159         def On3DClick(self, e):\r
160                 self.glCanvas.yaw = 30\r
161                 self.glCanvas.pitch = 60\r
162                 self.glCanvas.zoom = 150\r
163                 self.glCanvas.view3D = True\r
164                 self.glCanvas.Refresh()\r
165 \r
166         def OnTopClick(self, e):\r
167                 self.glCanvas.view3D = False\r
168                 self.glCanvas.zoom = 100\r
169                 self.glCanvas.offsetX = 0\r
170                 self.glCanvas.offsetY = 0\r
171                 self.glCanvas.Refresh()\r
172 \r
173         def OnLayerNrChange(self, e):\r
174                 self.gcodeDirty = True\r
175                 self.glCanvas.Refresh()\r
176 \r
177         def updateCenterX(self, x):\r
178                 self.machineCenter.x = x\r
179                 self.moveModel()\r
180                 self.glCanvas.Refresh()\r
181 \r
182         def updateCenterY(self, y):\r
183                 self.machineCenter.y = y\r
184                 self.moveModel()\r
185                 self.glCanvas.Refresh()\r
186         \r
187         def setViewMode(self, mode):\r
188                 self.viewSelect.SetValue(mode)\r
189                 self.glCanvas.viewMode = self.viewSelect.GetValue()\r
190                 wx.CallAfter(self.glCanvas.Refresh)\r
191         \r
192         def loadModelFile(self, filename):\r
193                 if self.modelFilename != filename:\r
194                         self.modelFileTime = None\r
195                         self.gcodeFileTime = None\r
196                         self.logFileTime = None\r
197                 \r
198                 self.modelFilename = filename\r
199                 self.gcodeFilename = filename[: filename.rfind('.')] + "_export.gcode"\r
200                 self.logFilename = filename[: filename.rfind('.')] + "_export.log"\r
201                 #Do the STL file loading in a background thread so we don't block the UI.\r
202                 if self.loadThread != None and self.loadThread.isAlive():\r
203                         self.loadThread.join()\r
204                 self.loadThread = threading.Thread(target=self.doFileLoadThread)\r
205                 self.loadThread.daemon = True\r
206                 self.loadThread.start()\r
207         \r
208         def loadReModelFile(self, filename):\r
209                 #Only load this again if the filename matches the file we have already loaded (for auto loading GCode after slicing)\r
210                 if self.modelFilename != filename:\r
211                         return False\r
212                 self.loadModelFile(filename)\r
213                 return True\r
214         \r
215         def doFileLoadThread(self):\r
216                 if os.path.isfile(self.modelFilename) and self.modelFileTime != os.stat(self.modelFilename).st_mtime:\r
217                         self.modelFileTime = os.stat(self.modelFilename).st_mtime\r
218                         triangleMesh = fabmetheus_interpret.getCarving(self.modelFilename)\r
219                         triangleMesh.origonalVertexes = list(triangleMesh.vertexes)\r
220                         for i in xrange(0, len(triangleMesh.origonalVertexes)):\r
221                                 triangleMesh.origonalVertexes[i] = triangleMesh.origonalVertexes[i].copy()\r
222                         triangleMesh.getMinimumZ()\r
223                         self.modelDirty = False\r
224                         self.errorList = []\r
225                         self.triangleMesh = triangleMesh\r
226                         self.updateModelTransform()\r
227                         wx.CallAfter(self.updateToolbar)\r
228                         wx.CallAfter(self.glCanvas.Refresh)\r
229                 \r
230                 if os.path.isfile(self.gcodeFilename) and self.gcodeFileTime != os.stat(self.gcodeFilename).st_mtime:\r
231                         self.gcodeFileTime = os.stat(self.gcodeFilename).st_mtime\r
232                         gcode = gcodeInterpreter.gcode()\r
233                         gcode.progressCallback = self.loadProgress\r
234                         gcode.load(self.gcodeFilename)\r
235                         self.loadingProgressAmount = 0\r
236                         self.gcodeDirty = False\r
237                         self.errorList = []\r
238                         self.gcode = gcode\r
239                         self.gcodeDirty = True\r
240                         wx.CallAfter(self.updateToolbar)\r
241                         wx.CallAfter(self.glCanvas.Refresh)\r
242                 elif not os.path.isfile(self.gcodeFilename):\r
243                         self.gcode = None\r
244                 \r
245                 if os.path.isfile(self.logFilename):\r
246                         errorList = []\r
247                         for line in open(self.logFilename, "rt"):\r
248                                 res = re.search('Model error\(([a-z ]*)\): \(([0-9\.\-e]*), ([0-9\.\-e]*), ([0-9\.\-e]*)\) \(([0-9\.\-e]*), ([0-9\.\-e]*), ([0-9\.\-e]*)\)', line)\r
249                                 if res != None:\r
250                                         v1 = util3d.Vector3(float(res.group(2)), float(res.group(3)), float(res.group(4)))\r
251                                         v2 = util3d.Vector3(float(res.group(5)), float(res.group(6)), float(res.group(7)))\r
252                                         errorList.append([v1, v2])\r
253                         self.errorList = errorList\r
254                         wx.CallAfter(self.glCanvas.Refresh)\r
255         \r
256         def loadProgress(self, progress):\r
257                 self.loadingProgressAmount = progress\r
258                 wx.CallAfter(self.glCanvas.Refresh)\r
259         \r
260         def updateToolbar(self):\r
261                 self.layerSpin.Show(self.gcode != None)\r
262                 if self.gcode != None:\r
263                         self.layerSpin.SetRange(1, self.gcode.layerCount)\r
264                 self.toolbar.Realize()\r
265         \r
266         def OnViewChange(self, e):\r
267                 self.glCanvas.viewMode = self.viewSelect.GetValue()\r
268                 self.glCanvas.Refresh()\r
269         \r
270         def updateModelTransform(self, f=0):\r
271                 if self.triangleMesh == None:\r
272                         return\r
273                 scale = 1.0\r
274                 rotate = 0.0\r
275                 try:\r
276                         scale = float(profile.getProfileSetting('model_scale'))\r
277                         rotate = float(profile.getProfileSetting('model_rotate_base')) / 180 * math.pi\r
278                 except:\r
279                         pass\r
280                 scaleX = scale\r
281                 scaleY = scale\r
282                 scaleZ = scale\r
283                 if profile.getProfileSetting('flip_x') == 'True':\r
284                         scaleX = -scaleX\r
285                 if profile.getProfileSetting('flip_y') == 'True':\r
286                         scaleY = -scaleY\r
287                 if profile.getProfileSetting('flip_z') == 'True':\r
288                         scaleZ = -scaleZ\r
289                 mat00 = math.cos(rotate) * scaleX\r
290                 mat01 =-math.sin(rotate) * scaleY\r
291                 mat10 = math.sin(rotate) * scaleX\r
292                 mat11 = math.cos(rotate) * scaleY\r
293                 \r
294                 for i in xrange(0, len(self.triangleMesh.origonalVertexes)):\r
295                         self.triangleMesh.vertexes[i].x = self.triangleMesh.origonalVertexes[i].x * mat00 + self.triangleMesh.origonalVertexes[i].y * mat01\r
296                         self.triangleMesh.vertexes[i].y = self.triangleMesh.origonalVertexes[i].x * mat10 + self.triangleMesh.origonalVertexes[i].y * mat11\r
297                         self.triangleMesh.vertexes[i].z = self.triangleMesh.origonalVertexes[i].z * scaleZ\r
298 \r
299                 for face in self.triangleMesh.faces:\r
300                         v1 = self.triangleMesh.vertexes[face.vertexIndexes[0]]\r
301                         v2 = self.triangleMesh.vertexes[face.vertexIndexes[1]]\r
302                         v3 = self.triangleMesh.vertexes[face.vertexIndexes[2]]\r
303                         face.normal = (v2 - v1).cross(v3 - v1)\r
304                         face.normal.normalize()\r
305 \r
306                 self.moveModel()\r
307         \r
308         def moveModel(self):\r
309                 if self.triangleMesh == None:\r
310                         return\r
311                 minZ = self.triangleMesh.getMinimumZ()\r
312                 min = self.triangleMesh.getCarveCornerMinimum()\r
313                 max = self.triangleMesh.getCarveCornerMaximum()\r
314                 for v in self.triangleMesh.vertexes:\r
315                         v.z -= minZ\r
316                         v.x -= min.x + (max.x - min.x) / 2\r
317                         v.y -= min.y + (max.y - min.y) / 2\r
318                         v.x += self.machineCenter.x\r
319                         v.y += self.machineCenter.y\r
320                 self.triangleMesh.getMinimumZ()\r
321                 self.modelDirty = True\r
322                 self.glCanvas.Refresh()\r
323 \r
324 class PreviewGLCanvas(glcanvas.GLCanvas):\r
325         def __init__(self, parent):\r
326                 attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)\r
327                 glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)\r
328                 self.parent = parent\r
329                 self.context = glcanvas.GLContext(self)\r
330                 wx.EVT_PAINT(self, self.OnPaint)\r
331                 wx.EVT_SIZE(self, self.OnSize)\r
332                 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)\r
333                 wx.EVT_MOTION(self, self.OnMouseMotion)\r
334                 wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)\r
335                 self.yaw = 30\r
336                 self.pitch = 60\r
337                 self.zoom = 150\r
338                 self.offsetX = 0\r
339                 self.offsetY = 0\r
340                 self.view3D = True\r
341                 self.modelDisplayList = None\r
342                 self.gcodeDisplayList = None\r
343         \r
344         def OnMouseMotion(self,e):\r
345                 if e.Dragging() and e.LeftIsDown():\r
346                         if self.view3D:\r
347                                 self.yaw += e.GetX() - self.oldX\r
348                                 self.pitch -= e.GetY() - self.oldY\r
349                                 if self.pitch > 170:\r
350                                         self.pitch = 170\r
351                                 if self.pitch < 10:\r
352                                         self.pitch = 10\r
353                         else:\r
354                                 self.offsetX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2\r
355                                 self.offsetY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2\r
356                         self.Refresh()\r
357                 if e.Dragging() and e.RightIsDown():\r
358                         self.zoom += e.GetY() - self.oldY\r
359                         if self.zoom < 1:\r
360                                 self.zoom = 1\r
361                         self.Refresh()\r
362                 self.oldX = e.GetX()\r
363                 self.oldY = e.GetY()\r
364         \r
365         def OnMouseWheel(self,e):\r
366                 self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0\r
367                 if self.zoom < 1.0:\r
368                         self.zoom = 1.0\r
369                 self.Refresh()\r
370         \r
371         def OnEraseBackground(self,event):\r
372                 #Workaround for windows background redraw flicker.\r
373                 pass\r
374         \r
375         def OnSize(self,event):\r
376                 self.Refresh()\r
377 \r
378         def OnPaint(self,event):\r
379                 dc = wx.PaintDC(self)\r
380                 if not hasOpenGLlibs:\r
381                         dc.Clear()\r
382                         dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)\r
383                         return\r
384                 self.SetCurrent(self.context)\r
385                 self.InitGL()\r
386                 self.OnDraw()\r
387                 self.SwapBuffers()\r
388 \r
389         def OnDraw(self):\r
390                 machineSize = self.parent.machineSize\r
391                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)\r
392                 \r
393                 glTranslate(-self.parent.machineCenter.x, -self.parent.machineCenter.y, 0)\r
394                 \r
395                 glColor3f(1,1,1)\r
396                 glLineWidth(4)\r
397                 glDisable(GL_LIGHTING)\r
398                 glBegin(GL_LINE_LOOP)\r
399                 glVertex3f(0, 0, 0)\r
400                 glVertex3f(machineSize.x, 0, 0)\r
401                 glVertex3f(machineSize.x, machineSize.y, 0)\r
402                 glVertex3f(0, machineSize.y, 0)\r
403                 glEnd()\r
404                 glLineWidth(2)\r
405                 glBegin(GL_LINES)\r
406                 for i in xrange(0, int(machineSize.x), 10):\r
407                         glVertex3f(i, 0, 0)\r
408                         glVertex3f(i, machineSize.y, 0)\r
409                 for i in xrange(0, int(machineSize.y), 10):\r
410                         glVertex3f(0, i, 0)\r
411                         glVertex3f(machineSize.x, i, 0)\r
412                 glEnd()\r
413                 glLineWidth(1)\r
414                 glBegin(GL_LINE_LOOP)\r
415                 glVertex3f(0, 0, machineSize.z)\r
416                 glVertex3f(machineSize.x, 0, machineSize.z)\r
417                 glVertex3f(machineSize.x, machineSize.y, machineSize.z)\r
418                 glVertex3f(0, machineSize.y, machineSize.z)\r
419                 glEnd()\r
420                 glBegin(GL_LINES)\r
421                 glVertex3f(0, 0, 0)\r
422                 glVertex3f(0, 0, machineSize.z)\r
423                 glVertex3f(machineSize.x, 0, 0)\r
424                 glVertex3f(machineSize.x, 0, machineSize.z)\r
425                 glVertex3f(machineSize.x, machineSize.y, 0)\r
426                 glVertex3f(machineSize.x, machineSize.y, machineSize.z)\r
427                 glVertex3f(0, machineSize.y, 0)\r
428                 glVertex3f(0, machineSize.y, machineSize.z)\r
429                 glEnd()\r
430 \r
431                 if self.parent.gcode != None:\r
432                         if self.gcodeDisplayList == None:\r
433                                 self.gcodeDisplayList = glGenLists(1);\r
434                         if self.parent.gcodeDirty:\r
435                                 self.parent.gcodeDirty = False\r
436                                 glNewList(self.gcodeDisplayList, GL_COMPILE)\r
437                                 prevLayerZ = 0.0\r
438                                 curLayerZ = 0.0\r
439                                 \r
440                                 layerThickness = 0.0\r
441                                 filamentRadius = float(profile.getProfileSetting('filament_diameter')) / 2\r
442                                 filamentArea = math.pi * filamentRadius * filamentRadius\r
443                                 lineWidth = float(profile.getProfileSetting('nozzle_size')) / 2\r
444                                 \r
445                                 curLayerNum = 0\r
446                                 for path in self.parent.gcode.pathList:\r
447                                         if path.layerNr != curLayerNum:\r
448                                                 prevLayerZ = curLayerZ\r
449                                                 curLayerZ = path.list[1].z\r
450                                                 curLayerNum = path.layerNr\r
451                                                 layerThickness = curLayerZ - prevLayerZ\r
452                                         \r
453                                         c = 1.0\r
454                                         if path.layerNr != self.parent.layerSpin.GetValue():\r
455                                                 if path.layerNr < self.parent.layerSpin.GetValue():\r
456                                                         c = 0.9 - (self.parent.layerSpin.GetValue() - path.layerNr) * 0.1\r
457                                                         if c < 0.4:\r
458                                                                 c = 0.4\r
459                                                 else:\r
460                                                         break\r
461                                         if path.type == 'move':\r
462                                                 glColor3f(0,0,c)\r
463                                         if path.type == 'extrude':\r
464                                                 if path.pathType == 'FILL':\r
465                                                         glColor3f(c/2,c/2,0)\r
466                                                 elif path.pathType == 'WALL-INNER':\r
467                                                         glColor3f(0,c,0)\r
468                                                 else:\r
469                                                         glColor3f(c,0,0)\r
470                                         if path.type == 'retract':\r
471                                                 glColor3f(0,c,c)\r
472                                         if c > 0.4 and path.type == 'extrude':\r
473                                                 for i in xrange(0, len(path.list)-1):\r
474                                                         v0 = path.list[i]\r
475                                                         v1 = path.list[i+1]\r
476 \r
477                                                         # Calculate line width from ePerDistance (needs layer thickness and filament diameter)\r
478                                                         dist = (v0 - v1).vsize()\r
479                                                         if dist > 0 and layerThickness > 0:\r
480                                                                 extrusionMMperDist = (v1.e - v0.e) / (v0 - v1).vsize()\r
481                                                                 lineWidth = extrusionMMperDist * filamentArea / layerThickness / 2\r
482 \r
483                                                         normal = (v0 - v1).cross(util3d.Vector3(0,0,1))\r
484                                                         normal.normalize()\r
485                                                         v2 = v0 + normal * lineWidth\r
486                                                         v3 = v1 + normal * lineWidth\r
487                                                         v0 = v0 - normal * lineWidth\r
488                                                         v1 = v1 - normal * lineWidth\r
489 \r
490                                                         glBegin(GL_QUADS)\r
491                                                         if path.pathType == 'FILL':     #Remove depth buffer fighting on infill/wall overlap\r
492                                                                 glVertex3f(v0.x, v0.y, v0.z - 0.02)\r
493                                                                 glVertex3f(v1.x, v1.y, v1.z - 0.02)\r
494                                                                 glVertex3f(v3.x, v3.y, v3.z - 0.02)\r
495                                                                 glVertex3f(v2.x, v2.y, v2.z - 0.02)\r
496                                                         else:\r
497                                                                 glVertex3f(v0.x, v0.y, v0.z - 0.01)\r
498                                                                 glVertex3f(v1.x, v1.y, v1.z - 0.01)\r
499                                                                 glVertex3f(v3.x, v3.y, v3.z - 0.01)\r
500                                                                 glVertex3f(v2.x, v2.y, v2.z - 0.01)\r
501                                                         glEnd()\r
502                                                 \r
503                                                 #for v in path['list']:\r
504                                                 #       glBegin(GL_TRIANGLE_FAN)\r
505                                                 #       glVertex3f(v.x, v.y, v.z - 0.001)\r
506                                                 #       for i in xrange(0, 16+1):\r
507                                                 #               if path['pathType'] == 'FILL':  #Remove depth buffer fighting on infill/wall overlap\r
508                                                 #                       glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.02)\r
509                                                 #               else:\r
510                                                 #                       glVertex3f(v.x + math.cos(math.pi*2/16*i) * lineWidth, v.y + math.sin(math.pi*2/16*i) * lineWidth, v.z - 0.01)\r
511                                                 #       glEnd()\r
512                                         else:\r
513                                                 glBegin(GL_LINE_STRIP)\r
514                                                 for v in path.list:\r
515                                                         glVertex3f(v.x, v.y, v.z)\r
516                                                 glEnd()\r
517                                 glEndList()\r
518                         if self.viewMode == "GCode" or self.viewMode == "Mixed":\r
519                                 glCallList(self.gcodeDisplayList)\r
520                 \r
521                 if self.parent.triangleMesh != None:\r
522                         if self.modelDisplayList == None:\r
523                                 self.modelDisplayList = glGenLists(1);\r
524                         if self.parent.modelDirty:\r
525                                 self.parent.modelDirty = False\r
526                                 multiX = int(profile.getProfileSetting('model_multiply_x'))\r
527                                 multiY = int(profile.getProfileSetting('model_multiply_y'))\r
528                                 modelSize = self.parent.triangleMesh.getCarveCornerMaximum() - self.parent.triangleMesh.getCarveCornerMinimum()\r
529                                 glNewList(self.modelDisplayList, GL_COMPILE)\r
530                                 glPushMatrix()\r
531                                 glTranslate(-(modelSize.x+10)*(multiX-1)/2,-(modelSize.y+10)*(multiY-1)/2, 0)\r
532                                 for mx in xrange(0, multiX):\r
533                                         for my in xrange(0, multiY):\r
534                                                 for face in self.parent.triangleMesh.faces:\r
535                                                         glPushMatrix()\r
536                                                         glTranslate((modelSize.x+10)*mx,(modelSize.y+10)*my, 0)\r
537                                                         glBegin(GL_TRIANGLES)\r
538                                                         v1 = self.parent.triangleMesh.vertexes[face.vertexIndexes[0]]\r
539                                                         v2 = self.parent.triangleMesh.vertexes[face.vertexIndexes[1]]\r
540                                                         v3 = self.parent.triangleMesh.vertexes[face.vertexIndexes[2]]\r
541                                                         glNormal3f(face.normal.x, face.normal.y, face.normal.z)\r
542                                                         glVertex3f(v1.x, v1.y, v1.z)\r
543                                                         glVertex3f(v2.x, v2.y, v2.z)\r
544                                                         glVertex3f(v3.x, v3.y, v3.z)\r
545                                                         glNormal3f(-face.normal.x, -face.normal.y, -face.normal.z)\r
546                                                         glVertex3f(v1.x, v1.y, v1.z)\r
547                                                         glVertex3f(v3.x, v3.y, v3.z)\r
548                                                         glVertex3f(v2.x, v2.y, v2.z)\r
549                                                         glEnd()\r
550                                                         glPopMatrix()\r
551                                 glPopMatrix()\r
552                                 glEndList()\r
553                         if self.viewMode == "Model - Transparent" or self.viewMode == "Mixed":\r
554                                 #If we want transparent, then first render a solid black model to remove the printer size lines.\r
555                                 if self.viewMode != "Mixed":\r
556                                         glDisable(GL_BLEND)\r
557                                         glDisable(GL_LIGHTING)\r
558                                         glColor3f(0,0,0)\r
559                                         glCallList(self.modelDisplayList)\r
560                                         glColor3f(1,1,1)\r
561                                 #After the black model is rendered, render the model again but now with lighting and no depth testing.\r
562                                 glDisable(GL_DEPTH_TEST)\r
563                                 glEnable(GL_LIGHTING)\r
564                                 glEnable(GL_BLEND)\r
565                                 glBlendFunc(GL_ONE, GL_ONE)\r
566                                 glEnable(GL_LIGHTING)\r
567                                 glCallList(self.modelDisplayList)\r
568                         elif self.viewMode == "Model - X-Ray":\r
569                                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)\r
570                                 glDisable(GL_DEPTH_TEST)\r
571                                 glEnable(GL_STENCIL_TEST);\r
572                                 glStencilFunc(GL_ALWAYS, 1, 1)\r
573                                 glStencilOp(GL_INCR, GL_INCR, GL_INCR)\r
574                                 glCallList(self.modelDisplayList)\r
575                                 glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);\r
576                                 \r
577                                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)\r
578                                 glStencilFunc(GL_EQUAL, 0, 1);\r
579                                 glColor(1, 1, 1)\r
580                                 glCallList(self.modelDisplayList)\r
581                                 glStencilFunc(GL_EQUAL, 1, 1);\r
582                                 glColor(1, 0, 0)\r
583                                 glCallList(self.modelDisplayList)\r
584 \r
585                                 glPushMatrix()\r
586                                 glLoadIdentity()\r
587                                 for i in xrange(2, 15, 2):\r
588                                         glStencilFunc(GL_EQUAL, i, 0xFF);\r
589                                         glColor(float(i)/10, float(i)/10, float(i)/5)\r
590                                         glBegin(GL_QUADS)\r
591                                         glVertex3f(-1000,-1000,-1)\r
592                                         glVertex3f( 1000,-1000,-1)\r
593                                         glVertex3f( 1000, 1000,-1)\r
594                                         glVertex3f(-1000, 1000,-1)\r
595                                         glEnd()\r
596                                 for i in xrange(1, 15, 2):\r
597                                         glStencilFunc(GL_EQUAL, i, 0xFF);\r
598                                         glColor(float(i)/10, 0, 0)\r
599                                         glBegin(GL_QUADS)\r
600                                         glVertex3f(-1000,-1000,-1)\r
601                                         glVertex3f( 1000,-1000,-1)\r
602                                         glVertex3f( 1000, 1000,-1)\r
603                                         glVertex3f(-1000, 1000,-1)\r
604                                         glEnd()\r
605                                 glPopMatrix()\r
606 \r
607                                 glDisable(GL_STENCIL_TEST);\r
608                                 glEnable(GL_DEPTH_TEST)\r
609                         elif self.viewMode == "Model - Normal":\r
610                                 glEnable(GL_LIGHTING)\r
611                                 glCallList(self.modelDisplayList)\r
612                         \r
613                         if self.viewMode == "Model - Normal" or self.viewMode == "Model - Transparent" or self.viewMode == "Model - X-Ray":\r
614                                 glDisable(GL_LIGHTING)\r
615                                 glDisable(GL_DEPTH_TEST)\r
616                                 glDisable(GL_BLEND)\r
617                                 glColor3f(1,0,0)\r
618                                 glTranslate(self.parent.machineCenter.x, self.parent.machineCenter.y, 0)\r
619                                 glBegin(GL_LINES)\r
620                                 for err in self.parent.errorList:\r
621                                         glVertex3f(err[0].x, err[0].y, err[0].z)\r
622                                         glVertex3f(err[1].x, err[1].y, err[1].z)\r
623                                 glEnd()\r
624                 \r
625                 glFlush()\r
626 \r
627         def InitGL(self):\r
628                 # set viewing projection\r
629                 glMatrixMode(GL_MODELVIEW)\r
630                 glLoadIdentity()\r
631                 size = self.GetSize()\r
632                 glViewport(0,0, size.GetWidth(), size.GetHeight())\r
633                 \r
634                 if self.viewMode == "Model - Transparent" or self.viewMode == "Mixed":\r
635                         glLightfv(GL_LIGHT0, GL_DIFFUSE,  [0.5, 0.4, 0.3, 1.0])\r
636                         glLightfv(GL_LIGHT0, GL_AMBIENT,  [0.1, 0.1, 0.1, 0.0])\r
637                 else:\r
638                         glLightfv(GL_LIGHT0, GL_DIFFUSE,  [1.0, 0.8, 0.6, 1.0])\r
639                         glLightfv(GL_LIGHT0, GL_AMBIENT,  [0.2, 0.2, 0.2, 0.0])\r
640                 glLightfv(GL_LIGHT0, GL_POSITION, [1.0, 1.0, 1.0, 0.0])\r
641 \r
642                 glEnable(GL_LIGHTING)\r
643                 glEnable(GL_LIGHT0)\r
644                 glEnable(GL_DEPTH_TEST)\r
645                 glEnable(GL_CULL_FACE)\r
646                 glDisable(GL_BLEND)\r
647 \r
648                 glClearColor(0.0, 0.0, 0.0, 1.0)\r
649                 glClearStencil(0)\r
650                 glClearDepth(1.0)\r
651 \r
652                 glMatrixMode(GL_PROJECTION)\r
653                 glLoadIdentity()\r
654                 aspect = float(self.GetSize().GetWidth()) / float(self.GetSize().GetHeight())\r
655                 if self.view3D:\r
656                         gluPerspective(90.0, aspect, 1.0, 1000.0)\r
657                 else:\r
658                         glOrtho(-self.zoom * aspect, self.zoom * aspect, -self.zoom, self.zoom, -1000.0, 1000.0)\r
659 \r
660                 glMatrixMode(GL_MODELVIEW)\r
661                 glLoadIdentity()\r
662                 if self.view3D:\r
663                         glTranslate(0,0,-self.zoom)\r
664                         glRotate(-self.pitch, 1,0,0)\r
665                         glRotate(self.yaw, 0,0,1)\r
666                         if self.parent.triangleMesh != None:\r
667                                 glTranslate(0,0,-self.parent.triangleMesh.getCarveCornerMaximum().z / 2)\r
668                 else:\r
669                         glTranslate(self.offsetX, self.offsetY, 0)\r
670 \r