1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2 from __future__ import absolute_import
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
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
25 class GLReferenceCounter(object):
34 return self._refCounter <= 0
36 class GLShader(GLReferenceCounter):
37 def __init__(self, vertexProgram, fragmentProgram):
38 super(GLShader, self).__init__()
39 self._vertexString = vertexProgram
40 self._fragmentString = fragmentProgram
42 self._vertexProgram = shaders.compileShader(vertexProgram, GL_VERTEX_SHADER)
43 self._fragmentProgram = shaders.compileShader(fragmentProgram, GL_FRAGMENT_SHADER)
44 self._program = shaders.compileProgram(self._vertexProgram, self._fragmentProgram)
45 except RuntimeError, e:
50 if self._program is not None:
51 shaders.glUseProgram(self._program)
54 shaders.glUseProgram(0)
57 if self._program is not None:
58 shaders.glDeleteShader(self._vertexProgram)
59 shaders.glDeleteShader(self._fragmentProgram)
60 glDeleteProgram(self._program)
63 def setUniform(self, name, value):
64 if self._program is not None:
65 glUniform1f(glGetUniformLocation(self._program, name), value)
68 return self._program is not None
70 def getVertexShader(self):
71 return self._vertexString
73 def getFragmentShader(self):
74 return self._fragmentString
77 if self._program is not None and bool(glDeleteProgram):
78 print "Shader was not properly released!"
80 class GLVBO(GLReferenceCounter):
81 def __init__(self, vertexArray, normalArray = None):
82 super(GLVBO, self).__init__()
83 self._buffer = glGenBuffers(1)
84 self._size = len(vertexArray)
85 self._hasNormals = normalArray is not None
86 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
88 glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray, normalArray), 1), GL_STATIC_DRAW)
90 glBufferData(GL_ARRAY_BUFFER, vertexArray, GL_STATIC_DRAW)
91 glBindBuffer(GL_ARRAY_BUFFER, 0)
93 def render(self, render_type = GL_TRIANGLES):
94 glEnableClientState(GL_VERTEX_ARRAY)
95 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
98 glEnableClientState(GL_NORMAL_ARRAY)
99 glVertexPointer(3, GL_FLOAT, 2*3*4, c_void_p(0))
100 glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4))
102 glVertexPointer(3, GL_FLOAT, 3*4, c_void_p(0))
104 batchSize = 996 #Warning, batchSize needs to be dividable by 4, 3 and 2
105 extraStartPos = int(self._size / batchSize) * batchSize
106 extraCount = self._size - extraStartPos
108 for i in xrange(0, int(self._size / batchSize)):
109 glDrawArrays(render_type, i * batchSize, batchSize)
110 glDrawArrays(render_type, extraStartPos, extraCount)
111 glBindBuffer(GL_ARRAY_BUFFER, 0)
113 glDisableClientState(GL_VERTEX_ARRAY)
115 glDisableClientState(GL_NORMAL_ARRAY)
118 if self._buffer is not None:
119 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
120 glBufferData(GL_ARRAY_BUFFER, None, GL_STATIC_DRAW)
121 glBindBuffer(GL_ARRAY_BUFFER, 0)
122 glDeleteBuffers(1, [self._buffer])
126 if self._buffer is not None and bool(glDeleteBuffers):
127 print "VBO was not properly released!"
129 def DrawMachine(machineSize):
130 glDisable(GL_LIGHTING)
131 glDisable(GL_CULL_FACE)
133 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
137 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
138 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
143 x1 = max(min(x1, sx), 0)
144 y1 = max(min(y1, sy), 0)
145 x2 = max(min(x2, sx), 0)
146 y2 = max(min(y2, sy), 0)
147 if (x & 1) == (y & 1):
148 glColor4ub(5, 171, 231, 127)
150 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
152 glVertex3f(x1, y1, -0.02)
153 glVertex3f(x2, y1, -0.02)
154 glVertex3f(x2, y2, -0.02)
155 glVertex3f(x1, y2, -0.02)
158 glEnable(GL_CULL_FACE)
160 if profile.getPreference('machine_type') == 'ultimaker':
162 glEnable(GL_LIGHTING)
163 glTranslate(100, 200, -1)
164 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8])
165 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5])
167 glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR)
170 if platformMesh is None:
172 platformMesh = meshLoader.loadMesh(getPathForMesh('ultimaker_platform.stl'))
177 DrawMesh(platformMesh)
179 glDisable(GL_LIGHTING)
180 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
182 glColor4ub(5, 171, 231, 64)
184 glVertex3f(0, 0, machineSize.z)
185 glVertex3f(0, machineSize.y, machineSize.z)
186 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
187 glVertex3f(machineSize.x, 0, machineSize.z)
190 glColor4ub(5, 171, 231, 96)
193 glVertex3f(0, 0, machineSize.z)
194 glVertex3f(machineSize.x, 0, machineSize.z)
195 glVertex3f(machineSize.x, 0, 0)
197 glVertex3f(0, machineSize.y, machineSize.z)
198 glVertex3f(0, machineSize.y, 0)
199 glVertex3f(machineSize.x, machineSize.y, 0)
200 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
203 glColor4ub(5, 171, 231, 128)
205 glVertex3f(0, 0, machineSize.z)
207 glVertex3f(0, machineSize.y, 0)
208 glVertex3f(0, machineSize.y, machineSize.z)
210 glVertex3f(machineSize.x, 0, 0)
211 glVertex3f(machineSize.x, 0, machineSize.z)
212 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
213 glVertex3f(machineSize.x, machineSize.y, 0)
218 #Draw the X/Y/Z indicator
238 glDisable(GL_DEPTH_TEST)
242 glTranslate(20, 0, 0)
243 noZ = ResetMatrixRotationAndScale()
244 glDrawStringCenter("X")
250 glTranslate(0, 20, 0)
251 glDrawStringCenter("Y")
258 glTranslate(0, 0, 20)
259 glDrawStringCenter("Z")
263 glEnable(GL_DEPTH_TEST)
265 def glDrawStringCenter(s):
267 glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
269 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
271 def glGetStringSize(s):
274 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
278 def glDrawStringLeft(s):
284 glTranslate(0, 18 * n, 0)
289 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
291 def glDrawStringRight(s):
293 glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
295 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
297 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
298 tx = float(texID % 4) / 4
299 ty = float(int(texID / 4)) / 8
309 glTranslatef(x, y, 0)
310 glEnable(GL_TEXTURE_2D)
312 glTexCoord2f(tx+tsx, ty)
316 glTexCoord2f(tx, ty+tsy)
318 glTexCoord2f(tx+tsx, ty+tsy)
323 def glDrawStretchedQuad(x, y, w, h, cornerSize, texID):
324 tx0 = float(texID % 4) / 4
325 ty0 = float(int(texID / 4)) / 8
326 tx1 = tx0 + 0.25 / 2.0
327 ty1 = ty0 + 0.125 / 2.0
332 glTranslatef(x, y, 0)
333 glEnable(GL_TEXTURE_2D)
336 glTexCoord2f(tx1, ty0)
337 glVertex2f( cornerSize, 0)
338 glTexCoord2f(tx0, ty0)
340 glTexCoord2f(tx0, ty1)
341 glVertex2f( 0, cornerSize)
342 glTexCoord2f(tx1, ty1)
343 glVertex2f( cornerSize, cornerSize)
345 glTexCoord2f(tx2, ty0)
347 glTexCoord2f(tx1, ty0)
348 glVertex2f( w - cornerSize, 0)
349 glTexCoord2f(tx1, ty1)
350 glVertex2f( w - cornerSize, cornerSize)
351 glTexCoord2f(tx2, ty1)
352 glVertex2f( w, cornerSize)
354 glTexCoord2f(tx1, ty1)
355 glVertex2f( cornerSize, h - cornerSize)
356 glTexCoord2f(tx0, ty1)
357 glVertex2f( 0, h - cornerSize)
358 glTexCoord2f(tx0, ty2)
360 glTexCoord2f(tx1, ty2)
361 glVertex2f( cornerSize, h)
363 glTexCoord2f(tx2, ty1)
364 glVertex2f( w, h - cornerSize)
365 glTexCoord2f(tx1, ty1)
366 glVertex2f( w - cornerSize, h - cornerSize)
367 glTexCoord2f(tx1, ty2)
368 glVertex2f( w - cornerSize, h)
369 glTexCoord2f(tx2, ty2)
373 glTexCoord2f(tx1, ty1)
374 glVertex2f( w-cornerSize, cornerSize)
375 glTexCoord2f(tx1, ty1)
376 glVertex2f( cornerSize, cornerSize)
377 glTexCoord2f(tx1, ty1)
378 glVertex2f( cornerSize, h-cornerSize)
379 glTexCoord2f(tx1, ty1)
380 glVertex2f( w-cornerSize, h-cornerSize)
383 glTexCoord2f(tx2, ty1)
384 glVertex2f( w, cornerSize)
385 glTexCoord2f(tx1, ty1)
386 glVertex2f( w-cornerSize, cornerSize)
387 glTexCoord2f(tx1, ty1)
388 glVertex2f( w-cornerSize, h-cornerSize)
389 glTexCoord2f(tx2, ty1)
390 glVertex2f( w, h-cornerSize)
393 glTexCoord2f(tx1, ty1)
394 glVertex2f( cornerSize, cornerSize)
395 glTexCoord2f(tx0, ty1)
396 glVertex2f( 0, cornerSize)
397 glTexCoord2f(tx0, ty1)
398 glVertex2f( 0, h-cornerSize)
399 glTexCoord2f(tx1, ty1)
400 glVertex2f( cornerSize, h-cornerSize)
403 glTexCoord2f(tx1, ty0)
404 glVertex2f( w-cornerSize, 0)
405 glTexCoord2f(tx1, ty0)
406 glVertex2f( cornerSize, 0)
407 glTexCoord2f(tx1, ty1)
408 glVertex2f( cornerSize, cornerSize)
409 glTexCoord2f(tx1, ty1)
410 glVertex2f( w-cornerSize, cornerSize)
413 glTexCoord2f(tx1, ty1)
414 glVertex2f( w-cornerSize, h-cornerSize)
415 glTexCoord2f(tx1, ty1)
416 glVertex2f( cornerSize, h-cornerSize)
417 glTexCoord2f(tx1, ty2)
418 glVertex2f( cornerSize, h)
419 glTexCoord2f(tx1, ty2)
420 glVertex2f( w-cornerSize, h)
423 glDisable(GL_TEXTURE_2D)
426 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
427 npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
428 npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
429 finalMatrix = npModelMatrix * npProjMatrix
430 finalMatrix = numpy.linalg.inv(finalMatrix)
432 viewport = map(float, viewport)
433 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))
434 vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
435 ret = list(vector)[0:3] / vector[3]
438 def convert3x3MatrixTo4x4(matrix):
439 return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
441 def loadGLTexture(filename):
442 tex = glGenTextures(1)
443 glBindTexture(GL_TEXTURE_2D, tex)
444 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
445 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
446 img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
447 rgbData = img.GetData()
448 alphaData = img.GetAlphaData()
449 if alphaData is not None:
451 for i in xrange(0, len(alphaData)):
452 data += rgbData[i*3:i*3+3] + alphaData[i]
453 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
455 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
458 def ResetMatrixRotationAndScale():
459 matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
463 scale2D = matrix[0][0]
474 if matrix[3][2] != 0.0:
475 matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100)
476 matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100)
479 matrix[0][0] = scale2D
480 matrix[1][1] = scale2D
481 matrix[2][2] = scale2D
485 glLoadMatrixf(matrix)
489 def DrawBox(vMin, vMax):
490 glBegin(GL_LINE_LOOP)
491 glVertex3f(vMin[0], vMin[1], vMin[2])
492 glVertex3f(vMax[0], vMin[1], vMin[2])
493 glVertex3f(vMax[0], vMax[1], vMin[2])
494 glVertex3f(vMin[0], vMax[1], vMin[2])
497 glBegin(GL_LINE_LOOP)
498 glVertex3f(vMin[0], vMin[1], vMax[2])
499 glVertex3f(vMax[0], vMin[1], vMax[2])
500 glVertex3f(vMax[0], vMax[1], vMax[2])
501 glVertex3f(vMin[0], vMax[1], vMax[2])
504 glVertex3f(vMin[0], vMin[1], vMin[2])
505 glVertex3f(vMin[0], vMin[1], vMax[2])
506 glVertex3f(vMax[0], vMin[1], vMin[2])
507 glVertex3f(vMax[0], vMin[1], vMax[2])
508 glVertex3f(vMax[0], vMax[1], vMin[2])
509 glVertex3f(vMax[0], vMax[1], vMax[2])
510 glVertex3f(vMin[0], vMax[1], vMin[2])
511 glVertex3f(vMin[0], vMax[1], vMax[2])
515 def DrawMeshOutline(mesh):
516 glEnable(GL_CULL_FACE)
517 glEnableClientState(GL_VERTEX_ARRAY);
518 glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
522 glPolygonMode(GL_BACK, GL_LINE)
523 glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount)
524 glPolygonMode(GL_BACK, GL_FILL)
527 glDisableClientState(GL_VERTEX_ARRAY)
530 def DrawMesh(mesh, insideOut = False):
531 glEnable(GL_CULL_FACE)
532 glEnableClientState(GL_VERTEX_ARRAY)
533 glEnableClientState(GL_NORMAL_ARRAY)
534 for m in mesh._meshList:
535 glVertexPointer(3, GL_FLOAT, 0, m.vertexes)
537 glNormalPointer(GL_FLOAT, 0, m.invNormal)
539 glNormalPointer(GL_FLOAT, 0, m.normal)
541 #Odd, drawing in batchs is a LOT faster then drawing it all at once.
542 batchSize = 999 #Warning, batchSize needs to be dividable by 3
543 extraStartPos = int(m.vertexCount / batchSize) * batchSize
544 extraCount = m.vertexCount - extraStartPos
547 for i in xrange(0, int(m.vertexCount / batchSize)):
548 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
549 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
553 glNormalPointer(GL_FLOAT, 0, m.normal)
555 glNormalPointer(GL_FLOAT, 0, m.invNormal)
556 for i in xrange(0, int(m.vertexCount / batchSize)):
557 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
558 extraStartPos = int(m.vertexCount / batchSize) * batchSize
559 extraCount = m.vertexCount - extraStartPos
560 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
563 glDisableClientState(GL_VERTEX_ARRAY)
564 glDisableClientState(GL_NORMAL_ARRAY)
567 def DrawMeshSteep(mesh, matrix, angle):
568 cosAngle = math.sin(angle / 180.0 * math.pi)
569 glDisable(GL_LIGHTING)
570 glDepthFunc(GL_EQUAL)
571 normals = (numpy.matrix(mesh.normal, copy = False) * matrix).getA()
572 for i in xrange(0, int(mesh.vertexCount), 3):
573 if normals[i][2] < -0.999999:
574 if mesh.vertexes[i + 0][2] > 0.01:
576 glBegin(GL_TRIANGLES)
577 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
578 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
579 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
581 elif normals[i][2] < -cosAngle:
582 glColor3f(-normals[i][2], 0, 0)
583 glBegin(GL_TRIANGLES)
584 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
585 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
586 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
588 elif normals[i][2] > 0.999999:
589 if mesh.vertexes[i + 0][2] > 0.01:
591 glBegin(GL_TRIANGLES)
592 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
593 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
594 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
596 elif normals[i][2] > cosAngle:
597 glColor3f(normals[i][2], 0, 0)
598 glBegin(GL_TRIANGLES)
599 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
600 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
601 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
605 def DrawGCodeLayer(layer, drawQuick = True):
606 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
607 filamentArea = math.pi * filamentRadius * filamentRadius
608 lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
611 fillColorCycle = [[0.5, 0.5, 0.0, 1], [0.0, 0.5, 0.5, 1], [0.5, 0.0, 0.5, 1]]
612 moveColor = [0, 0, 1, 0.5]
613 retractColor = [1, 0, 0.5, 0.5]
614 supportColor = [0, 1, 1, 1]
615 extrudeColor = [[1, 0, 0, 1], [0, 1, 1, 1], [1, 1, 0, 1], [1, 0, 1, 1]]
616 innerWallColor = [0, 1, 0, 1]
617 skirtColor = [0, 0.5, 0.5, 1]
618 prevPathWasRetract = False
620 glDisable(GL_CULL_FACE)
622 if path.type == 'move':
623 if prevPathWasRetract:
630 if path.type == 'extrude':
631 if path.pathType == 'FILL':
632 c = fillColorCycle[fillCycle]
633 fillCycle = (fillCycle + 1) % len(fillColorCycle)
634 elif path.pathType == 'WALL-INNER':
637 elif path.pathType == 'SUPPORT':
639 elif path.pathType == 'SKIRT':
642 c = extrudeColor[path.extruder]
643 if path.type == 'retract':
645 if path.type == 'extrude' and not drawQuick:
648 for i in xrange(0, len(path.points) - 1):
650 v1 = path.points[i + 1]
652 # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
653 dist = (v0 - v1).vsize()
654 if dist > 0 and path.layerThickness > 0:
655 extrusionMMperDist = (v1.e - v0.e) / dist
656 lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply
658 drawLength += (v0 - v1).vsize()
659 normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1))
662 vv2 = v0 + normal * lineWidth
663 vv3 = v1 + normal * lineWidth
664 vv0 = v0 - normal * lineWidth
665 vv1 = v1 - normal * lineWidth
669 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
670 glVertex3f(vv1.x, vv1.y, vv1.z - zOffset)
671 glVertex3f(vv3.x, vv3.y, vv3.z - zOffset)
672 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
674 if prevNormal is not None:
675 n = (normal + prevNormal)
677 vv4 = v0 + n * lineWidth
678 vv5 = v0 - n * lineWidth
681 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
682 glVertex3f(vv4.x, vv4.y, vv4.z - zOffset)
683 glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset)
684 glVertex3f(v0.x, v0.y, v0.z - zOffset)
686 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
687 glVertex3f(vv5.x, vv5.y, vv5.z - zOffset)
688 glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset)
689 glVertex3f(v0.x, v0.y, v0.z - zOffset)
697 glBegin(GL_TRIANGLES)
698 for v in path.points:
699 glVertex3f(v[0], v[1], v[2])
702 if not path.type == 'move':
703 prevPathWasRetract = False
704 #if path.type == 'retract' and path.points[0].almostEqual(path.points[-1]):
705 # prevPathWasRetract = True
706 glEnable(GL_CULL_FACE)