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