/* Test 2D-tesselation.
Features:
- get point-index of all tesselated points

TODO:

-) combine returns points already defined;                       DONE
   - test if point already defined;
     if yes: return its address, else create new point.

-) creates triangles with zero-width;                            <<<<<<
   - test if points of triangles are linear;
     if yes: repair (eliminate triangle and fix neighbour-triangle).

-) make library for grid-tesselation and documentation for it.
   - input:   OB, 0-n IB's, grid (rectangles).
   - output:  tesselated triangles.



*/

#ifdef _MSC_VER
#define GLU_CB static void CALLBACK
#else
#define GLU_CB static void
#endif


#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>



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



#include "../ut/ut_geo.h"                // Point-def


#define COL_RED    0
#define COL_YELLOW 1
#define COL_SUR    2

static GLfloat colTab [][4] = {
  1.0, 0.0, 0.0, 1.0,
  1.0, 1.0, 0.0, 1.0,
  0.6, 0.5, 0.5, 1.0};


static int   GLU_Err=0;

#define GLC_PT_MAX 1000  // point-set (including combine-points)
static Point GLC_pa[GLC_PT_MAX];
static int GLC_pNr;

#define GLP_PT_MAX 1000  // patch-points
static Point GLP_pa[GLP_PT_MAX];
static int GLP_pNr, GLP_typ;

#define GLP_TRI_MAX 300
static Triangle GLP_ta[GLP_TRI_MAX];




GLU_CB GLCB2_error (GLenum errNo);
GLU_CB GLCB2_begin (GLenum type);
GLU_CB GLCB2_vertex (GLdouble*);
GLU_CB GLCB2_combi (GLdouble*, GLdouble**, GLfloat*, GLdouble**);
GLU_CB GLCB2_edge (GLboolean flag);


/*
GLU_CB GLCB2_iData  (GLdouble*, void *data);
//================================================================
  GLU_CB GLCB2_iData (GLdouble *vertex, void *data) {
//================================================================
// data provides the pointer given in last gluTessBeginPolygon-call; USELESS.

   printf ("glVertex3f (%5.3f, %5.3f, %5.3f)\n",
     vertex[0], vertex[1], vertex[2]);

}
*/


//================================================================
  GLU_CB GLCB2_vertex (GLdouble *vertex) {
//================================================================


  int   ipt;


   printf ("glVertex3f (%5.3f, %5.3f, %5.3f - %d)\n",
     vertex[0], vertex[1], vertex[2], GLP_pNr);

  if(GLU_Err) return;


  // find point-index. For this all points (including combine-points)
  // must be in the same set.
  ipt = ((char*)vertex - (char*)GLC_pa) / sizeof(Point);
    printf(" point-index-ipt=%d\n",ipt);


  GLP_pa[GLP_pNr].x = vertex[0];
  GLP_pa[GLP_pNr].y = vertex[1];
  GLP_pa[GLP_pNr].z = vertex[2];

  if(GLP_pNr < GLP_PT_MAX) ++GLP_pNr;
  else printf("********* ERROR GLCB2_vertex EOF\n");

}


//=========================================================
  GLU_CB GLCB2_combi (GLdouble newPt[3],
                     GLdouble *data[4],
                     GLfloat  weight[4],
                     GLdouble **dataOut) {
//=========================================================
// MUST set dataOut to address of static stored vertex newPt !
// This points must be kept until end of tesselation.

  int    i1;

  printf(" GLCB2_combi %lf %lf %lf - %d\n",
    newPt[0], newPt[1], newPt[2], GLC_pNr);

  if(GLU_Err) return;


  // test if point already defined.
  for(i1=0; i1<GLC_pNr; ++i1) {
    if(UT3D_ck2D_equ_2pt ((Point*)newPt, &GLC_pa[i1], UT_TOL_cv)) {
      // points are equal
        printf("   point already defined = %d\n",i1);
      *dataOut = (GLdouble*)&GLC_pa[i1];
      return;
    }
  }


  // point is new; add to pointset.
  GLC_pa[GLC_pNr].x = newPt[0];
  GLC_pa[GLC_pNr].y = newPt[1];
  GLC_pa[GLC_pNr].z = newPt[2];


  // set dataOut to address of static stored vertex newPt
  *dataOut = (GLdouble*)&GLC_pa[GLC_pNr];


  if(GLC_pNr < GLC_PT_MAX) ++GLC_pNr;
  else printf("********* ERROR GLCB2_combi EOF\n");


  return;
}


