/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)
  
	Adresse mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea P fr.

	Ce logiciel est un programme informatique servant à visualiser des
	structures atomiques dans un rendu pseudo-3D. 

	Ce logiciel est régi par la licence CeCILL soumise au droit français et
	respectant les principes de diffusion des logiciels libres. Vous pouvez
	utiliser, modifier et/ou redistribuer ce programme sous les conditions
	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
	sur le site "http://www.cecill.info".

	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
	pris connaissance de la licence CeCILL, et que vous en avez accepté les
	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
	Copyright CEA, contributors : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)

	E-mail address:
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.

	This software is a computer program whose purpose is to visualize atomic
	configurations in 3D.

	This software is governed by the CeCILL  license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/
#include "objectList.h"

#include <stdlib.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include <visu_tools.h>
#include <opengl.h>
#include <coreTools/toolColor.h>

#include "text.h"

/**
 * SECTION:objectList
 * @short_description: Gives storage for ids used by OpenGL lists and
 * provides primitive routine for common drawing operations
 * (distances, torus...).
 *
 * <para>When using OpenGL list, one must specify ids. This module can
 * return unused ids when needed.</para>
 *
 * <para>Here are also defined the common primitive for drawing, see
 * visu_gl_drawDistance() for instance.</para>
 */

/* Variables to store the glObjectList. */
/* The default size of the table to store the user
   lists. */
#define GLOBJECTLIST_INCREMENT 15
/* First value given to user to store their glObjectList. */
#define GLOBJECTLIST_FIRST 1001
/* The table that stores the identifier of user's glObjectList. */
static int *glObjectListRegistered;
/* The allocated size of this table. */
static int glObjectListRegisteredSize;
/* The number of stored elements. */
static int nbGlObjectListRegistered;
/* The number of user's reserved numbers of the last
   entry in this table. */
static int lastGlObjectListSize;

/**
 * visu_gl_objectlist_init: (skip)
 *
 * Used to initialise this part of code, don't use it.
 */
void visu_gl_objectlist_init(void)
{
  /* Initialisation of the table that stores the user
     glObjectList. */
  glObjectListRegistered = (int*)0;
  glObjectListRegisteredSize = 0;
  nbGlObjectListRegistered = 0;
  lastGlObjectListSize = 0;
}


/**
 * visu_gl_objectlist_new:
 * @size: the requested size.
 *
 * It returns the id that can be used to link glObjectList.
 * This number is also added to the list of glObjectList to be displayed.
 * The size parameter is the number of lists that could be used by
 * the user for this glObjectList. Then the next call to this function
 * will return the last value plus the size plus one.
 *
 * Returns: an identifier used by OpenGl as a list.
 */
int visu_gl_objectlist_new(int size)
{
  if (nbGlObjectListRegistered >= glObjectListRegisteredSize)
    {
      glObjectListRegisteredSize += GLOBJECTLIST_INCREMENT;
      glObjectListRegistered = g_realloc(glObjectListRegistered,
					 sizeof(int) * glObjectListRegisteredSize);
    }
  if (nbGlObjectListRegistered > 0)
    glObjectListRegistered[nbGlObjectListRegistered] = 
      glObjectListRegistered[nbGlObjectListRegistered - 1] + lastGlObjectListSize;
  else
    glObjectListRegistered[0] = GLOBJECTLIST_FIRST;
  lastGlObjectListSize = size;
  nbGlObjectListRegistered++;
  DBG_fprintf(stderr, "Object List: request %d OpenGL lists -> %d %d.\n",
	      size, glObjectListRegistered[nbGlObjectListRegistered - 1],
	      glObjectListRegistered[nbGlObjectListRegistered - 1] + size - 1);
  return glObjectListRegistered[nbGlObjectListRegistered - 1];
}


