blob: c773fbaae4b2d3b62ebd82b79e2f69e7d322ab07 [file] [log] [blame]
/* $Id: tess.c,v 1.1 1999/08/19 00:55:42 jtg Exp $ */
/*
* Mesa 3-D graphics library
* Version: 3.1
* Copyright (C) 1995-1999 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.
*/
/*
* $Log: tess.c,v $
* Revision 1.1 1999/08/19 00:55:42 jtg
* Initial revision
*
* Revision 1.11 1999/02/27 13:55:31 brianp
* fixed BeOS-related GLU typedef problems
*
* Revision 1.10 1999/01/03 03:23:15 brianp
* now using GLAPIENTRY and GLCALLBACK keywords (Ted Jump)
*
* Revision 1.9 1998/06/01 01:10:29 brianp
* small update for Next/OpenStep from Alexander Mai
*
* Revision 1.8 1998/02/04 00:27:58 brianp
* cygnus changes from Stephane Rehel
*
* Revision 1.7 1998/01/16 03:35:26 brianp
* fixed Windows compilation warnings (Theodore Jump)
*
* Revision 1.6 1997/09/17 01:51:48 brianp
* changed glu*Callback() functions to match prototype in glu.h
*
* Revision 1.5 1997/07/24 01:28:44 brianp
* changed precompiled header symbol from PCH to PC_HEADER
*
* Revision 1.4 1997/05/28 02:29:38 brianp
* added support for precompiled headers (PCH), inserted APIENTRY keyword
*
* Revision 1.3 1996/11/12 01:23:02 brianp
* added test to prevent free(vertex) when vertex==NULL in delete_contours()
*
* Revision 1.2 1996/10/22 22:57:19 brianp
* better error handling in gluBegin/EndPolygon() from Erich Eder
*
* Revision 1.1 1996/09/27 01:19:39 brianp
* Initial revision
*
*/
/*
* This file is part of the polygon tesselation code contributed by
* Bogdan Sikorski
*/
#ifdef PC_HEADER
#include "all.h"
#else
#include <math.h>
#include <stdlib.h>
#include "tess.h"
#endif
/*
* This is ugly, but seems the easiest way to do things to make the
* code work under YellowBox for Windows
*/
#if defined(OPENSTEP) && defined(GLCALLBACK)
#undef GLCALLBACK
#define GLCALLBACK
#endif
extern void tess_test_polygon(GLUtriangulatorObj *);
extern void tess_find_contour_hierarchies(GLUtriangulatorObj *);
extern void tess_handle_holes(GLUtriangulatorObj *);
extern void tess_tesselate(GLUtriangulatorObj *);
extern void tess_tesselate_with_edge_flag(GLUtriangulatorObj *);
static void delete_contours(GLUtriangulatorObj *);
#ifdef __CYGWIN32__
#define _CALLBACK
#else
#define _CALLBACK GLCALLBACK
#endif
void init_callbacks(tess_callbacks *callbacks)
{
callbacks->begin = ( void (_CALLBACK*)(GLenum) ) 0;
callbacks->edgeFlag = ( void (_CALLBACK*)(GLboolean) ) 0;
callbacks->vertex = ( void (_CALLBACK*)(void*) ) 0;
callbacks->end = ( void (_CALLBACK*)(void) ) 0;
callbacks->error = ( void (_CALLBACK*)(GLenum) ) 0;
}
void tess_call_user_error(GLUtriangulatorObj *tobj, GLenum gluerr)
{
if(tobj->error==GLU_NO_ERROR)
tobj->error=gluerr;
if(tobj->callbacks.error!=NULL)
(tobj->callbacks.error)(gluerr);
}
GLUtriangulatorObj* GLAPIENTRY gluNewTess( void )
{
GLUtriangulatorObj *tobj;
tobj = (GLUtriangulatorObj *) malloc(sizeof(struct GLUtesselator));
if (!tobj)
return NULL;
tobj->contours=tobj->last_contour=NULL;
init_callbacks(&tobj->callbacks);
tobj->error=GLU_NO_ERROR;
tobj->current_polygon=NULL;
tobj->contour_cnt=0;
return tobj;
}
void GLAPIENTRY gluTessCallback( GLUtriangulatorObj *tobj, GLenum which,
void (GLCALLBACK *fn)() )
{
switch(which)
{
case GLU_BEGIN:
tobj->callbacks.begin = (void (_CALLBACK*)(GLenum)) fn;
break;
case GLU_EDGE_FLAG:
tobj->callbacks.edgeFlag = (void (_CALLBACK*)(GLboolean)) fn;
break;
case GLU_VERTEX:
tobj->callbacks.vertex = (void (_CALLBACK*)(void *)) fn;
break;
case GLU_END:
tobj->callbacks.end = (void (_CALLBACK*)(void)) fn;
break;
case GLU_ERROR:
tobj->callbacks.error = (void (_CALLBACK*)(GLenum)) fn;
break;
default:
tobj->error=GLU_INVALID_ENUM;
break;
}
}
void GLAPIENTRY gluDeleteTess( GLUtriangulatorObj *tobj )
{
if(tobj->error==GLU_NO_ERROR && tobj->contour_cnt)
/* was gluEndPolygon called? */
tess_call_user_error(tobj,GLU_TESS_ERROR1);
/* delete all internal structures */
delete_contours(tobj);
free(tobj);
}
void GLAPIENTRY gluBeginPolygon( GLUtriangulatorObj *tobj )
{
/*
if(tobj->error!=GLU_NO_ERROR)
return;
*/
tobj->error = GLU_NO_ERROR;
if(tobj->current_polygon!=NULL)
{
/* gluEndPolygon was not called */
tess_call_user_error(tobj,GLU_TESS_ERROR1);
/* delete all internal structures */
delete_contours(tobj);
}
else
{
if((tobj->current_polygon=
(tess_polygon *)malloc(sizeof(tess_polygon)))==NULL)
{
tess_call_user_error(tobj,GLU_OUT_OF_MEMORY);
return;
}
tobj->current_polygon->vertex_cnt=0;
tobj->current_polygon->vertices=
tobj->current_polygon->last_vertex=NULL;
}
}
void GLAPIENTRY gluEndPolygon( GLUtriangulatorObj *tobj )
{
/*tess_contour *contour_ptr;*/
/* there was an error */
if(tobj->error!=GLU_NO_ERROR) goto end;
/* check if gluBeginPolygon was called */
if(tobj->current_polygon==NULL)
{
tess_call_user_error(tobj,GLU_TESS_ERROR2);
return;
}
tess_test_polygon(tobj);
/* there was an error */
if(tobj->error!=GLU_NO_ERROR) goto end;
/* any real contours? */
if(tobj->contour_cnt==0)
{
/* delete all internal structures */
delete_contours(tobj);
return;
}
tess_find_contour_hierarchies(tobj);
/* there was an error */
if(tobj->error!=GLU_NO_ERROR) goto end;
tess_handle_holes(tobj);
/* there was an error */
if(tobj->error!=GLU_NO_ERROR) goto end;
/* if no callbacks, nothing to do */
if(tobj->callbacks.begin!=NULL && tobj->callbacks.vertex!=NULL &&
tobj->callbacks.end!=NULL)
{
if(tobj->callbacks.edgeFlag==NULL)
tess_tesselate(tobj);
else
tess_tesselate_with_edge_flag(tobj);
}
end:
/* delete all internal structures */
delete_contours(tobj);
}
void GLAPIENTRY gluNextContour( GLUtriangulatorObj *tobj, GLenum type )
{
if(tobj->error!=GLU_NO_ERROR)
return;
if(tobj->current_polygon==NULL)
{
tess_call_user_error(tobj,GLU_TESS_ERROR2);
return;
}
/* first contour? */
if(tobj->current_polygon->vertex_cnt)
tess_test_polygon(tobj);
}
void GLAPIENTRY gluTessVertex( GLUtriangulatorObj *tobj, GLdouble v[3], void *data )
{
tess_polygon *polygon=tobj->current_polygon;
tess_vertex *last_vertex_ptr;
if(tobj->error!=GLU_NO_ERROR)
return;
if(polygon==NULL)
{
tess_call_user_error(tobj,GLU_TESS_ERROR2);
return;
}
last_vertex_ptr=polygon->last_vertex;
if(last_vertex_ptr==NULL)
{
if((last_vertex_ptr=(tess_vertex *)
malloc(sizeof(tess_vertex)))==NULL)
{
tess_call_user_error(tobj,GLU_OUT_OF_MEMORY);
return;
}
polygon->vertices=last_vertex_ptr;
polygon->last_vertex=last_vertex_ptr;
last_vertex_ptr->data=data;
last_vertex_ptr->location[0]=v[0];
last_vertex_ptr->location[1]=v[1];
last_vertex_ptr->location[2]=v[2];
last_vertex_ptr->next=NULL;
last_vertex_ptr->previous=NULL;
++(polygon->vertex_cnt);
}
else
{
tess_vertex *vertex_ptr;
/* same point twice? */
if(fabs(last_vertex_ptr->location[0]-v[0]) < EPSILON &&
fabs(last_vertex_ptr->location[1]-v[1]) < EPSILON &&
fabs(last_vertex_ptr->location[2]-v[2]) < EPSILON)
{
tess_call_user_error(tobj,GLU_TESS_ERROR6);
return;
}
if((vertex_ptr=(tess_vertex *)
malloc(sizeof(tess_vertex)))==NULL)
{
tess_call_user_error(tobj,GLU_OUT_OF_MEMORY);
return;
}
vertex_ptr->data=data;
vertex_ptr->location[0]=v[0];
vertex_ptr->location[1]=v[1];
vertex_ptr->location[2]=v[2];
vertex_ptr->next=NULL;
vertex_ptr->previous=last_vertex_ptr;
++(polygon->vertex_cnt);
last_vertex_ptr->next=vertex_ptr;
polygon->last_vertex=vertex_ptr;
}
}
static void delete_contours(GLUtriangulatorObj *tobj)
{
tess_polygon *polygon=tobj->current_polygon;
tess_contour *contour,*contour_tmp;
tess_vertex *vertex,*vertex_tmp;
/* remove current_polygon list - if exists due to detected error */
if(polygon!=NULL)
{
if (polygon->vertices)
{
for(vertex=polygon->vertices;vertex!=polygon->last_vertex;)
{
vertex_tmp=vertex->next;
free(vertex);
vertex=vertex_tmp;
}
free(vertex);
}
free(polygon);
tobj->current_polygon=NULL;
}
/* remove all contour data */
for(contour=tobj->contours;contour!=NULL;)
{
for(vertex=contour->vertices;vertex!=contour->last_vertex;)
{
vertex_tmp=vertex->next;
free(vertex);
vertex=vertex_tmp;
}
free(vertex);
contour_tmp=contour->next;
free(contour);
contour=contour_tmp;
}
tobj->contours=tobj->last_contour=NULL;
tobj->contour_cnt=0;
}