//=========================================================
  GLU_CB GLCB2_edge (GLboolean flag) {
//=========================================================
// flag TRUE=1: folling vertex is on boundary; else not.

  if(flag == GL_TRUE)
  printf("GLCB2_edge %d\n",flag);

  // GLU_flag = flag;

  return;

}


//================================================================
  GLU_CB GLCB2_begin (GLenum type) {
//================================================================

   printf ("glBegin(");

   // glBegin (type);  //  resubmit rendering directive

  GLP_pNr = 0;
  GLP_typ = type;


   switch (type) {  /*  print diagnostic message  */
     case GL_LINES:
       printf ("GL_LINES)\n");
       break;
     case GL_LINE_LOOP:
       printf ("GL_LINE_LOOP)\n");
       break;
     case GL_LINE_STRIP:
       printf ("GL_LINE_STRIP)\n");
       break;
     case GL_TRIANGLES:
       printf ("GL_TRIANGLES)\n");
       break;
     case GL_TRIANGLE_STRIP:
       printf ("GL_TRIANGLE_STRIP)\n");
       break;
     case GL_TRIANGLE_FAN:
       printf ("GL_TRIANGLE_FAN)\n");
       break;
     case GL_QUADS:
       printf ("GL_QUADS)\n");
       break;
     case GL_QUAD_STRIP:
       printf ("GL_QUAD_STRIP)\n");
       break;
     case GL_POLYGON:
       printf ("GL_POLYGON)\n");
       break;
     default:
   break;
   }
}


//================================================================
  GLU_CB GLCB2_end () {
//================================================================
// Input:
//   type of patch: GLP_typ; all patch-points are in GLP_pa, nr = GLP_pNr.


   printf ("glEnd()\n");
   // printf ("glEnd() ptNr=%d\n",ptNr);


  // display as points
  tst_gl2_point_d (GLP_pa, GLP_pNr, (GLfloat*)&colTab[COL_YELLOW]);


  // display triangles (transform patch into triangles and display)
  tst_gl2_patch__ (GLP_typ, GLP_pa, GLP_pNr);


}


//=========================================================
  GLU_CB GLCB2_error (GLenum errNo) {
//=========================================================

  const GLubyte *estring;

  printf("****** GLCB2_error %d\n",errNo);

  estring = gluErrorString(errNo);
  fprintf (stderr, "Nurbs Error: %s\n", estring);
  GLU_Err = -1;
  // exit (0);


}


//========================================================================
  int tst_gl2_point_d (Point *GLC_pab, int pNr, GLfloat *glCol) {
//========================================================================
// draw pNr points into open glList 

  int   ii, ip;

  glDisable(GL_LIGHTING);

  glPointSize (5.0);

  // glColor3f(1.0, 1.0, 0.0);
  glColor3fv (glCol);

  glBegin(GL_POINTS);
  for(ii=0; ii<pNr; ++ii) {
      // printf(" pt[%d]= %f %f %f\n",ip,GLC_pab[ip], GLC_pab[ip+1], GLC_pab[ip+2]);
    glVertex3dv ((double*)&GLC_pab[ii]);
  }
  glEnd();

  glEnable(GL_LIGHTING);

  return 0;

}