static void drawRoundedHat(GLUquadricObj *obj, float hat_length,
			   float hat_height, float radius, int nlat)
{
  gluCylinder(obj, hat_length, 0, hat_height, nlat, 1);
  glRotatef(180, 1, 0, 0);
  gluDisk(obj, 0, radius, nlat, 1);
  glRotatef(180, 1, 0, 0);
}
static void drawRoundedTail(GLUquadricObj *obj, float tail_length,
			    float tail_height, int nlat)
{
  if (tail_height <= 0.f)
    return;

  glTranslatef(0, 0, -tail_height);
  glRotatef(180, 1, 0, 0);
  gluDisk(obj, 0, (tail_length)*1.03, nlat, 1);      
  glRotatef(180, 1, 0, 0);
  gluCylinder(obj, tail_length, tail_length, tail_height, nlat, 1);
}
static void drawEdgedHat(float hat_length, float hat_height)
{
  glBegin(GL_TRIANGLE_FAN);
      
  glNormal3f(hat_height, 0, hat_length);
  glVertex3f(0, 0, hat_height); 
  glVertex3f(hat_length,-hat_length,0);
  glVertex3f(hat_length,hat_length,0);

  glNormal3f(0, hat_height, hat_length);
  glVertex3f(-hat_length,hat_length,0);
       
  glNormal3f(-hat_height, 0, hat_length);
  glVertex3f(-hat_length,-hat_length,0);

  glNormal3f(-hat_height, 0, hat_length);
  glVertex3f(hat_length,-hat_length,0);

  glEnd();

  glBegin(GL_QUADS);

  glNormal3f(0., 0., -1.);
  glVertex3f(hat_length ,hat_length ,0 );
  glVertex3f(hat_length ,-hat_length ,0 );
  glVertex3f(-hat_length ,-hat_length ,0 );
  glVertex3f(-hat_length ,hat_length ,0 );

  glEnd();
}
static void drawEdgedTail(float tail_length, float tail_height)
{
  glBegin(GL_QUADS);

  glNormal3f(0., 0., 1.);
  glVertex3f(tail_length , tail_length , 0 );
  glVertex3f(-tail_length , tail_length , 0 );
  glVertex3f(-tail_length , -tail_length , 0 );
  glVertex3f(tail_length , -tail_length , 0 );

  glNormal3f(0., 0., -1.);
  glVertex3f(tail_length ,tail_length ,-tail_height );
  glVertex3f(tail_length ,-tail_length ,-tail_height );
  glVertex3f(-tail_length ,-tail_length ,-tail_height );
  glVertex3f(-tail_length ,tail_length ,-tail_height );

  glNormal3f(1., 0., 0.);
  glVertex3f(tail_length ,tail_length ,0 );
  glVertex3f(tail_length ,-tail_length ,0 );
  glVertex3f(tail_length ,-tail_length ,-tail_height );
  glVertex3f(tail_length ,tail_length ,-tail_height );

  glNormal3f(-1., 0., 0.);
  glVertex3f(-tail_length ,tail_length ,0 );
  glVertex3f(-tail_length ,tail_length ,-tail_height );
  glVertex3f(-tail_length ,-tail_length ,-tail_height );
  glVertex3f(-tail_length ,-tail_length ,0 );

  glNormal3f(0., 1., 0.);
  glVertex3f(-tail_length ,tail_length ,-tail_height );
  glVertex3f(-tail_length ,tail_length ,0 );
  glVertex3f(tail_length ,tail_length ,0 );
  glVertex3f(tail_length ,tail_length ,-tail_height );

  glNormal3f(0., -1., 0.);
  glVertex3f(-tail_length ,-tail_length ,-tail_height );
  glVertex3f(tail_length ,-tail_length ,-tail_height );
  glVertex3f(tail_length ,-tail_length ,0 );
  glVertex3f(-tail_length ,-tail_length ,0 );

  glEnd();
}

/**
 * visu_gl_drawSmoothArrow:
 * @obj: (type gpointer): ...
 * @centering: a flag.
 * @tailLength: length of tail part.
 * @tailRadius: length of edge of tail part.
 * @tailN: number of edges in the approximation.
 * @tailRenderer: (allow-none): a given #VisuElementRenderer to take
 * the rendering properties of.
 * @hatLength: length of hat part.
 * @hatRadius: length of edge of hat part.
 * @hatN: number of edges in the approximation.
 * @hatRenderer: (allow-none): a given #VisuElementRenderer to take
 * the rendering properties of.
 *
 * Draw arrows, using sqaure edges. For rounded arrows, see
 * visu_gl_drawEdgeArrow().
 * 
 * Since: 3.6
 */
