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