//===========================================================================
  int tst_gl2_patch__ (int pTyp, Point *pa, int pNr) {
//===========================================================================
// display triangles of OpenGL-patch

static int ti=0;

  int      irc, i1, tNr;


  printf("tst_gl2_patch__ ti=%d\n",ti);


  // get tNr = nr of triangles for this patch
  tNr = UTRI_triaNr_patch (pNr, pTyp);
  if(tNr < 1) {printf("***** tst_gl2_patch__ E001\n"); return -1;}


  // get space for <tNr> triangles
  // triTab = (Triangle*) MEM_alloc_tmp (tNr * sizeof(Triangle));


  // get <tNr> triangles from patch (GLC_pab, pNr, pTyp)
  tNr = GLP_TRI_MAX;
  irc = UTRI_ntria_patch (GLP_ta, &tNr, GLC_pab, pNr, pTyp);
  if(irc < 0) return -1;


  // find triangle with zero-width;
  // (cannot repair; need neighbour-triangles for repair).
  test_ck_bugTriaZero (GLP_ta, tNr);




  // display triangles
  for(i1=0; i1<tNr; ++i1) {
    printf(" ti=%d p1 %f %f %f\n", ti,
           GLP_ta[i1].pa[0]->x,GLP_ta[i1].pa[0]->y,GLP_ta[i1].pa[0]->z);
    printf(" ti=%d p2 %f %f %f\n", ti,
           GLP_ta[i1].pa[1]->x,GLP_ta[i1].pa[1]->y,GLP_ta[i1].pa[1]->z);
    printf(" ti=%d p3 %f %f %f\n", ti,
           GLP_ta[i1].pa[2]->x,GLP_ta[i1].pa[2]->y,GLP_ta[i1].pa[2]->z);
    GR_Disp_tria (&GLP_ta[i1], 9);
    GR_Disp_triv  (&GLP_ta[i1], 3, ti, -1);
    ++ti;
    GR_Disp_tris (&GLP_ta[i1], 8);
  }

  return 0;

}


//================================================================
  int tst_gl2_2Dtess3 () {     
//================================================================
// tst_gl2_2Dtess3      boxes
// BUGS: 
// - combine macht alle Punkte neu !!!! ????
// - macht 3Ecke die linear sind (ohne Fläche)
// see GLU_tess__
// see redBook/tess.c, redBook/tesswind.c

  GLUtesselator *ot1;
  int           i1, i2, ii, ip1, idx, idy;
  Point pta[] =
    {{ 0.0,  0.0, 0.0},  // 0  OB
     { 3.0,  0.0, 0.0},
     { 7.0,  0.0, 0.0},
     {10.0,  0.0, 0.0},
    
     { 0.0,  3.0, 0.0},  // 4
     { 3.0,  3.0, 0.0},
     { 7.0,  3.0, 0.0},
     {10.0,  3.0, 0.0},

     { 0.0,  7.0, 0.0},  // 8
     { 3.0,  7.0, 0.0},
     { 7.0,  7.0, 0.0},
     {10.0,  7.0, 0.0},

     { 0.0, 10.0, 0.0}, // 12
     { 3.0, 10.0, 0.0},
     { 7.0, 10.0, 0.0},
     {10.0, 10.0, 0.0},

     { 1.0,  3.0, 0.0},  // 16  IB
     { 5.0,  5.0, 0.0},
     { 5.0,  1.0, 0.0}};

  //----------------------------------------------------------------
  printf("tst_gl2_2Dtess3 \n");
  // get all points into pointset GLC_pa
  GLC_pNr = sizeof(pta)/sizeof(Point);
  for(i1=0; i1<GLC_pNr; ++i1) {
    GLC_pa[i1] = pta[i1];
  }


  //----------------------------------------------------------------
  // glEnable(GL_LIGHTING);
  // glEnable(GL_AUTO_NORMAL);
  // glEnable(GL_DEPTH_TEST);
  // glEnable(GL_NORMALIZE);


  //----------------------------------------------------------------
  glRenderMode(GL_FEEDBACK);

  GLU_Err = 0;
  ot1 = gluNewTess();

  // define the callback-functions
  gluTessCallback (ot1, GLU_TESS_BEGIN,      (void*)&GLCB2_begin);
  gluTessCallback (ot1, GLU_TESS_VERTEX,   (void*)&GLCB2_vertex);
  // gluTessCallback (ot1, GLU_TESS_VERTEX_DATA,(void*)&GLCB2_iData);
  gluTessCallback (ot1, GLU_TESS_COMBINE,    (void*)&GLCB2_combi);
  gluTessCallback (ot1, GLU_TESS_EDGE_FLAG,  (void*)&GLCB2_edge);
  gluTessCallback (ot1, GLU_TESS_END,        (void*)&GLCB2_end);
  gluTessCallback (ot1, GLU_TESS_ERROR,      (void*)&GLCB2_error);

  // union oder a-b oder b-a aber nicht das gemeinsame von a,b
  // gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE);

  // 1(OB=CCW) = surface
  // 1(OB=CCW) + -1(IB=CW) = 0 = hole
  gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);

  // gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);

  // gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ABS_GEQ_TWO);


  // triangles, not boundary-lines
  gluTessProperty(ot1, GLU_TESS_BOUNDARY_ONLY, GL_FALSE);
  // gluTessProperty(ot1, GLU_TESS_BOUNDARY_ONLY, GL_TRUE); // get lines


  // gluTessProperty(ot1, GLU_TESS_TOLERANCE, 0.9); // maxVal 1. ?
  // unsupported ..


  gluTessNormal (ot1, 0., 0., 1.);
  // glNormal3d (0., 0., 1.);


  //================================================================
  gluTessBeginPolygon (ot1, NULL);
  // add contour from single point:     IGNORED !
  // add contour from linesegment only: IGNORED !