void visu_gl_drawSmoothArrow(GLUquadricObj *obj, VisuGlArrowCentering centering,
                             float tailLength, float tailRadius, float tailN,
                             const VisuElementRenderer *tailRenderer, float hatLength,
                             float hatRadius, float hatN, const VisuElementRenderer *hatRenderer)
{
  switch (centering)
    {
    case VISU_GL_ARROW_TAIL_CENTERED:
      glTranslatef(0.f, 0.f, tailLength / 2.f);
      break;
    case VISU_GL_ARROW_CENTERED:
      glTranslatef(0.f, 0.f, -(hatLength - tailLength) / 2.f);
      break;
    case VISU_GL_ARROW_BOTTOM_CENTERED:
      glTranslatef(0.f, 0.f, tailLength);
      break;
    default:
      break;
    }
  if (tailRenderer && hatRenderer)
    {
      visu_gl_setColor((VisuGl*)0, visu_element_renderer_getMaterial(hatRenderer),
                       visu_element_renderer_getColor(hatRenderer)->rgba);
      drawRoundedHat(obj, hatRadius,
		     hatLength, MAX(hatRadius, tailRadius)*1.03, hatN);
      visu_gl_setColor((VisuGl*)0, visu_element_renderer_getMaterial(tailRenderer),
                       visu_element_renderer_getColor(tailRenderer)->rgba);
      drawRoundedTail(obj, tailRadius, tailLength, tailN);
    }
  else if (tailRenderer)
    {
      drawRoundedHat(obj, hatRadius, hatLength,
		     MAX(hatRadius, tailRadius)*1.03, hatN);
      visu_gl_setColor((VisuGl*)0, visu_element_renderer_getMaterial(tailRenderer),
                       visu_element_renderer_getColor(tailRenderer)->rgba);
      drawRoundedTail(obj, tailRadius, tailLength, tailN);
    }
  else if (hatRenderer)
    {
      drawRoundedTail(obj, tailRadius, tailLength, tailN);
      glTranslatef(0, 0, tailLength);
      visu_gl_setColor((VisuGl*)0, visu_element_renderer_getMaterial(hatRenderer),
                       visu_element_renderer_getColor(hatRenderer)->rgba);
      drawRoundedHat(obj, hatRadius, hatLength,
		     MAX(hatRadius, tailRadius)*1.03, hatN);
    }
  else
    {
      drawRoundedHat(obj, hatRadius, hatLength,
		     MAX(hatRadius, tailRadius)*1.03, hatN);
      drawRoundedTail(obj, tailRadius, tailLength, tailN);
    }
}
/**
 * visu_gl_drawEdgeArrow:
 * @centering: a flag.
 * @tailLength: length of tail part.
 * @tailRadius: length of edge of tail part.
 * @tailRenderer: (allow-none): a given #VisuElementRenderer to take
 * the rendering properties of.
 * @hatLength: length of hat part.
 * @hatRadius: length of edge of hat part.
 * @hatRenderer: (allow-none): a given #VisuElementRenderer to take
 * the rendering properties of.
 *
 * Draw arrows, using sqaure edges. For rounded arrows, see
 * visu_gl_drawSmoothArrow().
 * 
 * Since: 3.6
 */
void visu_gl_drawEdgeArrow(VisuGlArrowCentering centering,
                           float tailLength, float tailRadius,
                           const VisuElementRenderer *tailRenderer, float hatLength,
                           float hatRadius, const VisuElementRenderer *hatRenderer)
{
  /* This one is a edged arrow. */
  switch (centering)
    {
    case VISU_GL_ARROW_TAIL_CENTERED:
      glTranslatef(0.f, 0.f, tailLength / 2.f);
      break;
    case VISU_GL_ARROW_CENTERED:
      glTranslatef(0.f, 0.f, tailLength - (hatLength + tailLength) / 2.f);
      break;
    case VISU_GL_ARROW_BOTTOM_CENTERED:
      glTranslatef(0.f, 0.f, tailLength);
      break;
    default:
      break;
    }
  if (tailRenderer && hatRenderer)
    {
      visu_gl_setColor((VisuGl*)0, visu_element_renderer_getMaterial(hatRenderer),
                       visu_element_renderer_getColor(hatRenderer)->rgba);
      drawEdgedHat(hatRadius, hatLength);
      visu_gl_setColor((VisuGl*)0, visu_element_renderer_getMaterial(tailRenderer),
                       visu_element_renderer_getColor(tailRenderer)->rgba);
      drawEdgedTail(tailRadius, tailLength);
    }
  else if (tailRenderer)
    {
      drawEdgedHat(hatRadius, hatLength);
      visu_gl_setColor((VisuGl*)0, visu_element_renderer_getMaterial(tailRenderer),
                       visu_element_renderer_getColor(tailRenderer)->rgba);
      drawEdgedTail(tailRadius, tailLength);
    }
  else if (hatRenderer)
    {
      drawEdgedTail(tailRadius, tailLength);
      visu_gl_setColor((VisuGl*)0, visu_element_renderer_getMaterial(hatRenderer),
                       visu_element_renderer_getColor(hatRenderer)->rgba);
      drawEdgedHat(hatRadius, hatLength);
    }
  else
    {
      drawEdgedHat(hatRadius, hatLength);
      drawEdgedTail(tailRadius, tailLength);
    }
}
/**
 * visu_gl_drawEllipsoid:
 * @obj: (type gpointer): ...
 * @aAxis: length of long axis.
 * @bAxis: length of short axis.
 * @n: number of edges for the sphere approximation.
 * @renderer: (allow-none): a given #VisuElementRenderer to take
 * the rendering properties of.
 *
 * Draw  an ellipsoid.
 *
 * Since: 3.6
 */
