/* $Id: quadric.c,v 1.9 2001/01/15 20:04:28 brianp Exp $ */

/*
 * Mesa 3-D graphics library
 * Version:  3.3
 * Copyright (C) 1999-2000  Brian Paul
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


/* TODO:
 *   texture coordinate support
 *   flip normals according to orientation
 *   there's still some inside/outside orientation bugs in possibly all
 *     but the sphere function
 */


#ifdef PC_HEADER
#include "all.h"
#else
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "gluP.h"
#endif



#ifndef M_PI
#  define M_PI (3.1415926)
#endif


/*
 * Convert degrees to radians:
 */
#define DEG_TO_RAD(A)   ((A)*(M_PI/180.0))


/*
 * Sin and Cos for degree angles:
 */
#define SIND( A )   sin( (A)*(M_PI/180.0) )
#define COSD( A)    cos( (A)*(M_PI/180.0) )


/*
 * Texture coordinates if texture flag is set
 */
#define TXTR_COORD(x,y)    if (qobj->TextureFlag) glTexCoord2f(x,y);



struct GLUquadric
{
   GLenum DrawStyle;		/* GLU_FILL, LINE, SILHOUETTE, or POINT */
   GLenum Orientation;		/* GLU_INSIDE or GLU_OUTSIDE */
   GLboolean TextureFlag;	/* Generate texture coords? */
   GLenum Normals;		/* GLU_NONE, GLU_FLAT, or GLU_SMOOTH */
   void (GLCALLBACK * ErrorFunc) (GLenum err);	/* Error handler callback function */
};



/*
 * Process a GLU error.
 */
static void
quadric_error(GLUquadricObj * qobj, GLenum error, const char *msg)
{
   /* Call the error call back function if any */
   if (qobj->ErrorFunc) {
      (*qobj->ErrorFunc) (error);
   }
   /* Print a message to stdout if MESA_DEBUG variable is defined */
   if (getenv("MESA_DEBUG")) {
      fprintf(stderr, "GLUError: %s: %s\n", (char *) gluErrorString(error),
	      msg);
   }
}




GLUquadricObj *GLAPIENTRY
gluNewQuadric(void)
{
   GLUquadricObj *q;

   q = (GLUquadricObj *) malloc(sizeof(struct GLUquadric));
   if (q) {
      q->DrawStyle = GLU_FILL;
      q->Orientation = GLU_OUTSIDE;
      q->TextureFlag = GL_FALSE;
      q->Normals = GLU_SMOOTH;
      q->ErrorFunc = NULL;
   }
   return q;
}



void GLAPIENTRY
gluDeleteQuadric(GLUquadricObj * state)
{
   if (state) {
      free((void *) state);
   }
}



/*
 * Set the drawing style to be GLU_FILL, GLU_LINE, GLU_SILHOUETTE,
 * or GLU_POINT.
 */
void GLAPIENTRY
gluQuadricDrawStyle(GLUquadricObj * quadObject, GLenum drawStyle)
{
   if (quadObject && (drawStyle == GLU_FILL || drawStyle == GLU_LINE
		      || drawStyle == GLU_SILHOUETTE
		      || drawStyle == GLU_POINT)) {
      quadObject->DrawStyle = drawStyle;
   }
   else {
      quadric_error(quadObject, GLU_INVALID_ENUM, "qluQuadricDrawStyle");
   }
}



/*
 * Set the orientation to GLU_INSIDE or GLU_OUTSIDE.
 */
void GLAPIENTRY
gluQuadricOrientation(GLUquadricObj * quadObject, GLenum orientation)
{
   if (quadObject
       && (orientation == GLU_INSIDE || orientation == GLU_OUTSIDE)) {
      quadObject->Orientation = orientation;
   }
   else {
      quadric_error(quadObject, GLU_INVALID_ENUM, "qluQuadricOrientation");
   }
}



/*
 * Set the error handler callback function.
 */
void GLAPIENTRY
gluQuadricCallback(GLUquadricObj * qobj,
		   GLenum which, void (GLCALLBACK * fn) ())
{
   /*
    * UGH, this is a mess!  I thought ANSI was a standard.
    */
   if (qobj && which == GLU_ERROR) {
#ifdef __CYGWIN32__
      qobj->ErrorFunc = (void (GLCALLBACKPCAST) (GLenum)) fn;
#elif defined(OPENSTEP)
      qobj->ErrorFunc = (void (*)(GLenum)) fn;
#elif defined(_WIN32)
      qobj->ErrorFunc = (void (GLCALLBACK *) (int)) fn;
#elif defined(__STORM__)
      qobj->ErrorFunc = (void (GLCALLBACK *) (GLenum)) fn;
#elif defined(__BEOS__)
      qobj->ErrorFunc = (void (*)(GLenum)) fn;
#else
      qobj->ErrorFunc = (void (GLCALLBACK *) ()) fn;
#endif
   }
}