/*
  // add CCW overallbox.  Completly OK.
    gluTessBeginContour (ot1);
      gluTessVertex (ot1, (double*)&GLC_pa[0], (double*)&GLC_pa[0]);
      gluTessVertex (ot1, (double*)&GLC_pa[3], (double*)&GLC_pa[3]);
      gluTessVertex (ot1, (double*)&GLC_pa[15], (double*)&GLC_pa[15]);
      gluTessVertex (ot1, (double*)&GLC_pa[12], (double*)&GLC_pa[12]);
    gluTessEndContour (ot1);
*/

/*
  // test contour from single point or linesegment only: IGNORED !
    gluTessBeginContour (ot1);
      gluTessVertex (ot1, (double*)&GLC_pa[4], (double*)&GLC_pa[4]);
    gluTessEndContour (ot1);
*/


  // add all boxes CCW; horizontally then vertically.
  // outer-boundary horizontal must be CCW;
  for(i1=0; i1<2; ++i1) { // vert loop
    ip1 = i1 * 4;
    for(i2=0; i2<2; ++i2) { // hor loop
        printf(" nxt rect %d\n",ip1);
      gluTessBeginContour (ot1);
        gluTessVertex (ot1, (double*)&GLC_pa[ip1], (double*)&GLC_pa[ip1]);
        gluTessVertex (ot1, (double*)&GLC_pa[ip1+1], (double*)&GLC_pa[ip1+1]);
        gluTessVertex (ot1, (double*)&GLC_pa[ip1+5], (double*)&GLC_pa[ip1+5]);
        gluTessVertex (ot1, (double*)&GLC_pa[ip1+4], (double*)&GLC_pa[ip1+4]);
      gluTessEndContour (ot1);
      ip1 += 1;
    }
  }


  // inner-boundary must be CW;
  ip1 = 16;
    gluTessBeginContour (ot1);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1], (double*)&GLC_pa[ip1]);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1+1], (double*)&GLC_pa[ip1+1]);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1+2], (double*)&GLC_pa[ip1+2]);
    gluTessEndContour (ot1);


  // start tesselation
  gluTessEndPolygon(ot1);

  gluDeleteTess (ot1);

  glRenderMode(GL_RENDER);

  return 0;

}


