Exemple #1
0
void QChain::cutCircle(Circle circle) {
  if (m_vertices.size() == 0) return;
  circle.setCenter(circle.pos());

  QPainterPath cr;
  cr.addEllipse(circle.x - circle.r, circle.y - circle.r, 2 * circle.r,
                2 * circle.r);

  QPolygonF polygon;
  for (QPointF p : m_vertices) polygon.append(p);
  QPainterPath chain;
  chain.addPolygon(polygon);

  if (!chain.intersects(cr)) return;

  chain = chain.subtracted(cr);

  for (const QPolygonF &poly : chain.toSubpathPolygons()) {
    std::vector<Vector2d> pts(poly.begin(), poly.end() - 1);

    if (std::fabs(Geometry::area(pts.begin(), pts.end())) > 5.f) {
      auto chain = std::make_unique<QChain>(world());
      chain->setVertices(std::vector<QPointF>(pts.begin(), pts.end()));
      chain->initializeLater(world());

      world()->itemSet()->addBody(std::move(chain));
    }
  }

  m_vertices.clear();
  destroyLater();
}
TrackActivities ViewInteractionItemDrawer::AddTrackingVertex(
        const QPointF &src, bool force)
{
    QPainterPath p = _vitem->path();
    QList<QPolygonF> ps = p.toSubpathPolygons();
    Q_ASSERT(ps.size() <= 1);
    Q_ASSERT(_vitem->_feature != nullptr);
    Q_ASSERT(_vitem->_feature->paths().size() == 1);
    _vitem->ClearTempItems();
    TrackActivities ret;
    int n = _vitem->_feature->get_geometry(0).size();
    if (n == 0) {
        ret = TryTrackingMulti(src);
        if (force && ret.is_tracked()) {
            _vitem->add_temp_vertex(ret.closest()._vpos);
        }
    } else {
        Q_ASSERT(p.elementCount() == n || p.elementCount() == n + 1);
        if (p.elementCount() == n) {
            p.lineTo(src);
        } else {
            ret = TryTrackingMulti(src);
            if (force && ret.is_tracked()) {
                p.setElementPositionAt(n, ret.closest()._vpos.x(), ret.closest()._vpos.y());
                _vitem->add_temp_vertex(ret.closest()._vpos);
            } else {
                p.setElementPositionAt(n, src.x(), src.y());
            }
        }
    }
    _vitem->setPath(p);
    return force ? ret : TrackActivities();
}
Exemple #3
0
bool SmoothPathPlugin::run(ScribusDoc* doc, QString)
{
	ScribusDoc* currDoc = doc;
	if (currDoc == 0)
		currDoc = ScCore->primaryMainWindow()->doc;
	if (currDoc->m_Selection->count() > 0)
	{
		PageItem *currItem = currDoc->m_Selection->itemAt(0);
		QPainterPath pp;
		if (currItem->itemType() == PageItem::PolyLine)
			pp = currItem->PoLine.toQPainterPath(false);
		else
			pp = currItem->PoLine.toQPainterPath(true);
		QList<QPolygonF> polyList = pp.toSubpathPolygons();
		QPainterPath result;
		for (int a = 0; a < polyList.count(); a++)
		{
			result.addPath(bezierFit(polyList[a], 5.0));
		}
		currItem->PoLine.fromQPainterPath(result);
		currItem->ClipEdited = true;
		currItem->FrameType = 3;
		currDoc->AdjustItemSize(currItem);
		currItem->OldB2 = currItem->width();
		currItem->OldH2 = currItem->height();
		currItem->updateClip();
		currDoc->regionsChanged()->update(QRectF());
		currDoc->changed();
	}
	return true;
}
/**
 * Este método dibuja una línea de texto.
 * @brief ThreeDWriter::writeLine
 * @param textLine Linea de texto a escribir
 * @param px coordenada x de donde comenzar a escribir
 * @param py coordenada y de donde comenzar a escribir
 * @param pz coordenada z de donde comenzar a escribir
 */