void visu_gl_drawEllipsoid(GLUquadricObj *obj,
                           float aAxis, float bAxis,
                           float n, const VisuElementRenderer *renderer)
{
  if (bAxis == 0.f)
    glScalef(1.f, 1.f, 10.f);
  else
    glScalef(1.f, 1.f, aAxis / bAxis);
  if (renderer)
    visu_gl_setColor((VisuGl*)0, visu_element_renderer_getMaterial(renderer),
                     visu_element_renderer_getColor(renderer)->rgba);
  gluSphere(obj, bAxis, n, n);
}
/**
 * visu_gl_drawTorus:
 * @obj: (type gpointer): ...
 * @radius: global radius.
 * @ratio: ratio on internal radius over global radius.
 * @nA: number of edges for the global radius.
 * @nB: number of edges for the internal radius.
 * @renderer: (allow-none): a given #VisuElementRenderer to take
 * the rendering properties of.
 *
 * Draw  a torus.
 *
 * Since: 3.5
 */
void visu_gl_drawTorus(GLUquadricObj *obj _U_, float radius,
                       float ratio, int nA, int nB, const VisuElementRenderer *renderer)
{
  int i, j;
  float tp, tc, dtA, dtB, xp1, yp1, xp2, yp2, xc1, yc1, xc2, yc2, alpha;
  float a[3], b[3], c[3], d[3], aa[3], bb[3], cc[3], dd[3];

  if (renderer)
    visu_gl_setColor((VisuGl*)0, visu_element_renderer_getMaterial(renderer),
                     visu_element_renderer_getColor(renderer)->rgba);

  glBegin(GL_QUADS);

  glEnable(GL_NORMALIZE);

  dtA = 2.f*G_PI/(float)nA;
  dtB = 2.f*G_PI/(float)nB;
  alpha = 1.f / ratio;

  /* loop on the position of the centre of the circle
     which stands vertical in the plane, and which
     is rotated to create the torus */
  for (i = 0; i < nA; i++)
    {
      tp = (float)i*dtA;
      xp1 = radius*cos(tp);
      yp1 = radius*sin(tp);
      xp2 = radius*cos(tp+dtA);
      yp2 = radius*sin(tp+dtA);

      /* loop around the circle */
      for (j = 0; j < nB; j++)
	{
	  tc = (float)j*dtB;
	  xc1 = alpha*cos(tc);
	  yc1 = alpha*sin(tc);
	  xc2 = alpha*cos(tc+dtB);
	  yc2 = alpha*sin(tc+dtB);

	  a[0] = xp1 * (1.f + xc1);
	  a[1] = yp1 * (1.f + xc1);
	  a[2] = yc1 * radius;

	  b[0] = xp2 * (1.f + xc1);
	  b[1] = yp2 * (1.f + xc1);
	  b[2] = yc1 * radius;

	  c[0] = xp1 * (1.f + xc2);
	  c[1] = yp1 * (1.f + xc2);
	  c[2] = yc2 * radius;

	  d[0] = xp2 * (1.f + xc2);
	  d[1] = yp2 * (1.f + xc2);
	  d[2] = yc2 * radius;

	  aa[0] = xp1 * xc1;
	  aa[1] = yp1 * xc1;
	  aa[2] = yc1 * radius;

	  bb[0] = xp2 * xc1;
	  bb[1] = yp2 * xc1;
	  bb[2] = yc1 * radius;
	      
	  cc[0] = xp1 * xc2;
	  cc[1] = yp1 * xc2;
	  cc[2] = yc2 * radius;

	  dd[0] = xp2 * xc2;
	  dd[1] = yp2 * xc2;
	  dd[2] = yc2 * radius;	      

	  /* Point A */
	  glNormal3fv(aa);
	  glVertex3fv(a);

	  /* Point B */
	  glNormal3fv(bb);
	  glVertex3fv(b);

	  /* Point D */
	  glNormal3fv(dd);
	  glVertex3fv(d);

	  /* Point C */
	  glNormal3fv(cc);
	  glVertex3fv(c);
	}
	  
    }

  glDisable(GL_NORMALIZE);
  glEnd();
}

