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