def normalise(self, tessellate=None): """Normalise this polygon to a set of triangle vertices tesselate -- callable which can accept a Polygon instance and return a list of vertices representing a tesselated polygon triangle-set. (And only a triangle- set, no triangle-fans or triangle-strips). """ if self.normalised is not None: return self.normalised if tessellate is None: tessellate = polygontessellator.PolygonTessellator().tessellate if (len(self) > 4 or (len(self) == 4 and utilities.coplanar([v.point for v in self]))): ### we would like to cache this partial solution somewhere ## but at the moment I don't see how to do it usefully, ## given that there may be a different tessellation required ## if the vertex positions change ## if not self.ccw: ## self.reverse() self[:] = tessellate(self) elif len(self) == 4: a, b, c, d = self self[:] = [a, b, c, a, c, d] elif len(self) < 3: log.info( repr( DegeneratePolygon( self, self.node, """Less than 3 vertices specified in indices""", ))) ### doing a sanity check an excluding degenerate polygons exclusively... return self.checkVertices()
def tessellate(self, polygons=None, sources=None): """Tessellate our arrays into triangle-only arrays The return value is a list of triangle vertices, with each vertex represented by a Vertex instance (see below). In addition to tessellation, this function is responsible for turning the index-format structures into a set of polygons (i.e. identifying the individual polygons within the indexed face set). """ # check to see that there's something to do if (not len(self.target.coordIndex)) or (not self.target.coord) or ( not len(self.target.coord.point)): return [] tessellate = polygontessellator.PolygonTessellator().tessellate vertices = [] if polygons is None: polygons = self.polygons(sources=sources) for polygon in polygons: polygon.normalise(tessellate) vertices.extend(polygon) return vertices
class SolidGlyph(OutlineGlyph): """Glyph composed of triangle geometry""" tess = polygontessellator.PolygonTessellator() DEBUG_RENDER_EXTRUSION_NORMALS = 0 DEBUG_RENDER_OUTLINES = 0 def renderExtrusion(self, scale=400.0, distance=1.0): """Render the extrusion of the font backward by distance (using GLE) This is pretty simple, save that there's no good way to specify the normals for the edge properly :( , basically anything we do is going to look weird because we can only specify one normal for each vertex on the outline. """ gleSetJoinStyle(TUBE_CONTOUR_CLOSED | TUBE_JN_RAW) ## TTF defines everything in clockwise order, GLE assumes CCW, ## so need to reverse the rendering mode... glFrontFace(GL_CW) try: data = self._calculateExtrusionData(scale) for points, normals in data: gleExtrusion( points, normals, array((0, 1, 0), 'd'), # up array([(0, 0, 1), (0, 0, 0), (0, 0, -distance), (0, 0, -distance - 1.0)], 'd'), # spine None, ) if __debug__: if self.DEBUG_RENDER_EXTRUSION_NORMALS: glBegin(GL_LINES) try: for ((x, y), (dx, dy)) in zip(points, normals): glColor(0, 1, 0) glVertex2d(x, y) glColor(1, 0, 0) glVertex2d(x + dx, y + dy) finally: glEnd() finally: glFrontFace(GL_CCW) return data def _calculateExtrusionData(self, scale=400.0): """Calculate extrusion points + normals for the glyph""" def calculateNormal(first, second, third): """Calculate an approximate 2D normal for a 3-point set""" (x1, y1) = first (x2, y2) = second (x3, y3) = third x, y = -(y3 - y1), (x3 - x1) l = sqrt(x * x + y * y) if l == 0: # a null 3-point set, we'll skip this point & normal return None return x / l, y / l result = [] for contour in self.outlines: contour = asarray(contour, 'd') / scale points = [] clen = len(contour) normals = [] for i in range(clen): last = contour[((i - 1) + clen) % clen] current = contour[i] next = contour[((i + 1) + clen) % clen] normal = calculateNormal(last, current, next) if normal: normals.append(normal) points.append(current) result.append((asarray(points, 'd'), asarray(normals, 'd'))) return result def renderCap(self, scale=400.0, front=1): """The cap is generated with GLU tessellation routines... """ if self.DEBUG_RENDER_CONTOUR_HULLS: self.renderContours(scale) if self.DEBUG_RENDER_CONTROL_POINTS: self.renderControlPoints(scale) contours = self._calculateCapData(scale) if front: glNormal(0, 0, 1) glFrontFace(GL_CCW) else: glNormal(0, 0, -1) glFrontFace(GL_CW) try: try: glEnableClientState(GL_VERTEX_ARRAY) for type, vertices in contours: glVertexPointerd(vertices) glDrawArrays(type, 0, len(vertices)) finally: glDisableClientState(GL_VERTEX_ARRAY) finally: glFrontFace(GL_CCW) return contours def _calculateCapData(self, scale=400.0): """Calculate the tessellated data-sets for this glyph""" vertices = [[ vertex.Vertex(point=(x / scale, y / scale, 0.0)) for (x, y) in outline ] for outline in self.outlines] gluTessNormal(self.tess.controller, 0, 0, 1.0) contours = self.tess.tessContours(vertices, forceTriangles=0) return [(t, asarray([v.point for v in vertices], 'd')) for t, vertices in contours]
def OnInit(self): self.tess = polygontessellator.PolygonTessellator() glEnable(GL_COLOR_MATERIAL)
class SolidGlyph( OutlineGlyph ): """Glyph composed of triangle geometry""" tess = polygontessellator.PolygonTessellator() DEBUG_RENDER_EXTRUSION_NORMALS = 0 DEBUG_RENDER_OUTLINES = 0 def renderExtrusion( self, scale=400.0, distance=1.0): """Render the extrusion of the font backward by distance (using GLE) This is pretty simple, save that there's no good way to specify the normals for the edge properly :( , basically anything we do is going to look weird because we can only specify one normal for each vertex on the outline. """ gleSetJoinStyle( TUBE_CONTOUR_CLOSED|TUBE_JN_RAW ) ## TTF defines everything in clockwise order, GLE assumes CCW, ## so need to reverse the rendering mode... glFrontFace( GL_CW ) try: data = self._calculateExtrusionData( scale ) for points, normals in data: gleExtrusion( points, normals, array((0,1,0),'d'), # up array([(0,0,1),(0,0,0),(0,0,-distance),(0,0,-distance-1.0)],'d'), # spine None, ) if __debug__: if self.DEBUG_RENDER_EXTRUSION_NORMALS: glBegin( GL_LINES ) try: for ((x,y), (dx,dy)) in map( None, points, normals ): glColor( 0,1,0) glVertex2d( x,y ) glColor( 1,0,0) glVertex2d( x+dx,y+dy ) finally: glEnd() finally: glFrontFace( GL_CCW ) return data def _calculateExtrusionData( self, scale = 400.0 ): """Calculate extrusion points + normals for the glyph""" def calculateNormal((x1,y1),(x2,y2),(x3,y3)): """Calculate an approximate 2D normal for a 3-point set""" x,y = -(y3-y1),(x3-x1) l = sqrt(x*x+y*y) if l == 0: # a null 3-point set, we'll skip this point & normal return None return x/l,y/l result = [] for contour in self.outlines: contour = asarray( contour, 'd' )/scale points = [] clen = len(contour) normals = [] for i in range( clen): last = contour[((i-1)+clen)%clen] current = contour[i] next = contour[((i+1)+clen)%clen] normal = calculateNormal(last, current, next) if normal: normals.append( normal ) points.append( current ) result.append( (asarray(points,'d'), asarray(normals,'d')) ) return result