chiark / gitweb /
Fix crash bug.
[cura.git] / Cura / gui / util / opengl.py
1 # coding=utf-8
2 from __future__ import absolute_import
3
4 import math
5 import numpy
6 import wx
7 import time
8
9 from Cura.util import meshLoader
10 from Cura.util import util3d
11 from Cura.util import profile
12 from Cura.util.resources import getPathForMesh, getPathForImage
13
14 import OpenGL
15
16 OpenGL.ERROR_CHECKING = False
17 from OpenGL.GLUT import *
18 from OpenGL.GLU import *
19 from OpenGL.GL import *
20 from OpenGL.GL import shaders
21 glutInit()
22
23 platformMesh = None
24
25 class GLShader(object):
26         def __init__(self, vertexProgram, fragmentProgram):
27                 try:
28                         self._vertexProgram = shaders.compileShader(vertexProgram, GL_VERTEX_SHADER)
29                         self._fragmentProgram = shaders.compileShader(fragmentProgram, GL_FRAGMENT_SHADER)
30                         self._program = shaders.compileProgram(self._vertexProgram, self._fragmentProgram)
31                 except RuntimeError, e:
32                         print "Shader error:"
33                         print str(e)
34                         self._program = None
35
36         def bind(self):
37                 if self._program is not None:
38                         shaders.glUseProgram(self._program)
39
40         def unbind(self):
41                 shaders.glUseProgram(0)
42
43         def release(self):
44                 if self._program is not None:
45                         shaders.glDeleteShader(self._vertexProgram)
46                         shaders.glDeleteShader(self._fragmentProgram)
47                         glDeleteProgram(self._program)
48                         self._program = None
49
50         def setUniform(self, name, value):
51                 if self._program is not None:
52                         glUniform1f(glGetUniformLocation(self._program, name), value)
53
54         def __del__(self):
55                 if self._program is not None and bool(glDeleteProgram):
56                         print "Shader was not properly released!"
57
58 class GLVBO(object):
59         def __init__(self, vertexArray, normalArray):
60                 self._buffer = glGenBuffers(1)
61                 self._size = len(vertexArray)
62                 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
63                 glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray, normalArray), 1), GL_STATIC_DRAW)
64                 glBindBuffer(GL_ARRAY_BUFFER, 0)
65
66         def render(self):
67                 glEnableClientState(GL_VERTEX_ARRAY)
68                 glEnableClientState(GL_NORMAL_ARRAY)
69                 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
70                 glVertexPointer(3, GL_FLOAT, 2*3*4, c_void_p(0))
71                 glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4))
72
73                 batchSize = 999    #Warning, batchSize needs to be dividable by 3
74                 extraStartPos = int(self._size / batchSize) * batchSize
75                 extraCount = self._size - extraStartPos
76
77                 for i in xrange(0, int(self._size / batchSize)):
78                         glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
79                 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
80                 glBindBuffer(GL_ARRAY_BUFFER, 0)
81                 glDisableClientState(GL_VERTEX_ARRAY)
82                 glDisableClientState(GL_NORMAL_ARRAY)
83
84         def release(self):
85                 if self._buffer is not None:
86                         glDeleteBuffers(1, [self._buffer])
87                         self._buffer = None
88
89         def __del__(self):
90                 if self._buffer is not None and bool(glDeleteBuffers):
91                         print "VBO was not properly released!"
92
93 def DrawMachine(machineSize):
94         glDisable(GL_LIGHTING)
95         glDisable(GL_CULL_FACE)
96         glEnable(GL_BLEND)
97         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
98
99         sx = machineSize.x
100         sy = machineSize.y
101         for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
102                 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
103                         x1 = sx/2+x * 10
104                         x2 = x1 + 10
105                         y1 = sx/2+y * 10
106                         y2 = y1 + 10
107                         x1 = max(min(x1, sx), 0)
108                         y1 = max(min(y1, sy), 0)
109                         x2 = max(min(x2, sx), 0)
110                         y2 = max(min(y2, sy), 0)
111                         if (x & 1) == (y & 1):
112                                 glColor4ub(5, 171, 231, 127)
113                         else:
114                                 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
115                         glBegin(GL_QUADS)
116                         glVertex3f(x1, y1, -0.02)
117                         glVertex3f(x2, y1, -0.02)
118                         glVertex3f(x2, y2, -0.02)
119                         glVertex3f(x1, y2, -0.02)
120                         glEnd()
121
122         glEnable(GL_CULL_FACE)
123
124         if profile.getPreference('machine_type') == 'ultimaker':
125                 glPushMatrix()
126                 glEnable(GL_LIGHTING)
127                 glTranslate(100, 200, -1)
128                 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8])
129                 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5])
130                 glEnable(GL_BLEND)
131                 glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR)
132
133                 global platformMesh
134                 if platformMesh is None:
135                         try:
136                                 platformMesh = meshLoader.loadMesh(getPathForMesh('ultimaker_platform.stl'))
137                         except:
138                                 platformMesh = False
139
140                 if platformMesh:
141                         DrawMesh(platformMesh)
142                 glPopMatrix()
143                 glDisable(GL_LIGHTING)
144                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
145
146         glColor4ub(5, 171, 231, 64)
147         glBegin(GL_QUADS)
148         glVertex3f(0, 0, machineSize.z)
149         glVertex3f(0, machineSize.y, machineSize.z)
150         glVertex3f(machineSize.x, machineSize.y, machineSize.z)
151         glVertex3f(machineSize.x, 0, machineSize.z)
152         glEnd()
153
154         glColor4ub(5, 171, 231, 96)
155         glBegin(GL_QUADS)
156         glVertex3f(0, 0, 0)
157         glVertex3f(0, 0, machineSize.z)
158         glVertex3f(machineSize.x, 0, machineSize.z)
159         glVertex3f(machineSize.x, 0, 0)
160
161         glVertex3f(0, machineSize.y, machineSize.z)
162         glVertex3f(0, machineSize.y, 0)
163         glVertex3f(machineSize.x, machineSize.y, 0)
164         glVertex3f(machineSize.x, machineSize.y, machineSize.z)
165         glEnd()
166
167         glColor4ub(5, 171, 231, 128)
168         glBegin(GL_QUADS)
169         glVertex3f(0, 0, machineSize.z)
170         glVertex3f(0, 0, 0)
171         glVertex3f(0, machineSize.y, 0)
172         glVertex3f(0, machineSize.y, machineSize.z)
173
174         glVertex3f(machineSize.x, 0, 0)
175         glVertex3f(machineSize.x, 0, machineSize.z)
176         glVertex3f(machineSize.x, machineSize.y, machineSize.z)
177         glVertex3f(machineSize.x, machineSize.y, 0)
178         glEnd()
179
180         glDisable(GL_BLEND)
181
182         #Draw the X/Y/Z indicator
183         glPushMatrix()
184         glTranslate(5, 5, 2)
185         glLineWidth(2)
186         glColor3f(0.5, 0, 0)
187         glBegin(GL_LINES)
188         glVertex3f(0, 0, 0)
189         glVertex3f(20, 0, 0)
190         glEnd()
191         glColor3f(0, 0.5, 0)
192         glBegin(GL_LINES)
193         glVertex3f(0, 0, 0)
194         glVertex3f(0, 20, 0)
195         glEnd()
196         glColor3f(0, 0, 0.5)
197         glBegin(GL_LINES)
198         glVertex3f(0, 0, 0)
199         glVertex3f(0, 0, 20)
200         glEnd()
201
202         glDisable(GL_DEPTH_TEST)
203         #X
204         glColor3f(1, 0, 0)
205         glPushMatrix()
206         glTranslate(20, 0, 0)
207         noZ = ResetMatrixRotationAndScale()
208         glDrawStringCenter("X")
209         glPopMatrix()
210
211         #Y
212         glColor3f(0, 1, 0)
213         glPushMatrix()
214         glTranslate(0, 20, 0)
215         glDrawStringCenter("Y")
216         glPopMatrix()
217
218         #Z
219         if not noZ:
220                 glColor3f(0, 0, 1)
221                 glPushMatrix()
222                 glTranslate(0, 0, 20)
223                 glDrawStringCenter("Z")
224                 glPopMatrix()
225
226         glPopMatrix()
227         glEnable(GL_DEPTH_TEST)
228
229 def glDrawStringCenter(s):
230         glRasterPos2f(0, 0)
231         glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
232         for c in s:
233                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
234
235 def glGetStringSize(s):
236         width = 0
237         for c in s:
238                 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
239         height = 18
240         return width, height
241
242 def glDrawStringLeft(s):
243         glRasterPos2f(0, 0)
244         for c in s:
245                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
246
247 def glDrawStringRight(s):
248         glRasterPos2f(0, 0)
249         glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
250         for c in s:
251                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
252
253 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
254         tx = float(texID % 4) / 4
255         ty = float(int(texID / 4)) / 8
256         tsx = 0.25
257         tsy = 0.125
258         if mirror & 1:
259                 tx += tsx
260                 tsx = -tsx
261         if mirror & 2:
262                 ty += tsy
263                 tsy = -tsy
264         glPushMatrix()
265         glTranslatef(x, y, 0)
266         glEnable(GL_TEXTURE_2D)
267         glBegin(GL_QUADS)
268         glTexCoord2f(tx+tsx, ty)
269         glVertex2f(w, 0)
270         glTexCoord2f(tx, ty)
271         glVertex2f(0, 0)
272         glTexCoord2f(tx, ty+tsy)
273         glVertex2f(0, h)
274         glTexCoord2f(tx+tsx, ty+tsy)
275         glVertex2f(w, h)
276         glEnd()
277         glPopMatrix()
278
279 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
280         npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
281         npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
282         finalMatrix = npModelMatrix * npProjMatrix
283         finalMatrix = numpy.linalg.inv(finalMatrix)
284
285         viewport = map(float, viewport)
286         vector = numpy.array([(winx - viewport[0]) / viewport[2] * 2.0 - 1.0, (winy - viewport[1]) / viewport[3] * 2.0 - 1.0, winz * 2.0 - 1.0, 1]).reshape((1,4))
287         vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
288         ret = list(vector)[0:3] / vector[3]
289         return ret
290
291 def convert3x3MatrixTo4x4(matrix):
292         return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
293
294 def loadGLTexture(filename):
295         tex = glGenTextures(1)
296         glBindTexture(GL_TEXTURE_2D, tex)
297         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
298         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
299         img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
300         rgbData = img.GetData()
301         alphaData = img.GetAlphaData()
302         if alphaData is not None:
303                 data = ''
304                 for i in xrange(0, len(alphaData)):
305                         data += rgbData[i*3:i*3+3] + alphaData[i]
306                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
307         else:
308                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
309         return tex
310
311 def ResetMatrixRotationAndScale():
312         matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
313         noZ = False
314         if matrix[3][2] > 0:
315                 return False
316         scale2D = matrix[0][0]
317         matrix[0][0] = 1.0
318         matrix[1][0] = 0.0
319         matrix[2][0] = 0.0
320         matrix[0][1] = 0.0
321         matrix[1][1] = 1.0
322         matrix[2][1] = 0.0
323         matrix[0][2] = 0.0
324         matrix[1][2] = 0.0
325         matrix[2][2] = 1.0
326
327         if matrix[3][2] != 0.0:
328                 matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100)
329                 matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100)
330                 matrix[3][2] = -100
331         else:
332                 matrix[0][0] = scale2D
333                 matrix[1][1] = scale2D
334                 matrix[2][2] = scale2D
335                 matrix[3][2] = -100
336                 noZ = True
337
338         glLoadMatrixf(matrix)
339         return noZ
340
341
342 def DrawBox(vMin, vMax):
343         glBegin(GL_LINE_LOOP)
344         glVertex3f(vMin[0], vMin[1], vMin[2])
345         glVertex3f(vMax[0], vMin[1], vMin[2])
346         glVertex3f(vMax[0], vMax[1], vMin[2])
347         glVertex3f(vMin[0], vMax[1], vMin[2])
348         glEnd()
349
350         glBegin(GL_LINE_LOOP)
351         glVertex3f(vMin[0], vMin[1], vMax[2])
352         glVertex3f(vMax[0], vMin[1], vMax[2])
353         glVertex3f(vMax[0], vMax[1], vMax[2])
354         glVertex3f(vMin[0], vMax[1], vMax[2])
355         glEnd()
356         glBegin(GL_LINES)
357         glVertex3f(vMin[0], vMin[1], vMin[2])
358         glVertex3f(vMin[0], vMin[1], vMax[2])
359         glVertex3f(vMax[0], vMin[1], vMin[2])
360         glVertex3f(vMax[0], vMin[1], vMax[2])
361         glVertex3f(vMax[0], vMax[1], vMin[2])
362         glVertex3f(vMax[0], vMax[1], vMax[2])
363         glVertex3f(vMin[0], vMax[1], vMin[2])
364         glVertex3f(vMin[0], vMax[1], vMax[2])
365         glEnd()
366
367
368 def DrawMeshOutline(mesh):
369         glEnable(GL_CULL_FACE)
370         glEnableClientState(GL_VERTEX_ARRAY);
371         glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
372
373         glCullFace(GL_FRONT)
374         glLineWidth(3)
375         glPolygonMode(GL_BACK, GL_LINE)
376         glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount)
377         glPolygonMode(GL_BACK, GL_FILL)
378         glCullFace(GL_BACK)
379
380         glDisableClientState(GL_VERTEX_ARRAY)
381
382
383 def DrawMesh(mesh, insideOut = False):
384         glEnable(GL_CULL_FACE)
385         glEnableClientState(GL_VERTEX_ARRAY)
386         glEnableClientState(GL_NORMAL_ARRAY)
387         for m in mesh._meshList:
388                 glVertexPointer(3, GL_FLOAT, 0, m.vertexes)
389                 if insideOut:
390                         glNormalPointer(GL_FLOAT, 0, m.invNormal)
391                 else:
392                         glNormalPointer(GL_FLOAT, 0, m.normal)
393
394                 #Odd, drawing in batchs is a LOT faster then drawing it all at once.
395                 batchSize = 999    #Warning, batchSize needs to be dividable by 3
396                 extraStartPos = int(m.vertexCount / batchSize) * batchSize
397                 extraCount = m.vertexCount - extraStartPos
398
399                 glCullFace(GL_BACK)
400                 for i in xrange(0, int(m.vertexCount / batchSize)):
401                         glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
402                 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
403
404                 glCullFace(GL_FRONT)
405                 if insideOut:
406                         glNormalPointer(GL_FLOAT, 0, m.normal)
407                 else:
408                         glNormalPointer(GL_FLOAT, 0, m.invNormal)
409                 for i in xrange(0, int(m.vertexCount / batchSize)):
410                         glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
411                 extraStartPos = int(m.vertexCount / batchSize) * batchSize
412                 extraCount = m.vertexCount - extraStartPos
413                 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
414                 glCullFace(GL_BACK)
415
416         glDisableClientState(GL_VERTEX_ARRAY)
417         glDisableClientState(GL_NORMAL_ARRAY)
418
419
420 def DrawMeshSteep(mesh, matrix, angle):
421         cosAngle = math.sin(angle / 180.0 * math.pi)
422         glDisable(GL_LIGHTING)
423         glDepthFunc(GL_EQUAL)
424         normals = (numpy.matrix(mesh.normal, copy = False) * matrix).getA()
425         for i in xrange(0, int(mesh.vertexCount), 3):
426                 if normals[i][2] < -0.999999:
427                         if mesh.vertexes[i + 0][2] > 0.01:
428                                 glColor3f(0.5, 0, 0)
429                                 glBegin(GL_TRIANGLES)
430                                 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
431                                 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
432                                 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
433                                 glEnd()
434                 elif normals[i][2] < -cosAngle:
435                         glColor3f(-normals[i][2], 0, 0)
436                         glBegin(GL_TRIANGLES)
437                         glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
438                         glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
439                         glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
440                         glEnd()
441                 elif normals[i][2] > 0.999999:
442                         if mesh.vertexes[i + 0][2] > 0.01:
443                                 glColor3f(0.5, 0, 0)
444                                 glBegin(GL_TRIANGLES)
445                                 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
446                                 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
447                                 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
448                                 glEnd()
449                 elif normals[i][2] > cosAngle:
450                         glColor3f(normals[i][2], 0, 0)
451                         glBegin(GL_TRIANGLES)
452                         glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
453                         glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
454                         glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
455                         glEnd()
456         glDepthFunc(GL_LESS)
457
458
459 def DrawGCodeLayer(layer, drawQuick = True):
460         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
461         filamentArea = math.pi * filamentRadius * filamentRadius
462         lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
463
464         fillCycle = 0
465         fillColorCycle = [[0.5, 0.5, 0.0, 1], [0.0, 0.5, 0.5, 1], [0.5, 0.0, 0.5, 1]]
466         moveColor = [0, 0, 1, 0.5]
467         retractColor = [1, 0, 0.5, 0.5]
468         supportColor = [0, 1, 1, 1]
469         extrudeColor = [[1, 0, 0, 1], [0, 1, 1, 1], [1, 1, 0, 1], [1, 0, 1, 1]]
470         innerWallColor = [0, 1, 0, 1]
471         skirtColor = [0, 0.5, 0.5, 1]
472         prevPathWasRetract = False
473
474         glDisable(GL_CULL_FACE)
475         for path in layer:
476                 if path.type == 'move':
477                         if prevPathWasRetract:
478                                 c = retractColor
479                         else:
480                                 c = moveColor
481                         if drawQuick:
482                                 continue
483                 zOffset = 0.01
484                 if path.type == 'extrude':
485                         if path.pathType == 'FILL':
486                                 c = fillColorCycle[fillCycle]
487                                 fillCycle = (fillCycle + 1) % len(fillColorCycle)
488                                 if drawQuick:
489                                         continue
490                         elif path.pathType == 'WALL-INNER':
491                                 c = innerWallColor
492                                 zOffset = 0.02
493                         elif path.pathType == 'SUPPORT':
494                                 c = supportColor
495                         elif path.pathType == 'SKIRT':
496                                 c = skirtColor
497                         else:
498                                 c = extrudeColor[path.extruder]
499                 if path.type == 'retract':
500                         c = retractColor
501                 if path.type == 'extrude' and not drawQuick:
502                         drawLength = 0.0
503                         prevNormal = None
504                         for i in xrange(0, len(path.list) - 1):
505                                 v0 = path.list[i]
506                                 v1 = path.list[i + 1]
507
508                                 # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
509                                 dist = (v0 - v1).vsize()
510                                 if dist > 0 and path.layerThickness > 0:
511                                         extrusionMMperDist = (v1.e - v0.e) / dist
512                                         lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply
513
514                                 drawLength += (v0 - v1).vsize()
515                                 normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1))
516                                 normal.normalize()
517
518                                 vv2 = v0 + normal * lineWidth
519                                 vv3 = v1 + normal * lineWidth
520                                 vv0 = v0 - normal * lineWidth
521                                 vv1 = v1 - normal * lineWidth
522
523                                 glBegin(GL_QUADS)
524                                 glColor4fv(c)
525                                 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
526                                 glVertex3f(vv1.x, vv1.y, vv1.z - zOffset)
527                                 glVertex3f(vv3.x, vv3.y, vv3.z - zOffset)
528                                 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
529                                 glEnd()
530                                 if prevNormal is not None:
531                                         n = (normal + prevNormal)
532                                         n.normalize()
533                                         vv4 = v0 + n * lineWidth
534                                         vv5 = v0 - n * lineWidth
535                                         glBegin(GL_QUADS)
536                                         glColor4fv(c)
537                                         glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
538                                         glVertex3f(vv4.x, vv4.y, vv4.z - zOffset)
539                                         glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset)
540                                         glVertex3f(v0.x, v0.y, v0.z - zOffset)
541
542                                         glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
543                                         glVertex3f(vv5.x, vv5.y, vv5.z - zOffset)
544                                         glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset)
545                                         glVertex3f(v0.x, v0.y, v0.z - zOffset)
546                                         glEnd()
547
548                                 prevNormal = normal
549                                 prevVv1 = vv1
550                                 prevVv3 = vv3
551                 else:
552                         glBegin(GL_LINE_STRIP)
553                         glColor4fv(c)
554                         for v in path.list:
555                                 glVertex3f(v.x, v.y, v.z)
556                         glEnd()
557                 if not path.type == 'move':
558                         prevPathWasRetract = False
559                 if path.type == 'retract' and path.list[0].almostEqual(path.list[-1]):
560                         prevPathWasRetract = True
561         glEnable(GL_CULL_FACE)