void GLAPIENTRY
gluQuadricNormals(GLUquadricObj * quadObject, GLenum normals)
{
   if (quadObject
       && (normals == GLU_NONE || normals == GLU_FLAT
	   || normals == GLU_SMOOTH)) {
      quadObject->Normals = normals;
   }
}


void GLAPIENTRY
gluQuadricTexture(GLUquadricObj * quadObject, GLboolean textureCoords)
{
   if (quadObject) {
      quadObject->TextureFlag = textureCoords;
   }
}




/*
 * Call glNormal3f after scaling normal to unit length.
 */
static void
normal3f(GLfloat x, GLfloat y, GLfloat z)
{
   GLdouble mag;

   mag = sqrt(x * x + y * y + z * z);
   if (mag > 0.00001F) {
      x /= mag;
      y /= mag;
      z /= mag;
   }
   glNormal3f(x, y, z);
}



void GLAPIENTRY
gluCylinder(GLUquadricObj * qobj,
	    GLdouble baseRadius, GLdouble topRadius,
	    GLdouble height, GLint slices, GLint stacks)
{
   GLdouble da, r, dr, dz;
   GLfloat x, y, z, nz, nsign;
   GLint i, j;

   if (qobj->Orientation == GLU_INSIDE) {
      nsign = -1.0;
   }
   else {
      nsign = 1.0;
   }

   da = 2.0 * M_PI / slices;
   dr = (topRadius - baseRadius) / stacks;
   dz = height / stacks;
   nz = (baseRadius - topRadius) / height;	/* Z component of normal vectors */

   if (qobj->DrawStyle == GLU_POINT) {
      glBegin(GL_POINTS);
      for (i = 0; i < slices; i++) {
	 x = cos(i * da);
	 y = sin(i * da);
	 normal3f(x * nsign, y * nsign, nz * nsign);

	 z = 0.0;
	 r = baseRadius;
	 for (j = 0; j <= stacks; j++) {
	    glVertex3f(x * r, y * r, z);
	    z += dz;
	    r += dr;
	 }
      }
      glEnd();
   }
   else if (qobj->DrawStyle == GLU_LINE || qobj->DrawStyle == GLU_SILHOUETTE) {
      /* Draw rings */
      if (qobj->DrawStyle == GLU_LINE) {
	 z = 0.0;
	 r = baseRadius;
	 for (j = 0; j <= stacks; j++) {
	    glBegin(GL_LINE_LOOP);
	    for (i = 0; i < slices; i++) {
	       x = cos(i * da);
	       y = sin(i * da);
	       normal3f(x * nsign, y * nsign, nz * nsign);
	       glVertex3f(x * r, y * r, z);
	    }
	    glEnd();
	    z += dz;
	    r += dr;
	 }
      }
      else {
	 /* draw one ring at each end */
	 if (baseRadius != 0.0) {
	    glBegin(GL_LINE_LOOP);
	    for (i = 0; i < slices; i++) {
	       x = cos(i * da);
	       y = sin(i * da);
	       normal3f(x * nsign, y * nsign, nz * nsign);
	       glVertex3f(x * baseRadius, y * baseRadius, 0.0);
	    }
	    glEnd();
	    glBegin(GL_LINE_LOOP);
	    for (i = 0; i < slices; i++) {
	       x = cos(i * da);
	       y = sin(i * da);
	       normal3f(x * nsign, y * nsign, nz * nsign);
	       glVertex3f(x * topRadius, y * topRadius, height);
	    }
	    glEnd();
	 }
      }
      /* draw length lines */
      glBegin(GL_LINES);
      for (i = 0; i < slices; i++) {
	 x = cos(i * da);
	 y = sin(i * da);
	 normal3f(x * nsign, y * nsign, nz * nsign);
	 glVertex3f(x * baseRadius, y * baseRadius, 0.0);
	 glVertex3f(x * topRadius, y * topRadius, height);
      }
      glEnd();
   }
   else if (qobj->DrawStyle == GLU_FILL) {
      GLfloat ds = 1.0 / slices;
      GLfloat dt = 1.0 / stacks;
      GLfloat t = 0.0;
      z = 0.0;
      r = baseRadius;
      for (j = 0; j < stacks; j++) {
	 GLfloat s = 0.0;
	 glBegin(GL_QUAD_STRIP);
	 for (i = 0; i <= slices; i++) {
	    GLfloat x, y;
	    if (i == slices) {
	       x = sin(0.0);
	       y = cos(0.0);
	    }
	    else {
	       x = sin(i * da);
	       y = cos(i * da);
	    }
	    if (nsign == 1.0) {
	       normal3f(x * nsign, y * nsign, nz * nsign);
	       TXTR_COORD(s, t);
	       glVertex3f(x * r, y * r, z);
	       normal3f(x * nsign, y * nsign, nz * nsign);
	       TXTR_COORD(s, t + dt);
	       glVertex3f(x * (r + dr), y * (r + dr), z + dz);
	    }
	    else {
	       normal3f(x * nsign, y * nsign, nz * nsign);
	       TXTR_COORD(s, t);
	       glVertex3f(x * r, y * r, z);
	       normal3f(x * nsign, y * nsign, nz * nsign);
	       TXTR_COORD(s, t + dt);
	       glVertex3f(x * (r + dr), y * (r + dr), z + dz);
	    }
	    s += ds;
	 }			/* for slices */
	 glEnd();
	 r += dr;
	 t += dt;
	 z += dz;
      }				/* for stacks */
   }
}