/**
 * visu_gl_drawDistance:
 * @xyzRef: cartesian coordinates of first ref.
 * @xyz: cartesian coordinates of current point.
 * @drawLength: a boolean.
 *
 * Draw a distance mark between @xyzRef and @xyz. A distance mark is a
 * colour inverted line and two squared marks on node. @drawLength
 * is a flag to display or not the distance value.
 *
 * Since: 3.6
 */
void visu_gl_drawDistance(float xyzRef[3], float xyz[3], gboolean drawLength)
{
  float dist;
  int i;
  char distStr[8];

  glLineWidth(1);
  glColor4f(1., 1., 1., 0.);
  glBegin(GL_LINES);
  glVertex3fv(xyzRef);
  glVertex3fv(xyz);
  glEnd();
  glPointSize(8.);
  glBegin(GL_POINTS);
  glVertex3fv(xyzRef);
  glVertex3fv(xyz);
  glEnd();
  if (drawLength)
    {
      dist = 0.;
      for (i = 0; i < 3; i++)
	dist += (xyzRef[i] - xyz[i]) * (xyzRef[i] - xyz[i]);
      sprintf(distStr, "%7.3f", sqrt(dist));
      distStr[7] = '\0';
      DBG_fprintf(stderr, "OpenGL Objects: draw a link with distance %s.\n", distStr);
      glRasterPos3f((xyzRef[0] + xyz[0]) / 2.f,
		    (xyzRef[1] + xyz[1]) / 2.f,
		    (xyzRef[2] + xyz[2]) / 2.f); 
      visu_gl_text_drawChars(distStr, VISU_GL_TEXT_NORMAL);
    }
}

/**
 * visu_gl_drawAngle:
 * @xyzRef: cartesian coordinates of first ref.
 * @xyzRef2: cartesian coordinates of second ref.
 * @xyz: cartesian coordinates of current point.
 * @id: a counter.
 * @drawLength: a boolean.
 *
 * Draw an angle mark by to distance marks and a disk taking @xyzRef
 * as central point and @xyzRef2 and @xyz as the two positions. @id is
 * a counter to obtain different colours for the disk and @drawLength
 * is a flag to display or not the angle value in degrees.
 *
 * Since: 3.6
 */