//================================================================
  int tst_gl2_2Dtess2 () {     
//================================================================
// tst_gl2_2Dtess2      test horizontal & vertical rectangles
// BUGS: 
// - combine macht alle Punkte neu !!!! ????
// - macht 3Ecke die linear sind (ohne Fläche)
// see GLU_tess__
// see redBook/tess.c, redBook/tesswind.c

  GLUtesselator *ot1; 
  int           i1, ii, ip1, idx, idy;
  Point pta[] =
    {{ 0.0,  0.0, 0.0},  // 0  OB
     { 3.0,  0.0, 0.0},
     { 7.0,  0.0, 0.0},
     {10.0,  0.0, 0.0}, 

     { 0.0,  3.0, 0.0},  // 4
     { 3.0,  3.0, 0.0},
     { 7.0,  3.0, 0.0},
     {10.0,  3.0, 0.0},

     { 0.0,  7.0, 0.0},  // 8
     { 3.0,  7.0, 0.0},
     { 7.0,  7.0, 0.0},
     {10.0,  7.0, 0.0},

     { 0.0, 10.0, 0.0}, // 12
     { 3.0, 10.0, 0.0},
     { 7.0, 10.0, 0.0},
     {10.0, 10.0, 0.0},

     { 1.0,  3.0, 0.0},  // 16  IB
     { 5.0,  5.0, 0.0},
     { 5.0,  1.0, 0.0}};
    

  //----------------------------------------------------------------
  printf("tst_gl2_2Dtess2 \n");
  // get all points into pointset GLC_pa
  GLC_pNr = sizeof(pta)/sizeof(Point);
  for(i1=0; i1<GLC_pNr; ++i1) {
    GLC_pa[i1] = pta[i1];
  }


  //----------------------------------------------------------------
  // glEnable(GL_LIGHTING);
  // glEnable(GL_AUTO_NORMAL);
  // glEnable(GL_DEPTH_TEST);
  // glEnable(GL_NORMALIZE);


  //----------------------------------------------------------------
  glRenderMode(GL_FEEDBACK);

  GLU_Err = 0;
  ot1 = gluNewTess();

  // define the callback-functions
  gluTessCallback (ot1, GLU_TESS_BEGIN,      (void*)&GLCB2_begin);
  gluTessCallback (ot1, GLU_TESS_VERTEX,   (void*)&GLCB2_vertex);
  // gluTessCallback (ot1, GLU_TESS_VERTEX_DATA,(void*)&GLCB2_iData);
  gluTessCallback (ot1, GLU_TESS_COMBINE,    (void*)&GLCB2_combi);
  gluTessCallback (ot1, GLU_TESS_EDGE_FLAG,  (void*)&GLCB2_edge);
  gluTessCallback (ot1, GLU_TESS_END,        (void*)&GLCB2_end);
  gluTessCallback (ot1, GLU_TESS_ERROR,      (void*)&GLCB2_error);

  // union oder a-b oder b-a aber nicht das gemeinsame von a,b
  // gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE);

  // 1(OB=CCW) = surface
  // 1(OB=CCW) + -1(IB=CW) = 0 = hole
  // gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);

  // gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);

  gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ABS_GEQ_TWO);


  // triangles, not boundary-lines
  gluTessProperty(ot1, GLU_TESS_BOUNDARY_ONLY, GL_FALSE);
  // gluTessProperty(ot1, GLU_TESS_BOUNDARY_ONLY, GL_TRUE); // get lines


  // gluTessProperty(ot1, GLU_TESS_TOLERANCE, 0.9); // maxVal 1. ?
  // unsupported ..


  gluTessNormal (ot1, 0., 0., 1.);
  // glNormal3d (0., 0., 1.);


  //================================================================
  gluTessBeginPolygon (ot1, NULL);

  idx = 4;
  idy = 4;
  ip1 = 0;

  // outer-boundary horizontal must be CCW;
  for(ii=0; ii<3; ++ii) {
    gluTessBeginContour (ot1);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1], (double*)&GLC_pa[ip1]);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1+3], (double*)&GLC_pa[ip1+3]);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1+7], (double*)&GLC_pa[ip1+7]);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1+4], (double*)&GLC_pa[ip1+4]);
    gluTessEndContour (ot1);
    ip1 += idx;
  }

  // outer-boundary vertical must be CCW;
  ip1 = 0;
  for(ii=0; ii<3; ++ii) {
    gluTessBeginContour (ot1);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1], (double*)&GLC_pa[ip1]);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1+1], (double*)&GLC_pa[ip1+1]);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1+13], (double*)&GLC_pa[ip1+13]);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1+12], (double*)&GLC_pa[ip1+12]);
    gluTessEndContour (ot1);
    ip1 += 1;
  }


  ip1 = 16;
  // inner-boundary must be CW;
    gluTessBeginContour (ot1);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1], (double*)&GLC_pa[ip1]);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1+1], (double*)&GLC_pa[ip1+1]);
      gluTessVertex (ot1, (double*)&GLC_pa[ip1+2], (double*)&GLC_pa[ip1+2]);
    gluTessEndContour (ot1);


  gluTessEndPolygon(ot1);

  gluDeleteTess (ot1);

  glRenderMode(GL_RENDER);

  return 0;

}