void GLAPIENTRY
gluSphere(GLUquadricObj * qobj, GLdouble radius, GLint slices, GLint stacks)
{
   GLfloat rho, drho, theta, dtheta;
   GLfloat x, y, z;
   GLfloat s, t, ds, dt;
   GLint i, j, imin, imax;
   GLboolean normals;
   GLfloat nsign;

   if (qobj->Normals == GLU_NONE) {
      normals = GL_FALSE;
   }
   else {
      normals = GL_TRUE;
   }
   if (qobj->Orientation == GLU_INSIDE) {
      nsign = -1.0;
   }
   else {
      nsign = 1.0;
   }

   drho = M_PI / (GLfloat) stacks;
   dtheta = 2.0 * M_PI / (GLfloat) slices;

   /* texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y axis */
   /* t goes from -1.0/+1.0 at z = -radius/+radius (linear along longitudes) */
   /* cannot use triangle fan on texturing (s coord. at top/bottom tip varies) */

   if (qobj->DrawStyle == GLU_FILL) {
      if (!qobj->TextureFlag) {
	 /* draw +Z end as a triangle fan */
	 glBegin(GL_TRIANGLE_FAN);
	 glNormal3f(0.0, 0.0, 1.0);
	 glVertex3f(0.0, 0.0, nsign * radius);
	 for (j = 0; j <= slices; j++) {
	    theta = (j == slices) ? 0.0 : j * dtheta;
	    x = -sin(theta) * sin(drho);
	    y = cos(theta) * sin(drho);
	    z = nsign * cos(drho);
	    if (normals)
	       glNormal3f(x * nsign, y * nsign, z * nsign);
	    glVertex3f(x * radius, y * radius, z * radius);
	 }
	 glEnd();
      }

      ds = 1.0 / slices;
      dt = 1.0 / stacks;
      t = 1.0;			/* because loop now runs from 0 */
      if (qobj->TextureFlag) {
	 imin = 0;
	 imax = stacks;
      }
      else {
	 imin = 1;
	 imax = stacks - 1;
      }

      /* draw intermediate stacks as quad strips */
      for (i = imin; i < imax; i++) {
	 rho = i * drho;
	 glBegin(GL_QUAD_STRIP);
	 s = 0.0;
	 for (j = 0; j <= slices; j++) {
	    theta = (j == slices) ? 0.0 : j * dtheta;
	    x = -sin(theta) * sin(rho);
	    y = cos(theta) * sin(rho);
	    z = nsign * cos(rho);
	    if (normals)
	       glNormal3f(x * nsign, y * nsign, z * nsign);
	    TXTR_COORD(s, t);
	    glVertex3f(x * radius, y * radius, z * radius);
	    x = -sin(theta) * sin(rho + drho);
	    y = cos(theta) * sin(rho + drho);
	    z = nsign * cos(rho + drho);
	    if (normals)
	       glNormal3f(x * nsign, y * nsign, z * nsign);
	    TXTR_COORD(s, t - dt);
	    s += ds;
	    glVertex3f(x * radius, y * radius, z * radius);
	 }
	 glEnd();
	 t -= dt;
      }

      if (!qobj->TextureFlag) {
	 /* draw -Z end as a triangle fan */
	 glBegin(GL_TRIANGLE_FAN);
	 glNormal3f(0.0, 0.0, -1.0);
	 glVertex3f(0.0, 0.0, -radius * nsign);
	 rho = M_PI - drho;
	 s = 1.0;
	 t = dt;
	 for (j = slices; j >= 0; j--) {
	    theta = (j == slices) ? 0.0 : j * dtheta;
	    x = -sin(theta) * sin(rho);
	    y = cos(theta) * sin(rho);
	    z = nsign * cos(rho);
	    if (normals)
	       glNormal3f(x * nsign, y * nsign, z * nsign);
	    s -= ds;
	    glVertex3f(x * radius, y * radius, z * radius);
	 }
	 glEnd();
      }
   }
   else if (qobj->DrawStyle == GLU_LINE || qobj->DrawStyle == GLU_SILHOUETTE) {
      /* draw stack lines */
      for (i = 1; i < stacks; i++) {	/* stack line at i==stacks-1 was missing here */
	 rho = i * drho;
	 glBegin(GL_LINE_LOOP);
	 for (j = 0; j < slices; j++) {
	    theta = j * dtheta;
	    x = cos(theta) * sin(rho);
	    y = sin(theta) * sin(rho);
	    z = cos(rho);
	    if (normals)
	       glNormal3f(x * nsign, y * nsign, z * nsign);
	    glVertex3f(x * radius, y * radius, z * radius);
	 }
	 glEnd();
      }
      /* draw slice lines */
      for (j = 0; j < slices; j++) {
	 theta = j * dtheta;
	 glBegin(GL_LINE_STRIP);
	 for (i = 0; i <= stacks; i++) {
	    rho = i * drho;
	    x = cos(theta) * sin(rho);
	    y = sin(theta) * sin(rho);
	    z = cos(rho);
	    if (normals)
	       glNormal3f(x * nsign, y * nsign, z * nsign);
	    glVertex3f(x * radius, y * radius, z * radius);
	 }
	 glEnd();
      }
   }
   else if (qobj->DrawStyle == GLU_POINT) {
      /* top and bottom-most points */
      glBegin(GL_POINTS);
      if (normals)
	 glNormal3f(0.0, 0.0, nsign);
      glVertex3d(0.0, 0.0, radius);
      if (normals)
	 glNormal3f(0.0, 0.0, -nsign);
      glVertex3d(0.0, 0.0, -radius);

      /* loop over stacks */
      for (i = 1; i < stacks - 1; i++) {
	 rho = i * drho;
	 for (j = 0; j < slices; j++) {
	    theta = j * dtheta;
	    x = cos(theta) * sin(rho);
	    y = sin(theta) * sin(rho);
	    z = cos(rho);
	    if (normals)
	       glNormal3f(x * nsign, y * nsign, z * nsign);
	    glVertex3f(x * radius, y * radius, z * radius);
	 }
      }
      glEnd();
   }

}