void visu_gl_drawAngle(float xyzRef[3], float xyzRef2[3],
				float xyz[3], guint id, gboolean drawLength)
{
  float dist, u[3], v[3], M[3], c, s, theta, thetaM, l;
  int i, iM;
  char distStr[9];

  /* We calculate the new basis set. */
  u[0] = xyz[0] - xyzRef[0];
  u[1] = xyz[1] - xyzRef[1];
  u[2] = xyz[2] - xyzRef[2];
  l = sqrt(u[0] * u[0] + u[1] * u[1] + u[2] * u[2]);
  dist = 1.f / l;
  u[0] *= dist;
  u[1] *= dist;
  u[2] *= dist;

  v[0] = xyzRef2[0] - xyzRef[0];
  v[1] = xyzRef2[1] - xyzRef[1];
  v[2] = xyzRef2[2] - xyzRef[2];
  dist = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
  l = MIN(l, dist);
  dist = 1.f / dist;
  v[0] *= dist;
  v[1] *= dist;
  v[2] *= dist;
  l /= 4.f;

  dist = u[0] * v[0] + u[1] * v[1] + u[2] * v[2];
  thetaM = acos(dist);
  iM     = MAX((int)(thetaM / G_PI * 20.f), 2);
  if (drawLength)
    {
      sprintf(distStr, "%5.1f\302\260", thetaM * 180.f / G_PI);
      distStr[8] = '\0';
      DBG_fprintf(stderr, "OpenGL Objects: the angle is %g.\n",
		  thetaM * 180.f / G_PI);
    }

  v[0] -= dist * u[0];
  v[1] -= dist * u[1];
  v[2] -= dist * u[2];
  dist = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
  if (dist < 1e-3)
    {
      v[0] = 1.f;
      v[1] = 1.f;
      v[2] = 1.f;
      i = 0;
      if (u[0] != 0.f)
	i = 0;
      else if (u[1] != 0.f)
	i = 1;
      else if (u[2] != 0.f)
	i = 2;
      else
	g_warning("Selected node and reference are the same.");
      v[i] = 1.f - (u[0] + u[1] + u[2]) / u[i];
      dist = 1.f / sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
    }
  else
    dist = 1.f / dist;
  v[0] *= dist;
  v[1] *= dist;
  v[2] *= dist;

  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glColor4fv(tool_color_new_bright(id)->rgba);
  if (drawLength)
    {
      theta = 0.5f * thetaM;
      c = cos(theta);
      s = sin(theta);
      M[0] = xyzRef[0] + (u[0] * c + v[0] * s) * l * 1.33f;
      M[1] = xyzRef[1] + (u[1] * c + v[1] * s) * l * 1.33f;
      M[2] = xyzRef[2] + (u[2] * c + v[2] * s) * l * 1.33f;
      glRasterPos3fv(M);
      visu_gl_text_drawChars(distStr, VISU_GL_TEXT_SMALL); 
    }

  glLineWidth(1);
  glBegin(GL_LINES);
  glVertex3fv(xyzRef);
  glVertex3fv(xyzRef2);
  glVertex3fv(xyzRef);
  glVertex3fv(xyz);
  glEnd();

  glEnable(GL_POLYGON_OFFSET_FILL);
  glPolygonOffset(1.0, 1.0);

  glBegin(GL_POLYGON);
  glVertex3fv(xyzRef);
  for (i = 0; i < iM; i++)
    {
      theta = (float)i / ((float)iM - 1.f) * thetaM;
      c = cos(theta);
      s = sin(theta);
      M[0] = xyzRef[0] + (u[0] * c + v[0] * s) * l;
      M[1] = xyzRef[1] + (u[1] * c + v[1] * s) * l;
      M[2] = xyzRef[2] + (u[2] * c + v[2] * s) * l;
      glVertex3fv(M);
    }
  glEnd();

  glPointSize(4.);
  glBegin(GL_POINTS);
  glVertex3fv(xyzRef2);
  glVertex3fv(xyz);
  glEnd();

  glDisable(GL_POLYGON_OFFSET_FILL);
  glColor3f (0.0, 0.0, 0.0);
  glLineWidth(1.1);
  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  glBegin(GL_POLYGON);
  glVertex3fv(xyzRef);
  for (i = 0; i < iM; i++)
    {
      theta = (float)i / ((float)iM - 1.f) * thetaM;
      c = cos(theta);
      s = sin(theta);
      M[0] = xyzRef[0] + (u[0] * c + v[0] * s) * l;
      M[1] = xyzRef[1] + (u[1] * c + v[1] * s) * l;
      M[2] = xyzRef[2] + (u[2] * c + v[2] * s) * l;
      glVertex3fv(M);
    }
  glEnd();
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

/*   glColor4f(0., 0., 0., 1.); */
/*   glBegin(GL_LINE_LOOP); */
/*   glVertex3fv(xyzRef); */
/*   for (i = 0; i < iM; i++) */
/*     { */
/*       theta = (float)i / ((float)iM - 1.f) * thetaM; */
/*       c = cos(theta); */
/*       s = sin(theta); */
/*       M[0] = xyzRef[0] + (u[0] * c + v[0] * s) * l; */
/*       M[1] = xyzRef[1] + (u[1] * c + v[1] * s) * l; */
/*       M[2] = xyzRef[2] + (u[2] * c + v[2] * s) * l; */
/*       glVertex3fv(M); */
/*     } */
/*   glEnd(); */

  glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
}