void ThreeDWriter::writeLine(QString textLine)
{


    QPainterPath path;
    glDisable(GL_LIGHTING);
    glColor3f (0.1f, 0.1f, 0.1f);
    QFont font(this->fontName, this->fontSize);
    path.addText(QPointF(0, 0), font, textLine);
    QList<QPolygonF> poly = path.toSubpathPolygons();    
    for (QList<QPolygonF>::iterator i = poly.begin(); i != poly.end(); i++){
        glBegin(GL_LINE_LOOP);

        for (QPolygonF::iterator p = (*i).begin(); p != i->end(); p++){

            this->x = p->rx()*this->cof+this->start_x;
            this->y = p->ry()*this->cof+this->start_y;
            this->z = this->start_z;
            glVertex3f(this->x, this->y, this->z);
            //qDebug()<<"Escribiendo en x:"<<x<<" y"<<y<< " z"<<z<<" p-rx "<<p->rx()<<" p->ry"<<p->ry();

        }
        glEnd();

    }
    glEnable(GL_LIGHTING);
    glDisable(GL_LIGHTING);
}
Exemple #5
0
void PathPaintEngine::drawPath(const QPainterPath& path)
{
	if (!dev)
		return;

	if(!isCosmetic)
	{
		QList<QPolygonF> polys = path.toSubpathPolygons();
		for (int i = 0; i < polys.size(); ++i)
		{
			if(dashPattern.empty()) dev->addPath(transform.map(polys[i]));
			else
			{
				QPolygonF polytemp = transform.map(polys[i]), newpoly;
				int  dashtoggle = 1, dashi=0, j = 0;
				qreal actualdashsize = dashPattern[dashi];
				QPointF origin = QPointF(polytemp[j]), testp;
				j++; 
				do
				{
					newpoly = QPolygonF();
					newpoly.append(origin);
					do
					{
						testp = polytemp[j];
						origin = QPointF(getPointAtLenght(QPointF(origin), polytemp[j], actualdashsize));
						if (essentiallyEqual(origin.x(), polytemp[j].x(), 0.01 ) && approximatelyEqual(origin.y(), polytemp[j].y(),0.01) && j+1 < polytemp.size()) 
						{
							origin = polytemp[j];
							j++;
							testp =  polytemp[j];
						}
						newpoly.append(origin);
					
					}while(definitelyGreaterThan(actualdashsize,0.0,0.1) && testp!=origin);
					if(dashtoggle == 1)
					{
						dev->addPath(newpoly);
					}
					dashtoggle = dashtoggle * -1;
					dashi++;
					if(dashi >= dashPattern.size()) dashi=0;
					actualdashsize = dashPattern[dashi];
				}while(!essentiallyEqual(origin.x(), polytemp[j].x(), 0.001 ) || !essentiallyEqual(origin.y(), polytemp[j].y(),0.001));
			}
		}
	}
}
Exemple #6
0
    QList<QPainterPath> splitDisjointPaths(const QPainterPath &path)
    {
        QList<QPainterPath> resultList;
        QList<QPolygonF> inputPolygons = path.toSubpathPolygons();

        Q_FOREACH (const QPolygonF &poly, inputPolygons) {
            QPainterPath testPath;
            testPath.addPolygon(poly);

            if (resultList.isEmpty()) {
                resultList.append(testPath);
                continue;
            }

            QList<QPainterPath>::iterator it = resultList.begin();
            QList<QPainterPath>::iterator end = resultList.end();
            QList<QPainterPath>::iterator savedIt = end;

            bool wasMerged = false;

            while (it != end) {
                bool skipIncrement = false;

                if (it->intersects(testPath)) {
                    if (savedIt == end) {
                        it->addPath(testPath);
                        savedIt = it;
                    } else {
                        savedIt->addPath(*it);
                        it = resultList.erase(it);
                        skipIncrement = true;
                    }

                    wasMerged = true;
                }

                if (!skipIncrement) {
                    ++it;
                }
            }

            if (!wasMerged) {
                resultList.append(testPath);
            }
        }
Exemple #7
0
void tst_QPolygon::makeEllipse()
{
    // create an ellipse with R1 = R2 = R, i.e. a circle
    QPolygon pa;
    const int R = 50; // radius
    QPainterPath path;
    path.addEllipse(0, 0, 2*R, 2*R);
    pa = path.toSubpathPolygons().at(0).toPolygon();

    int i;
    // make sure that all points are R+-1 away from the center
    bool err = FALSE;
    for (i = 1; i < pa.size(); i++) {
        QPoint p = pa.at( i );
        double r = sqrt( pow( double(p.x() - R), 2.0 ) + pow( double(p.y() - R), 2.0 ) );
        // ### too strict ? at least from visual inspection it looks
        // quite odd around the main axes. 2.0 passes easily.
        err |= ( qAbs( r - double(R) ) > 2.0 );
    }
    QVERIFY( !err );
}
Exemple #8
0
void PencilTool::smoothPath(QPainterPath &path, double smoothness, int from, int to)
{
    QPolygonF polygon;
    QList<QPolygonF> polygons = path.toSubpathPolygons();
    QList<QPolygonF>::iterator it = polygons.begin();
    QPolygonF::iterator pointIt;

    while (it != polygons.end()) {
           pointIt = (*it).begin();

           while (pointIt <= (*it).end()-2) {
                  polygon << (*pointIt);
                  pointIt += 2;
           }
           ++it;
    }

    if (smoothness > 0) {
        path = TupGraphicalAlgorithm::bezierFit(polygon, smoothness, from, to);
    } else {
        path = QPainterPath();
        path.addPolygon(polygon);
    }
}
Exemple #9
0
/*!
    Sets the points of the array to those describing an ellipse with
    size, width \a w by height \a h, and position (\a x, \a y).

    The returned array has sufficient resolution for use as pixels.
*/
void Q3PointArray::makeEllipse(int x, int y, int w, int h)
{
    QPainterPath path;
    path.addEllipse(x, y, w, h);
    *this = path.toSubpathPolygons().at(0).toPolygon();
}
/*!
  Draw the shape item

  \param painter Painter
  \param xMap X-Scale Map
  \param yMap Y-Scale Map
  \param canvasRect Contents rect of the plot canvas
*/
void QwtPlotShapeItem::draw( QPainter *painter,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect ) const
{
    if ( d_data->shape.isEmpty() )
        return;

    if ( d_data->pen.style() == Qt::NoPen 
        && d_data->brush.style() == Qt::NoBrush )
    {
        return;
    }

    const QRectF cRect = QwtScaleMap::invTransform(
        xMap, yMap, canvasRect.toRect() );

    if ( d_data->boundingRect.intersects( cRect ) )
    {
        const bool doAlign = QwtPainter::roundingAlignment( painter );

        QPainterPath path = qwtTransformPath( xMap, yMap, 
            d_data->shape, doAlign );

        if ( testPaintAttribute( QwtPlotShapeItem::ClipPolygons ) )
        {
            qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF());
            QRectF clipRect = canvasRect.adjusted( -pw, -pw, pw, pw );

            QPainterPath clippedPath;
            clippedPath.setFillRule( path.fillRule() );

            const QList<QPolygonF> polygons = path.toSubpathPolygons();
            for ( int i = 0; i < polygons.size(); i++ )
            {
                const QPolygonF p = QwtClipper::clipPolygonF(
                    clipRect, polygons[i], true );

                clippedPath.addPolygon( p );

            }

            path = clippedPath;
        }

        if ( d_data->renderTolerance > 0.0 )
        {
            QwtWeedingCurveFitter fitter( d_data->renderTolerance );

            QPainterPath fittedPath;
            fittedPath.setFillRule( path.fillRule() );

            const QList<QPolygonF> polygons = path.toSubpathPolygons();
            for ( int i = 0; i < polygons.size(); i++ )
                fittedPath.addPolygon( fitter.fitCurve( polygons[ i ] ) );

            path = fittedPath;
        }

        painter->setPen( d_data->pen );
        painter->setBrush( d_data->brush );

        painter->drawPath( path );
    }
}
QPolygonF curvedArrow( QPointF po, QPointF pm, QPointF pd, qreal startWidth, qreal width, qreal headSize, QgsArrowSymbolLayer::HeadType headType, qreal offset )
{
  qreal circleRadius;
  QPointF circleCenter;
  if ( ! pointsToCircle( po, pm, pd, circleCenter, circleRadius ) || circleRadius > 10000.0 )
  {
    // aligned points => draw a straight arrow
    return straightArrow( po, pd, startWidth, width, headSize, headType, offset );
  }

  // angles of each point
  qreal angle_o = clampAngle( atan2( circleCenter.y() - po.y(), po.x() - circleCenter.x() ) );
  qreal angle_m = clampAngle( atan2( circleCenter.y() - pm.y(), pm.x() - circleCenter.x() ) );
  qreal angle_d = clampAngle( atan2( circleCenter.y() - pd.y(), pd.x() - circleCenter.x() ) );

  // arc direction : 1 = counter-clockwise, -1 = clockwise
  int direction = clampAngle( angle_m - angle_o ) < clampAngle( angle_m - angle_d ) ? 1 : -1;

  qreal deltaAngle = angle_d - angle_o;
  if ( direction * deltaAngle < 0.0 )
    deltaAngle = deltaAngle + direction * 2 * M_PI;

  qreal length = euclidian_distance( po, pd );
  // for close points and deltaAngle < 180, draw a straight line
  if ( fabs( deltaAngle ) < M_PI && ((( headType == QgsArrowSymbolLayer::HeadSingle ) && ( length < headSize ) ) ||
                                     (( headType == QgsArrowSymbolLayer::HeadReversed ) && ( length < headSize ) ) ||
                                     (( headType == QgsArrowSymbolLayer::HeadDouble ) && ( length < 2*headSize ) ) ) )
  {
    return straightArrow( po, pd, startWidth, width, headSize, headType, offset );
  }

  // ajust coordinates to include offset
  circleRadius += offset;
  po = circlePoint( circleCenter, circleRadius, angle_o );
  pm = circlePoint( circleCenter, circleRadius, angle_m );
  pd = circlePoint( circleCenter, circleRadius, angle_d );

  qreal headAngle = direction * atan( headSize / circleRadius );

  QPainterPath path;

  if ( headType == QgsArrowSymbolLayer::HeadDouble )
  {
    // the first head
    path.moveTo( po );
    path.lineTo( circlePoint( circleCenter, circleRadius + direction * headSize, angle_o + headAngle ) );

    pathArcTo( path, circleCenter, circleRadius + direction * width / 2, angle_o + headAngle, angle_d - headAngle, direction );

    // the second head
    path.lineTo( circlePoint( circleCenter, circleRadius + direction * headSize, angle_d - headAngle ) );
    path.lineTo( pd );
    path.lineTo( circlePoint( circleCenter, circleRadius - direction * headSize, angle_d - headAngle ) );

    pathArcTo( path, circleCenter, circleRadius - direction * width / 2, angle_d - headAngle, angle_o + headAngle, -direction );

    // the end of the first head
    path.lineTo( circlePoint( circleCenter, circleRadius - direction * headSize, angle_o + headAngle ) );
    path.lineTo( po );
  }
  else if ( headType == QgsArrowSymbolLayer::HeadSingle )
  {
    path.moveTo( circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) );

    spiralArcTo( path, circleCenter, angle_o, circleRadius + direction * startWidth / 2, angle_d - headAngle, circleRadius + direction * width / 2, direction );

    // the arrow head
    path.lineTo( circlePoint( circleCenter, circleRadius + direction * headSize, angle_d - headAngle ) );
    path.lineTo( pd );
    path.lineTo( circlePoint( circleCenter, circleRadius - direction * headSize, angle_d - headAngle ) );

    spiralArcTo( path, circleCenter, angle_d - headAngle, circleRadius - direction * width / 2, angle_o, circleRadius - direction * startWidth / 2, -direction );

    path.lineTo( circlePoint( circleCenter, circleRadius + direction * startWidth / 2, angle_o ) );
  }
  else if ( headType == QgsArrowSymbolLayer::HeadReversed )
  {
    path.moveTo( circlePoint( circleCenter, circleRadius + direction * width / 2, angle_o + headAngle ) );

    spiralArcTo( path, circleCenter, angle_o + headAngle, circleRadius + direction * width / 2, angle_d, circleRadius + direction * startWidth / 2, direction );

    path.lineTo( circlePoint( circleCenter, circleRadius - direction * startWidth / 2, angle_d ) );

    spiralArcTo( path, circleCenter, angle_d, circleRadius - direction * startWidth / 2, angle_o + headAngle, circleRadius - direction * width / 2, - direction );

    path.lineTo( circlePoint( circleCenter, circleRadius - direction * headSize, angle_o + headAngle ) );
    path.lineTo( po );
    path.lineTo( circlePoint( circleCenter, circleRadius + direction * headSize, angle_o + headAngle ) );
    path.lineTo( circlePoint( circleCenter, circleRadius + direction * width / 2, angle_o + headAngle ) );
  }

  return path.toSubpathPolygons().at( 0 );
}
/*!
  Draw the shape item

  \param painter Painter
  \param xMap X-Scale Map
  \param yMap Y-Scale Map
  \param canvasRect Contents rect of the plot canvas
*/
void QwtPlotShapeItem::draw( QPainter *painter,
    const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QRectF &canvasRect ) const
{
    if ( d_data->shape.isEmpty() )
        return;

    if ( d_data->pen.style() == Qt::NoPen
        && d_data->brush.style() == Qt::NoBrush )
    {
        return;
    }

    const QRectF cr = QwtScaleMap::invTransform(
        xMap, yMap, canvasRect.toRect() );

    const QRectF &br = d_data->boundingRect;

    if ( ( br.left() > cr.right() ) || ( br.right() < cr.left() )
        || ( br.top() > cr.bottom() ) || ( br.bottom() < cr.top() ) )
    {
        // outside the visisble area
        return;
    }

    const bool doAlign = QwtPainter::roundingAlignment( painter );

    QPainterPath path = qwtTransformPath( xMap, yMap,
        d_data->shape, doAlign );

    if ( testPaintAttribute( QwtPlotShapeItem::ClipPolygons ) )
    {
        const qreal pw = QwtPainter::effectivePenWidth( painter->pen() );
        const QRectF clipRect = canvasRect.adjusted( -pw, -pw, pw, pw );

        QPainterPath clippedPath;
        clippedPath.setFillRule( path.fillRule() );

        QList<QPolygonF> polygons = path.toSubpathPolygons();
        for ( int i = 0; i < polygons.size(); i++ )
        {
            QwtClipper::clipPolygonF( clipRect, polygons[i], true );
            clippedPath.addPolygon( polygons[i] );

        }

        path = clippedPath;
    }

    if ( d_data->renderTolerance > 0.0 )
    {
        QwtWeedingCurveFitter fitter( d_data->renderTolerance );

        QPainterPath fittedPath;
        fittedPath.setFillRule( path.fillRule() );

        const QList<QPolygonF> polygons = path.toSubpathPolygons();
        for ( int i = 0; i < polygons.size(); i++ )
            fittedPath.addPolygon( fitter.fitCurve( polygons[ i ] ) );

        path = fittedPath;
    }

    painter->setPen( d_data->pen );
    painter->setBrush( d_data->brush );

    painter->drawPath( path );
}
Exemple #13
0
void KisOpenGLCanvas2::paintToolOutline(const QPainterPath &path)
{
    d->cursorShader->bind();

    // setup the mvp transformation
    KisCoordinatesConverter *converter = coordinatesConverter();

    QMatrix4x4 projectionMatrix;
    projectionMatrix.setToIdentity();
    projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL);

    // Set view/projection matrices
    QMatrix4x4 modelMatrix(converter->flakeToWidgetTransform());
    modelMatrix.optimize();
    modelMatrix = projectionMatrix * modelMatrix;
    d->cursorShader->setUniformValue(d->cursorShader->location(Uniform::ModelViewProjection), modelMatrix);

    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

    // XXX: glLogicOp not in ES 2.0 -- it would be better to use another method.
    // It is defined in 3.1 core profile onward.
    // Actually, https://www.opengl.org/sdk/docs/man/html/glLogicOp.xhtml says it's in 2.0 onwards,
    // only not in ES, but we don't care about ES, so we could use the function directly.
    glEnable(GL_COLOR_LOGIC_OP);
    if (ptr_glLogicOp) {
        ptr_glLogicOp(GL_XOR);
    }

    // Paint the tool outline
    if (KisOpenGL::hasOpenGL3()) {
        d->outlineVAO.bind();
        d->lineBuffer.bind();
    }

    // Convert every disjointed subpath to a polygon and draw that polygon
    QList<QPolygonF> subPathPolygons = path.toSubpathPolygons();
    for (int i = 0; i < subPathPolygons.size(); i++) {
        const QPolygonF& polygon = subPathPolygons.at(i);

        QVector<QVector3D> vertices;
        vertices.resize(polygon.count());
        for (int j = 0; j < polygon.count(); j++) {
            QPointF p = polygon.at(j);
            vertices[j].setX(p.x());
            vertices[j].setY(p.y());
        }
        if (KisOpenGL::hasOpenGL3()) {
            d->lineBuffer.allocate(vertices.constData(), 3 * vertices.size() * sizeof(float));
        }
        else {
            d->cursorShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
            d->cursorShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, vertices.constData());
        }

        glDrawArrays(GL_LINE_STRIP, 0, vertices.size());
    }

    if (KisOpenGL::hasOpenGL3()) {
        d->lineBuffer.release();
        d->outlineVAO.release();
    }

    glDisable(GL_COLOR_LOGIC_OP);

    d->cursorShader->release();
}
Exemple #14
0
void GLText::initializeGlyph(char ch)
{
  Glyph glyph;
  QPainterPath path;
  if(debugTesselation) printf("Adding glyph %c\n",ch);
  path.addText(0,0,font,QString((QChar)ch));
  QList<QPolygonF> polygons = path.toSubpathPolygons();
  if(debugTesselation){
    printf("%d Sub-Polygons\n",polygons.size());
  printf("Poly has %d vertices:\n",polygons.size());
  }
  int numVertices = 0;
  double minX=DBL_MAX, minY=DBL_MAX, maxX=-DBL_MAX, maxY=-DBL_MAX;
  for(int i=0; i<polygons.size(); i++){
    if(debugTesselation) printf("Sub-Polygon %d:\n",i);
    numVertices += polygons[i].size();
    
    for(int k=0; k<polygons[i].size(); k++){
      if(debugTesselation) printf("%8.3f,%8.3f\n",polygons[i][k].x(),polygons[i][k].y());
      minX = min(minX, polygons[i][k].x());
      maxX = max(maxX, polygons[i][k].x());
      minY = min(minY, polygons[i][k].y());
      maxY = max(maxY, polygons[i][k].y());
    }
  }
  glyph.ascent = fabs(minY)/FontRenderSize;
  glyph.descent = fabs(maxY)/FontRenderSize;
  
  glyph.height = (maxY - minY)/FontRenderSize;
  glyph.width = (maxX - minX)/FontRenderSize;
  
  if(debugTesselation) printf("numVertices: %d\n",numVertices);
  GLdouble vertices[numVertices][3];
  int j=0;
  for(int i=0; i<polygons.size(); i++){
    for(int k=0; k<polygons[i].size(); k++){
      vertices[j][0] = polygons[i][k].x()/FontRenderSize;
      vertices[j][1] = -polygons[i][k].y()/FontRenderSize;
      vertices[j][2] = 9;
      j++;
    }
  }
  
  GLUtesselator* tess = gluNewTess();
  gluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr) tessBeginCB);
  gluTessCallback(tess, GLU_TESS_END, (_GLUfuncptr) tessEndCB);
  gluTessCallback(tess, GLU_TESS_ERROR, (_GLUfuncptr) tessErrorCB);
  gluTessCallback(tess, GLU_TESS_VERTEX, (_GLUfuncptr) tessVertexCB);
  
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  
  glyph.displayListID = glGenLists(1);
  if(glyph.displayListID==GL_INVALID_VALUE){
    printf("Unable to create display list!\n");
    exit(1);
  }
  glNewList(glyph.displayListID, GL_COMPILE);
  gluTessBeginPolygon(tess, 0);
  j=0;
  for(int i=0; i<polygons.size(); i++){
    gluTessBeginContour(tess);
    for(int k=0; k<polygons[i].size(); k++){
      gluTessVertex(tess, vertices[j], vertices[j]);
      j++;
    }
    gluTessEndContour(tess);
  }
  gluTessEndPolygon(tess);
  gluDeleteTess(tess);
  glEndList();
  glPopMatrix();
  glyph.compiled = true;
  glyphs[ch] = glyph;
}