void GLAPIENTRY
gluDisk(GLUquadricObj * qobj,
	GLdouble innerRadius, GLdouble outerRadius, GLint slices, GLint loops)
{
   GLfloat da, dr;
#if 0
   GLdouble a, da;
   GLfloat r, dr;
   GLfloat x, y;
   GLfloat r1, r2, dtc;
   GLint s, l;
#endif

   /* Normal vectors */
   if (qobj->Normals != GLU_NONE) {
      if (qobj->Orientation == GLU_OUTSIDE) {
	 glNormal3f(0.0, 0.0, +1.0);
      }
      else {
	 glNormal3f(0.0, 0.0, -1.0);
      }
   }

   da = 2.0 * M_PI / slices;
   dr = (outerRadius - innerRadius) / (GLfloat) loops;

   switch (qobj->DrawStyle) {
   case GLU_FILL:
      {
	 /* texture of a gluDisk is a cut out of the texture unit square
	  * x, y in [-outerRadius, +outerRadius]; s, t in [0, 1]
	  * (linear mapping)
	  */
	 GLfloat dtc = 2.0f * outerRadius;
	 GLfloat sa, ca;
	 GLfloat r1 = innerRadius;
	 GLint l;
	 for (l = 0; l < loops; l++) {
	    GLfloat r2 = r1 + dr;
	    if (qobj->Orientation == GLU_OUTSIDE) {
	       GLint s;
	       glBegin(GL_QUAD_STRIP);
	       for (s = 0; s <= slices; s++) {
		  GLfloat a;
		  if (s == slices)
		     a = 0.0;
		  else
		     a = s * da;
		  sa = sin(a);
		  ca = cos(a);
		  TXTR_COORD(0.5 + sa * r2 / dtc, 0.5 + ca * r2 / dtc);
		  glVertex2f(r2 * sa, r2 * ca);
		  TXTR_COORD(0.5 + sa * r1 / dtc, 0.5 + ca * r1 / dtc);
		  glVertex2f(r1 * sa, r1 * ca);
	       }
	       glEnd();
	    }
	    else {
	       GLint s;
	       glBegin(GL_QUAD_STRIP);
	       for (s = slices; s >= 0; s--) {
		  GLfloat a;
		  if (s == slices)
		     a = 0.0;
		  else
		     a = s * da;
		  sa = sin(a);
		  ca = cos(a);
		  TXTR_COORD(0.5 - sa * r2 / dtc, 0.5 + ca * r2 / dtc);
		  glVertex2f(r2 * sa, r2 * ca);
		  TXTR_COORD(0.5 - sa * r1 / dtc, 0.5 + ca * r1 / dtc);
		  glVertex2f(r1 * sa, r1 * ca);
	       }
	       glEnd();
	    }
	    r1 = r2;
	 }
	 break;
      }
   case GLU_LINE:
      {
	 GLint l, s;
	 /* draw loops */
	 for (l = 0; l <= loops; l++) {
	    GLfloat r = innerRadius + l * dr;
	    glBegin(GL_LINE_LOOP);
	    for (s = 0; s < slices; s++) {
	       GLfloat a = s * da;
	       glVertex2f(r * sin(a), r * cos(a));
	    }
	    glEnd();
	 }
	 /* draw spokes */
	 for (s = 0; s < slices; s++) {
	    GLfloat a = s * da;
	    GLfloat x = sin(a);
	    GLfloat y = cos(a);
	    glBegin(GL_LINE_STRIP);
	    for (l = 0; l <= loops; l++) {
	       GLfloat r = innerRadius + l * dr;
	       glVertex2f(r * x, r * y);
	    }
	    glEnd();
	 }
	 break;
      }
   case GLU_POINT:
      {
	 GLint s;
	 glBegin(GL_POINTS);
	 for (s = 0; s < slices; s++) {
	    GLfloat a = s * da;
	    GLfloat x = sin(a);
	    GLfloat y = cos(a);
	    GLint l;
	    for (l = 0; l <= loops; l++) {
	       GLfloat r = innerRadius * l * dr;
	       glVertex2f(r * x, r * y);
	    }
	 }
	 glEnd();
	 break;
      }
   case GLU_SILHOUETTE:
      {
	 if (innerRadius != 0.0) {
	    GLfloat a;
	    glBegin(GL_LINE_LOOP);
	    for (a = 0.0; a < 2.0 * M_PI; a += da) {
	       GLfloat x = innerRadius * sin(a);
	       GLfloat y = innerRadius * cos(a);
	       glVertex2f(x, y);
	    }
	    glEnd();
	 }
	 {
	    GLfloat a;
	    glBegin(GL_LINE_LOOP);
	    for (a = 0; a < 2.0 * M_PI; a += da) {
	       GLfloat x = outerRadius * sin(a);
	       GLfloat y = outerRadius * cos(a);
	       glVertex2f(x, y);
	    }
	    glEnd();
	 }
	 break;
      }
   default:
      abort();
   }
}