//================================================================
  int tst_gl2_2Dtess1 () {
//================================================================
// see GLU_tess__
// see redBook/tess.c, redBook/tesswind.c

  GLUtesselator *ot1;
  int           i1, ip1;
  Point pta[] =
    {{ 0.0,  0.0, 0.0},  // 0  OB
     { 3.0,  0.0, 0.0},
     { 7.0,  0.0, 0.0},
     {10.0,  0.0, 0.0},
     { 0.0, 10.0, 0.0},
     { 3.0, 10.0, 0.0},
     { 7.0, 10.0, 0.0},
     {10.0, 10.0, 0.0},
     { 1.0,  3.0, 0.0},  // 8  IB
     { 5.0,  5.0, 0.0},
     { 5.0,  1.0, 0.0}};


  //----------------------------------------------------------------
  printf("tst_gl2_2Dtess1 \n");

  // get all points into pointset GLC_pa
  GLC_pNr = sizeof(pta)/sizeof(Point);
  for(i1=0; i1<GLC_pNr; ++i1) {
    GLC_pa[i1] = pta[i1];
  }


  //----------------------------------------------------------------
  // glEnable(GL_LIGHTING);
  // glEnable(GL_AUTO_NORMAL);
  // glEnable(GL_DEPTH_TEST);
  // glEnable(GL_NORMALIZE);


  //----------------------------------------------------------------
  glRenderMode(GL_FEEDBACK);

  GLU_Err = 0;
  ot1 = gluNewTess();

  // define the callback-functions
  gluTessCallback (ot1, GLU_TESS_BEGIN,      (void*)&GLCB2_begin);
  gluTessCallback (ot1, GLU_TESS_VERTEX,   (void*)&GLCB2_vertex);
  // gluTessCallback (ot1, GLU_TESS_VERTEX_DATA,(void*)&GLCB2_iData);
  gluTessCallback (ot1, GLU_TESS_COMBINE,    (void*)&GLCB2_combi);
  gluTessCallback (ot1, GLU_TESS_EDGE_FLAG,  (void*)&GLCB2_edge);
  gluTessCallback (ot1, GLU_TESS_END,        (void*)&GLCB2_end);
  gluTessCallback (ot1, GLU_TESS_ERROR,      (void*)&GLCB2_error);


  // union oder a-b oder b-a aber nicht das gemeinsame von a,b
  gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE);

  // 1(OB=CCW) = surface
  // 1(OB=CCW) + -1(IB=CW) = 0 = hole
  // gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);

  // gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);

  // gluTessProperty (ot1, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ABS_GEQ_TWO);


  // triangles, not boundary-lines
  gluTessProperty(ot1, GLU_TESS_BOUNDARY_ONLY, GL_FALSE);
  // gluTessProperty(ot1, GLU_TESS_BOUNDARY_ONLY, GL_TRUE); // get lines


  // gluTessProperty(ot1, GLU_TESS_TOLERANCE, 0.01); // maxVal 1. ?


  gluTessNormal (ot1, 0., 0., 1.); 
  // glNormal3d (0., 0., 1.);


  //================================================================
  gluTessBeginPolygon (ot1, NULL);

    // outer-boundary must be CCW;
    gluTessBeginContour (ot1);
      gluTessVertex (ot1, (double*)&GLC_pa[0], (double*)&GLC_pa[0]);
      gluTessVertex (ot1, (double*)&GLC_pa[1], (double*)&GLC_pa[1]);
      gluTessVertex (ot1, (double*)&GLC_pa[5], (double*)&GLC_pa[5]);
      gluTessVertex (ot1, (double*)&GLC_pa[4], (double*)&GLC_pa[4]);
    gluTessEndContour (ot1);

    gluTessBeginContour (ot1);
      gluTessVertex (ot1, (double*)&GLC_pa[1], (double*)&GLC_pa[1]);
      gluTessVertex (ot1, (double*)&GLC_pa[2], (double*)&GLC_pa[2]);
      gluTessVertex (ot1, (double*)&GLC_pa[6], (double*)&GLC_pa[6]);
      gluTessVertex (ot1, (double*)&GLC_pa[5], (double*)&GLC_pa[5]);
    gluTessEndContour (ot1);

    gluTessBeginContour (ot1);
      gluTessVertex (ot1, (double*)&GLC_pa[2], (double*)&GLC_pa[2]);
      gluTessVertex (ot1, (double*)&GLC_pa[3], (double*)&GLC_pa[3]);
      gluTessVertex (ot1, (double*)&GLC_pa[7], (double*)&GLC_pa[7]);
      gluTessVertex (ot1, (double*)&GLC_pa[6], (double*)&GLC_pa[6]);
    gluTessEndContour (ot1);


    // inner-boundary must be CW;
    gluTessBeginContour (ot1);
      gluTessVertex (ot1, (double*)&GLC_pa[8], (double*)&GLC_pa[8]);
      gluTessVertex (ot1, (double*)&GLC_pa[9], (double*)&GLC_pa[9]);
      gluTessVertex (ot1, (double*)&GLC_pa[10], (double*)&GLC_pa[10]);
    gluTessEndContour (ot1);

  gluTessEndPolygon(ot1);




  gluDeleteTess (ot1);

  glRenderMode(GL_RENDER);
  
  return 0;

}


//================================================================
  int tst_gl2 () {
//================================================================

  long   dli;
  GLuint dlInd;


  dli = -1;
  dlInd = GL_fix_DL_ind (&dli);

  glNewList (dlInd, GL_COMPILE);


  //...........................................................
  // tst_gl2_2Dtess1 (); // test vertical rectangles

  // tst_gl2_2Dtess2 ();    // test horizontal & vertical rectangles

  tst_gl2_2Dtess3 ();       // boxes, one by one ..

  //...........................................................
  glEndList ();


  return 0;

}

 
//=========================================================
  int gCad_fini () {
//=========================================================

  AP_User_reset ();

  return 0;
}


//=========================================================
  int gCad_main () {
//=========================================================


  TX_Print(">>>>>>>>> gCad_main aus tst_gl2 <<<<<<<<<<\n");

  tst_gl2 ();

  DL_Redraw ();

  gCad_fini ();

  return 0;
}


// EOF