void GLAPIENTRY
gluPartialDisk(GLUquadricObj * qobj, GLdouble innerRadius,
	       GLdouble outerRadius, GLint slices, GLint loops,
	       GLdouble startAngle, GLdouble sweepAngle)
{
   if (qobj->Normals != GLU_NONE) {
      if (qobj->Orientation == GLU_OUTSIDE) {
	 glNormal3f(0.0, 0.0, +1.0);
      }
      else {
	 glNormal3f(0.0, 0.0, -1.0);
      }
   }

   if (qobj->DrawStyle == GLU_POINT) {
      GLint loop, slice;
      GLdouble radius, delta_radius;
      GLdouble angle, delta_angle;
      delta_radius = (outerRadius - innerRadius) / (loops - 1);
      delta_angle = DEG_TO_RAD((sweepAngle) / (slices - 1));
      glBegin(GL_POINTS);
      radius = innerRadius;
      for (loop = 0; loop < loops; loop++) {
	 angle = DEG_TO_RAD(startAngle);
	 for (slice = 0; slice < slices; slice++) {
	    glVertex2d(radius * sin(angle), radius * cos(angle));
	    angle += delta_angle;
	 }
	 radius += delta_radius;
      }
      glEnd();
   }
   else if (qobj->DrawStyle == GLU_LINE) {
      GLint loop, slice;
      GLdouble radius, delta_radius;
      GLdouble angle, delta_angle;
      delta_radius = (outerRadius - innerRadius) / loops;
      delta_angle = DEG_TO_RAD(sweepAngle / slices);
      /* draw rings */
      radius = innerRadius;
      for (loop = 0; loop < loops; loop++) {
	 angle = DEG_TO_RAD(startAngle);
	 glBegin(GL_LINE_STRIP);
	 for (slice = 0; slice <= slices; slice++) {
	    glVertex2d(radius * sin(angle), radius * cos(angle));
	    angle += delta_angle;
	 }
	 glEnd();
	 radius += delta_radius;
      }
      /* draw spokes */
      angle = DEG_TO_RAD(startAngle);
      for (slice = 0; slice <= slices; slice++) {
	 radius = innerRadius;
	 glBegin(GL_LINE_STRIP);
	 for (loop = 0; loop < loops; loop++) {
	    glVertex2d(radius * sin(angle), radius * cos(angle));
	    radius += delta_radius;
	 }
	 glEnd();
	 angle += delta_angle;
      }
   }
   else if (qobj->DrawStyle == GLU_SILHOUETTE) {
      GLint slice;
      GLdouble angle, delta_angle;
      delta_angle = DEG_TO_RAD(sweepAngle / slices);
      /* draw outer ring */
      glBegin(GL_LINE_STRIP);
      angle = DEG_TO_RAD(startAngle);
      for (slice = 0; slice <= slices; slice++) {
	 glVertex2d(outerRadius * sin(angle), outerRadius * cos(angle));
	 angle += delta_angle;
      }
      glEnd();
      /* draw inner ring */
      if (innerRadius > 0.0) {
	 glBegin(GL_LINE_STRIP);
	 angle = DEG_TO_RAD(startAngle);
	 for (slice = 0; slice < slices; slice++) {
	    glVertex2d(innerRadius * sin(angle), innerRadius * cos(angle));
	    angle += delta_angle;
	 }
	 glEnd();
      }
      /* draw spokes */
      if (sweepAngle < 360.0) {
	 GLdouble stopAngle = startAngle + sweepAngle;
	 glBegin(GL_LINES);
	 glVertex2d(innerRadius * SIND(startAngle),
		    innerRadius * COSD(startAngle));
	 glVertex2d(outerRadius * SIND(startAngle),
		    outerRadius * COSD(startAngle));
	 glVertex2d(innerRadius * SIND(stopAngle),
		    innerRadius * COSD(stopAngle));
	 glVertex2d(outerRadius * SIND(stopAngle),
		    outerRadius * COSD(stopAngle));
	 glEnd();
      }
   }
   else if (qobj->DrawStyle == GLU_FILL) {
      GLint loop, slice;
      GLdouble radius, delta_radius;
      GLdouble angle, delta_angle;
      delta_radius = (outerRadius - innerRadius) / loops;
      delta_angle = DEG_TO_RAD(sweepAngle / slices);
      radius = innerRadius;
      for (loop = 0; loop < loops; loop++) {
	 glBegin(GL_QUAD_STRIP);
	 angle = DEG_TO_RAD(startAngle);
	 for (slice = 0; slice <= slices; slice++) {
	    if (qobj->Orientation == GLU_OUTSIDE) {
	       glVertex2d((radius + delta_radius) * sin(angle),
			  (radius + delta_radius) * cos(angle));
	       glVertex2d(radius * sin(angle), radius * cos(angle));
	    }
	    else {
	       glVertex2d(radius * sin(angle), radius * cos(angle));
	       glVertex2d((radius + delta_radius) * sin(angle),
			  (radius + delta_radius) * cos(angle));
	    }
	    angle += delta_angle;
	 }
	 glEnd();
	 radius += delta_radius;
      }
   }
}