diff options
Diffstat (limited to 'src/glu/mesa/polytest.c')
-rw-r--r-- | src/glu/mesa/polytest.c | 1049 |
1 files changed, 1049 insertions, 0 deletions
diff --git a/src/glu/mesa/polytest.c b/src/glu/mesa/polytest.c new file mode 100644 index 00000000000..9b42c63477e --- /dev/null +++ b/src/glu/mesa/polytest.c @@ -0,0 +1,1049 @@ +/* $Id: polytest.c,v 1.1 1999/08/19 00:55:42 jtg Exp $ */ + +/* + * Mesa 3-D graphics library + * Version: 2.4 + * Copyright (C) 1995-1997 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: polytest.c,v $ + * Revision 1.1 1999/08/19 00:55:42 jtg + * Initial revision + * + * Revision 1.7 1999/06/08 00:44:51 brianp + * OpenStep updates ([email protected]) + * + * Revision 1.6 1998/07/26 02:08:52 brianp + * updated for Windows compilation per Ted Jump + * + * Revision 1.5 1997/10/29 02:02:20 brianp + * various MS Windows compiler changes (David Bucciarelli, v20 3dfx driver) + * + * Revision 1.4 1997/07/24 01:28:44 brianp + * changed precompiled header symbol from PCH to PC_HEADER + * + * Revision 1.3 1997/05/28 02:29:38 brianp + * added support for precompiled headers (PCH), inserted APIENTRY keyword + * + * Revision 1.2 1997/05/08 01:53:21 brianp + * fixed memory leak in free_current_polygon() reported by Randy Frank + * + * 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 "gluP.h" +#include "tess.h" +#endif + + + +static GLenum store_polygon_as_contour(GLUtriangulatorObj *); +static void free_current_polygon(tess_polygon *); +static void prepare_projection_info(GLUtriangulatorObj *); +static GLdouble twice_the_polygon_area(tess_vertex *,tess_vertex *); +static GLenum verify_edge_vertex_intersections(GLUtriangulatorObj *); +void tess_find_contour_hierarchies(GLUtriangulatorObj *); +static GLenum test_for_overlapping_contours(GLUtriangulatorObj *); +static GLenum contours_overlap(tess_contour *, tess_polygon *); +static GLenum is_contour_contained_in(tess_contour *,tess_contour *); +static void add_new_exterior(GLUtriangulatorObj *,tess_contour *); +static void add_new_interior(GLUtriangulatorObj *,tess_contour *, + tess_contour *); +static void add_interior_with_hierarchy_check(GLUtriangulatorObj *, + tess_contour *,tess_contour *); +static void reverse_hierarchy_and_add_exterior(GLUtriangulatorObj *, + tess_contour *,tess_contour *); +static GLboolean point_in_polygon(tess_contour *,GLdouble,GLdouble); +static void shift_interior_to_exterior(GLUtriangulatorObj *,tess_contour *); +static void add_exterior_with_check(GLUtriangulatorObj *,tess_contour *, + tess_contour *); +static GLenum cut_out_hole(GLUtriangulatorObj *,tess_contour *, + tess_contour *); +static GLenum merge_hole_with_contour(GLUtriangulatorObj *, + tess_contour *,tess_contour *,tess_vertex *, + tess_vertex *); + +static GLenum +find_normal(GLUtriangulatorObj *tobj) +{ + tess_polygon *polygon=tobj->current_polygon; + tess_vertex *va,*vb,*vc; + GLdouble A,B,C; + GLdouble A0,A1,A2,B0,B1,B2; + + va=polygon->vertices; + vb=va->next; + A0=vb->location[0]-va->location[0]; + A1=vb->location[1]-va->location[1]; + A2=vb->location[2]-va->location[2]; + for(vc=vb->next;vc!=va;vc=vc->next) + { + B0=vc->location[0]-va->location[0]; + B1=vc->location[1]-va->location[1]; + B2=vc->location[2]-va->location[2]; + A=A1*B2-A2*B1; + B=A2*B0-A0*B2; + C=A0*B1-A1*B0; + if(fabs(A)>EPSILON || fabs(B)>EPSILON || fabs(C)>EPSILON) + { + polygon->A=A; + polygon->B=B; + polygon->C=C; + polygon->D= -A*va->location[0]-B*va->location[1]-C*va->location[2]; + return GLU_NO_ERROR; + } + } + tess_call_user_error(tobj,GLU_TESS_ERROR7); + return GLU_ERROR; +} + +void +tess_test_polygon( GLUtriangulatorObj *tobj ) +{ + tess_polygon *polygon=tobj->current_polygon; + + /* any vertices defined? */ + if(polygon->vertex_cnt<3) + { + free_current_polygon(polygon); + return; + } + /* wrap pointers */ + polygon->last_vertex->next=polygon->vertices; + polygon->vertices->previous=polygon->last_vertex; + /* determine the normal */ + if(find_normal(tobj)==GLU_ERROR) + return; + /* compare the normals of previously defined contours and this one */ + /* first contour define ? */ + if(tobj->contours==NULL) + { + tobj->A=polygon->A; + tobj->B=polygon->B; + tobj->C=polygon->C; + tobj->D=polygon->D; + /* determine the best projection to use */ + if(fabs(polygon->A) > fabs(polygon->B)) + if(fabs(polygon->A) > fabs(polygon->C)) + tobj->projection=OYZ; + else + tobj->projection=OXY; + else + if(fabs(polygon->B) > fabs(polygon->C)) + tobj->projection=OXZ; + else + tobj->projection=OXY; + } + else + { + GLdouble a[3],b[3]; + tess_vertex *vertex=polygon->vertices; + + a[0]=tobj->A; + a[1]=tobj->B; + a[2]=tobj->C; + b[0]=polygon->A; + b[1]=polygon->B; + b[2]=polygon->C; + + /* compare the normals */ + if( fabs(a[1]*b[2]-a[2]*b[1]) > EPSILON || + fabs(a[2]*b[0]-a[0]*b[2]) > EPSILON || + fabs(a[0]*b[1]-a[1]*b[0]) > EPSILON) + { + /* not coplanar */ + tess_call_user_error(tobj,GLU_TESS_ERROR9); + return; + } + /* the normals are parallel - test for plane equation */ + if(fabs(a[0]*vertex->location[0]+a[1]*vertex->location[1]+ + a[2]*vertex->location[2]+tobj->D) > EPSILON) + { + /* not the same plane */ + tess_call_user_error(tobj,GLU_TESS_ERROR9); + return; + } + } + prepare_projection_info(tobj); + if(verify_edge_vertex_intersections(tobj)==GLU_ERROR) + return; + if(test_for_overlapping_contours(tobj)==GLU_ERROR) + return; + if(store_polygon_as_contour(tobj)==GLU_ERROR) + return; +} + +static GLenum test_for_overlapping_contours(GLUtriangulatorObj *tobj) +{ + tess_contour *contour; + tess_polygon *polygon; + + polygon=tobj->current_polygon; + for(contour=tobj->contours;contour!=NULL;contour=contour->next) + if(contours_overlap(contour,polygon)!=GLU_NO_ERROR) + { + tess_call_user_error(tobj,GLU_TESS_ERROR5); + return GLU_ERROR; + } + return GLU_NO_ERROR; +} + +static GLenum store_polygon_as_contour(GLUtriangulatorObj *tobj) +{ + tess_polygon *polygon=tobj->current_polygon; + tess_contour *contour=tobj->contours; + + /* the first contour defined */ + if(contour==NULL) + { + if((contour=(tess_contour *)malloc( + sizeof(tess_contour)))==NULL) + { + tess_call_user_error(tobj,GLU_OUT_OF_MEMORY); + free_current_polygon(polygon); + return GLU_ERROR; + } + tobj->contours=tobj->last_contour=contour; + contour->next=contour->previous=NULL; + } + else + { + if((contour=(tess_contour *)malloc( + sizeof(tess_contour)))==NULL) + { + tess_call_user_error(tobj,GLU_OUT_OF_MEMORY); + free_current_polygon(polygon); + return GLU_ERROR; + } + contour->previous=tobj->last_contour; + tobj->last_contour->next=contour; + tobj->last_contour=contour; + contour->next=NULL; + } + /* mark all vertices in new contour as not special */ + /* and all are boundary edges */ + { + tess_vertex *vertex; + GLuint vertex_cnt,i; + + for(vertex=polygon->vertices , i=0 , vertex_cnt=polygon->vertex_cnt; + i<vertex_cnt; + vertex=vertex->next , i++) + { + vertex->shadow_vertex=NULL; + vertex->edge_flag=GL_TRUE; + } + } + contour->vertex_cnt=polygon->vertex_cnt; + contour->area=polygon->area; + contour->orientation=polygon->orientation; + contour->type=GLU_UNKNOWN; + contour->vertices=polygon->vertices; + contour->last_vertex=polygon->last_vertex; + polygon->vertices=polygon->last_vertex=NULL; + polygon->vertex_cnt=0; + ++(tobj->contour_cnt); + return GLU_NO_ERROR; +} + +static void free_current_polygon(tess_polygon *polygon) +{ + tess_vertex *vertex,*vertex_tmp; + GLuint i; + + /* free current_polygon structures */ + for(vertex=polygon->vertices,i=0;i<polygon->vertex_cnt;i++) + { + vertex_tmp=vertex->next; + free(vertex); + vertex=vertex_tmp; + } + polygon->vertices=polygon->last_vertex=NULL; + polygon->vertex_cnt=0; +} + +static void prepare_projection_info(GLUtriangulatorObj *tobj) +{ + tess_polygon *polygon=tobj->current_polygon; + tess_vertex *vertex,*last_vertex_ptr; + GLdouble area; + + last_vertex_ptr=polygon->last_vertex; + switch(tobj->projection) + { + case OXY: + for(vertex=polygon->vertices;vertex!=last_vertex_ptr; + vertex=vertex->next) + { + vertex->x=vertex->location[0]; + vertex->y=vertex->location[1]; + } + last_vertex_ptr->x=last_vertex_ptr->location[0]; + last_vertex_ptr->y=last_vertex_ptr->location[1]; + break; + case OXZ: + for(vertex=polygon->vertices;vertex!=last_vertex_ptr; + vertex=vertex->next) + { + vertex->x=vertex->location[0]; + vertex->y=vertex->location[2]; + } + last_vertex_ptr->x=last_vertex_ptr->location[0]; + last_vertex_ptr->y=last_vertex_ptr->location[2]; + break; + case OYZ: + for(vertex=polygon->vertices;vertex!=last_vertex_ptr; + vertex=vertex->next) + { + vertex->x=vertex->location[1]; + vertex->y=vertex->location[2]; + } + last_vertex_ptr->x=last_vertex_ptr->location[1]; + last_vertex_ptr->y=last_vertex_ptr->location[2]; + break; + } + area=twice_the_polygon_area(polygon->vertices,polygon->last_vertex); + if(area >= 0.0) + { + polygon->orientation=GLU_CCW; + polygon->area=area; + } + else + { + polygon->orientation=GLU_CW; + polygon->area= -area; + } +} + +static GLdouble twice_the_polygon_area(tess_vertex *vertex, + tess_vertex *last_vertex) +{ + tess_vertex *next; + GLdouble area,x,y; + + area=0.0; + x=vertex->x; + y=vertex->y; + vertex=vertex->next; + for(; vertex!=last_vertex; vertex=vertex->next) + { + next=vertex->next; + area+=(vertex->x - x)*(next->y - y) - (vertex->y - y)*(next->x - x); + } + return area; +} + +/* test if edges ab and cd intersect */ +/* if not return GLU_NO_ERROR, else if cross return GLU_TESS_ERROR8, */ +/* else if adjacent return GLU_TESS_ERROR4 */ +static GLenum edge_edge_intersect( + tess_vertex *a, + tess_vertex *b, + tess_vertex *c, + tess_vertex *d) +{ + GLdouble denom,r,s; + GLdouble xba,ydc,yba,xdc,yac,xac; + + xba=b->x - a->x; + yba=b->y - a->y; + xdc=d->x - c->x; + ydc=d->y - c->y; + xac=a->x - c->x; + yac=a->y - c->y; + denom= xba*ydc - yba*xdc; + r = yac*xdc - xac*ydc; + /* parallel? */ + if(fabs(denom) < EPSILON) + { + if(fabs(r) < EPSILON) + { + /* colinear */ + if(fabs(xba) < EPSILON) + { + /* compare the Y coordinate */ + if(yba > 0.0) + { + if((fabs(a->y - c->y)<EPSILON && fabs(c->y - b->y)<EPSILON) + || + (fabs(a->y - d->y)<EPSILON && fabs(d->y - b->y)<EPSILON)) + return GLU_TESS_ERROR4; + + } + else + { + if((fabs(b->y - c->y)<EPSILON && fabs(c->y - a->y)<EPSILON) + || + (fabs(b->y - d->y)<EPSILON && fabs(d->y - a->y)<EPSILON)) + return GLU_TESS_ERROR4; + } + } + else + { + /* compare the X coordinate */ + if(xba > 0.0) + { + if((fabs(a->x - c->x)<EPSILON && fabs(c->x - b->x)<EPSILON) + || + (fabs(a->x - d->x)<EPSILON && fabs(d->x - b->x)<EPSILON)) + return GLU_TESS_ERROR4; + } + else + { + if((fabs(b->x - c->x)<EPSILON && fabs(c->x - a->x)<EPSILON) + || + (fabs(b->x - d->x)<EPSILON && fabs(d->x - a->x)<EPSILON)) + return GLU_TESS_ERROR4; + } + } + } + return GLU_NO_ERROR; + } + r /= denom; + s = (yac*xba - xac*yba) / denom; + /* test if one vertex lies on other edge */ + if(((fabs(r) < EPSILON || (r < 1.0+EPSILON && r > 1.0-EPSILON)) && + s > -EPSILON && s < 1.0+EPSILON) || + ((fabs(s) < EPSILON || (s < 1.0+EPSILON && s > 1.0-EPSILON)) && + r > -EPSILON && r < 1.0+EPSILON)) + { + return GLU_TESS_ERROR4; + } + /* test for crossing */ + if(r > -EPSILON && r < 1.0+EPSILON && + s > -EPSILON && s < 1.0+EPSILON) + { + return GLU_TESS_ERROR8; + } + return GLU_NO_ERROR; +} + +static GLenum verify_edge_vertex_intersections(GLUtriangulatorObj *tobj) +{ + tess_polygon *polygon=tobj->current_polygon; + tess_vertex *vertex1,*last_vertex,*vertex2; + GLenum test; + + last_vertex=polygon->last_vertex; + vertex1=last_vertex; + for(vertex2=vertex1->next->next; + vertex2->next!=last_vertex; + vertex2=vertex2->next) + { + test=edge_edge_intersect(vertex1,vertex1->next,vertex2, + vertex2->next); + if(test!=GLU_NO_ERROR) + { + tess_call_user_error(tobj,test); + return GLU_ERROR; + } + } + for(vertex1=polygon->vertices; + vertex1->next->next!=last_vertex; + vertex1=vertex1->next) + { + for(vertex2=vertex1->next->next; + vertex2!=last_vertex; + vertex2=vertex2->next) + { + test=edge_edge_intersect(vertex1,vertex1->next,vertex2, + vertex2->next); + if(test!=GLU_NO_ERROR) + { + tess_call_user_error(tobj,test); + return GLU_ERROR; + } + } + } + return GLU_NO_ERROR; +} + +static int +#if defined(WIN32) && !defined(OPENSTEP) +__cdecl +#endif +area_compare(const void *a,const void *b) +{ + GLdouble area1,area2; + + area1=(*((tess_contour **)a))->area; + area2=(*((tess_contour **)b))->area; + if(area1 < area2) + return 1; + if(area1 > area2) + return -1; + return 0; +} + +void tess_find_contour_hierarchies(GLUtriangulatorObj *tobj) +{ + tess_contour **contours; /* dinamic array of pointers */ + tess_contour *tmp_contour_ptr=tobj->contours; + GLuint cnt,i; + GLenum result; + GLboolean hierarchy_changed; + + /* any contours? */ + if(tobj->contour_cnt < 2) + { + tobj->contours->type=GLU_EXTERIOR; + return; + } + if((contours=(tess_contour **) + malloc(sizeof(tess_contour *)*(tobj->contour_cnt)))==NULL) + { + tess_call_user_error(tobj,GLU_OUT_OF_MEMORY); + return; + } + for(tmp_contour_ptr=tobj->contours , cnt=0; + tmp_contour_ptr!=NULL; + tmp_contour_ptr=tmp_contour_ptr->next) + contours[cnt++]=tmp_contour_ptr; + /* now sort the contours in decreasing area size order */ + qsort((void *)contours,(size_t)cnt,(size_t)sizeof(tess_contour *),area_compare); + /* we leave just the first contour - remove others from list */ + tobj->contours=contours[0]; + tobj->contours->next=tobj->contours->previous=NULL; + tobj->last_contour=tobj->contours; + tobj->contour_cnt=1; + /* first contour is the one with greatest area */ + /* must be EXTERIOR */ + tobj->contours->type=GLU_EXTERIOR; + tmp_contour_ptr=tobj->contours; + /* now we play! */ + for(i=1;i<cnt;i++) + { + hierarchy_changed=GL_FALSE; + for(tmp_contour_ptr=tobj->contours; + tmp_contour_ptr!=NULL; + tmp_contour_ptr=tmp_contour_ptr->next) + { + if(tmp_contour_ptr->type==GLU_EXTERIOR) + { + /* check if contour completely contained in EXTERIOR */ + result=is_contour_contained_in(tmp_contour_ptr,contours[i]); + switch(result) + { + case GLU_INTERIOR: + /* now we have to check if contour is inside interiors */ + /* or not */ + /* any interiors? */ + if(tmp_contour_ptr->next!=NULL && + tmp_contour_ptr->next->type==GLU_INTERIOR) + { + /* for all interior, check if inside any of them */ + /* if not inside any of interiors, its another */ + /* interior */ + /* or it may contain some interiors, then change */ + /* the contained interiors to exterior ones */ + add_interior_with_hierarchy_check(tobj, + tmp_contour_ptr,contours[i]); + } + else + { + /* not in interior, add as new interior contour */ + add_new_interior(tobj,tmp_contour_ptr,contours[i]); + } + hierarchy_changed=GL_TRUE; + break; + case GLU_EXTERIOR: + /* ooops, the marked as EXTERIOR (contours[i]) is */ + /* actually an interior of tmp_contour_ptr */ + /* reverse the local hierarchy */ + reverse_hierarchy_and_add_exterior(tobj,tmp_contour_ptr, + contours[i]); + hierarchy_changed=GL_TRUE; + break; + case GLU_NO_ERROR: + break; + default: + abort(); + } + } + if(hierarchy_changed) + break; /* break from for loop */ + } + if(hierarchy_changed==GL_FALSE) + { + /* disjoint with all contours, add to contour list */ + add_new_exterior(tobj,contours[i]); + } + } + free(contours); +} + +/* returns GLU_INTERIOR if inner is completey enclosed within outer */ +/* returns GLU_EXTERIOR if outer is completely enclosed within inner */ +/* returns GLU_NO_ERROR if contours are disjoint */ +static GLenum is_contour_contained_in( + tess_contour *outer, + tess_contour *inner) +{ + GLenum relation_flag; + + /* set relation_flag to relation of containment of first inner vertex */ + /* regarding outer contour */ + if(point_in_polygon(outer,inner->vertices->x,inner->vertices->y)) + relation_flag=GLU_INTERIOR; + else + relation_flag=GLU_EXTERIOR; + if(relation_flag==GLU_INTERIOR) + return GLU_INTERIOR; + if(point_in_polygon(inner,outer->vertices->x,outer->vertices->y)) + return GLU_EXTERIOR; + return GLU_NO_ERROR; +} + +static GLboolean point_in_polygon( + tess_contour *contour, + GLdouble x, + GLdouble y) +{ + tess_vertex *v1,*v2; + GLuint i,vertex_cnt; + GLdouble xp1,yp1,xp2,yp2; + GLboolean tst; + + tst=GL_FALSE; + v1=contour->vertices; + v2=contour->vertices->previous; + for(i=0 , vertex_cnt=contour->vertex_cnt; + i < vertex_cnt; + i++) + { + xp1=v1->x; + yp1=v1->y; + xp2=v2->x; + yp2=v2->y; + if ((((yp1<=y) && (y<yp2)) || ((yp2<=y) && (y<yp1))) && + (x < (xp2 - xp1) * (y - yp1) / (yp2 - yp1) + xp1)) + tst = (tst==GL_FALSE ? GL_TRUE : GL_FALSE); + v2=v1; + v1=v1->next; + } + return tst; +} + +static GLenum contours_overlap( + tess_contour *contour, + tess_polygon *polygon) +{ + tess_vertex *vertex1,*vertex2; + GLuint vertex1_cnt,vertex2_cnt,i,j; + GLenum test; + + vertex1=contour->vertices; + vertex2=polygon->vertices; + vertex1_cnt=contour->vertex_cnt; + vertex2_cnt=polygon->vertex_cnt; + for(i=0; i<vertex1_cnt; vertex1=vertex1->next , i++) + { + for(j=0; j<vertex2_cnt; vertex2=vertex2->next , j++) + if((test=edge_edge_intersect(vertex1,vertex1->next,vertex2, + vertex2->next))!=GLU_NO_ERROR) + return test; + } + return GLU_NO_ERROR; +} + +static void add_new_exterior( + GLUtriangulatorObj *tobj, + tess_contour *contour) +{ + contour->type=GLU_EXTERIOR; + contour->next=NULL; + contour->previous=tobj->last_contour; + tobj->last_contour->next=contour; + tobj->last_contour=contour; +} + +static void add_new_interior( + GLUtriangulatorObj *tobj, + tess_contour *outer, + tess_contour *contour) +{ + contour->type=GLU_INTERIOR; + contour->next=outer->next; + contour->previous=outer; + if(outer->next!=NULL) + outer->next->previous=contour; + outer->next=contour; + if(tobj->last_contour==outer) + tobj->last_contour=contour; +} + +static void add_interior_with_hierarchy_check( + GLUtriangulatorObj *tobj, + tess_contour *outer, + tess_contour *contour) +{ + tess_contour *ptr; + + /* for all interiors of outer check if they are interior of contour */ + /* if so, change that interior to exterior and move it of of the */ + /* interior sequence */ + if(outer->next!=NULL && outer->next->type==GLU_INTERIOR) + { + GLenum test; + + for(ptr=outer->next;ptr!=NULL && ptr->type==GLU_INTERIOR;ptr=ptr->next) + { + test=is_contour_contained_in(ptr,contour); + switch(test) + { + case GLU_INTERIOR: + /* contour is contained in one of the interiors */ + /* check if possibly contained in other exteriors */ + /* move ptr to first EXTERIOR */ + for(;ptr!=NULL && ptr->type==GLU_INTERIOR;ptr=ptr->next); + if(ptr==NULL) + /* another exterior */ + add_new_exterior(tobj,contour); + else + add_exterior_with_check(tobj,ptr,contour); + return; + case GLU_EXTERIOR: + /* one of the interiors is contained in the contour */ + /* change it to EXTERIOR, and shift it away from the */ + /* interior sequence */ + shift_interior_to_exterior(tobj,ptr); + break; + case GLU_NO_ERROR: + /* disjoint */ + break; + default: + abort(); + } + } + } + /* add contour to the interior sequence */ + add_new_interior(tobj,outer,contour); +} + +static void reverse_hierarchy_and_add_exterior( + GLUtriangulatorObj *tobj, + tess_contour *outer, + tess_contour *contour) +{ + tess_contour *ptr; + + /* reverse INTERIORS to EXTERIORS */ + /* any INTERIORS? */ + if(outer->next!=NULL && outer->next->type==GLU_INTERIOR) + for(ptr=outer->next;ptr!=NULL && ptr->type==GLU_INTERIOR;ptr=ptr->next) + ptr->type=GLU_EXTERIOR; + /* the outer now becomes inner */ + outer->type=GLU_INTERIOR; + /* contour is the EXTERIOR */ + contour->next=outer; + if(tobj->contours==outer) + { + /* first contour beeing reversed */ + contour->previous=NULL; + tobj->contours=contour; + } + else + { + outer->previous->next=contour; + contour->previous=outer->previous; + } + outer->previous=contour; +} + +static void shift_interior_to_exterior( + GLUtriangulatorObj *tobj, + tess_contour *contour) +{ + contour->previous->next=contour->next; + if(contour->next!=NULL) + contour->next->previous=contour->previous; + else + tobj->last_contour=contour->previous; +} + +static void add_exterior_with_check( + GLUtriangulatorObj *tobj, + tess_contour *outer, + tess_contour *contour) +{ + GLenum test; + + /* this contour might be interior to further exteriors - check */ + /* if not, just add as a new exterior */ + for(;outer!=NULL && outer->type==GLU_EXTERIOR;outer=outer->next) + { + test=is_contour_contained_in(outer,contour); + switch(test) + { + case GLU_INTERIOR: + /* now we have to check if contour is inside interiors */ + /* or not */ + /* any interiors? */ + if(outer->next!=NULL && outer->next->type==GLU_INTERIOR) + { + /* for all interior, check if inside any of them */ + /* if not inside any of interiors, its another */ + /* interior */ + /* or it may contain some interiors, then change */ + /* the contained interiors to exterior ones */ + add_interior_with_hierarchy_check(tobj, + outer,contour); + } + else + { + /* not in interior, add as new interior contour */ + add_new_interior(tobj,outer,contour); + } + return; + case GLU_NO_ERROR: + /* disjoint */ + break; + default: + abort(); + } + } + /* add contour to the exterior sequence */ + add_new_exterior(tobj,contour); +} + +void tess_handle_holes(GLUtriangulatorObj *tobj) +{ + tess_contour *contour,*hole; + GLenum exterior_orientation; + + /* verify hole orientation */ + for(contour=tobj->contours;contour!=NULL;) + { + exterior_orientation=contour->orientation; + for(contour=contour->next; + contour!=NULL && contour->type==GLU_INTERIOR; + contour=contour->next) + { + if(contour->orientation==exterior_orientation) + { + tess_call_user_error(tobj,GLU_TESS_ERROR5); + return; + } + } + } + /* now cut-out holes */ + for(contour=tobj->contours;contour!=NULL;) + { + hole=contour->next; + while(hole!=NULL && hole->type==GLU_INTERIOR) + { + if(cut_out_hole(tobj,contour,hole)==GLU_ERROR) + return; + hole=contour->next; + } + contour=contour->next; + } +} + +static GLenum cut_out_hole( + GLUtriangulatorObj *tobj, + tess_contour *contour, + tess_contour *hole) +{ + tess_contour *tmp_hole; + tess_vertex *v1,*v2,*tmp_vertex; + GLuint vertex1_cnt,vertex2_cnt,tmp_vertex_cnt; + GLuint i,j,k; + GLenum test; + + /* find an edge connecting contour and hole not intersecting any other */ + /* edge belonging to either the contour or any of the other holes */ + for(v1=contour->vertices , vertex1_cnt=contour->vertex_cnt , i=0; + i<vertex1_cnt; + i++ , v1=v1->next) + { + for(v2=hole->vertices , vertex2_cnt=hole->vertex_cnt , j=0; + j<vertex2_cnt; + j++ , v2=v2->next) + { + /* does edge (v1,v2) intersect any edge of contour */ + for(tmp_vertex=contour->vertices , tmp_vertex_cnt=contour->vertex_cnt , + k=0; + k<tmp_vertex_cnt; + tmp_vertex=tmp_vertex->next , k++) + { + /* skip edge tests for edges directly connected */ + if(v1==tmp_vertex || v1==tmp_vertex->next) + continue; + test=edge_edge_intersect(v1,v2,tmp_vertex,tmp_vertex->next); + if(test!=GLU_NO_ERROR) + break; + } + if(test==GLU_NO_ERROR) + { + /* does edge (v1,v2) intersect any edge of hole */ + for(tmp_vertex=hole->vertices , + tmp_vertex_cnt=hole->vertex_cnt , k=0; + k<tmp_vertex_cnt; + tmp_vertex=tmp_vertex->next , k++) + { + /* skip edge tests for edges directly connected */ + if(v2==tmp_vertex || v2==tmp_vertex->next) + continue; + test=edge_edge_intersect(v1,v2,tmp_vertex,tmp_vertex->next); + if(test!=GLU_NO_ERROR) + break; + } + if(test==GLU_NO_ERROR) + { + /* does edge (v1,v2) intersect any other hole? */ + for(tmp_hole=hole->next; + tmp_hole!=NULL && tmp_hole->type==GLU_INTERIOR; + tmp_hole=tmp_hole->next) + { + /* does edge (v1,v2) intersect any edge of hole */ + for(tmp_vertex=tmp_hole->vertices , + tmp_vertex_cnt=tmp_hole->vertex_cnt , k=0; + k<tmp_vertex_cnt; + tmp_vertex=tmp_vertex->next , k++) + { + test=edge_edge_intersect(v1,v2,tmp_vertex, + tmp_vertex->next); + if(test!=GLU_NO_ERROR) + break; + } + if(test!=GLU_NO_ERROR) + break; + } + } + } + if(test==GLU_NO_ERROR) + { + /* edge (v1,v2) is good for eliminating the hole */ + if(merge_hole_with_contour(tobj,contour,hole,v1,v2) + ==GLU_NO_ERROR) + return GLU_NO_ERROR; + else + return GLU_ERROR; + } + } + } + /* other holes are blocking all possible connections of hole */ + /* with contour, we shift this hole as the last hole and retry */ + for(tmp_hole=hole; + tmp_hole!=NULL && tmp_hole->type==GLU_INTERIOR; + tmp_hole=tmp_hole->next); + contour->next=hole->next; + hole->next->previous=contour; + if(tmp_hole==NULL) + { + /* last EXTERIOR contour, shift hole as last contour */ + hole->next=NULL; + hole->previous=tobj->last_contour; + tobj->last_contour->next=hole; + tobj->last_contour=hole; + } + else + { + tmp_hole->previous->next=hole; + hole->previous=tmp_hole->previous; + tmp_hole->previous=hole; + hole->next=tmp_hole; + } + hole=contour->next; + /* try once again - recurse */ + return cut_out_hole(tobj,contour,hole); +} + +static GLenum merge_hole_with_contour( + GLUtriangulatorObj *tobj, + tess_contour *contour, + tess_contour *hole, + tess_vertex *v1, + tess_vertex *v2) +{ + tess_vertex *v1_new,*v2_new; + + /* make copies of v1 and v2, place them respectively after their originals */ + if((v1_new=(tess_vertex *)malloc(sizeof(tess_vertex)))==NULL) + { + tess_call_user_error(tobj,GLU_OUT_OF_MEMORY); + return GLU_ERROR; + } + if((v2_new=(tess_vertex *)malloc(sizeof(tess_vertex)))==NULL) + { + tess_call_user_error(tobj,GLU_OUT_OF_MEMORY); + return GLU_ERROR; + } + v1_new->edge_flag=GL_TRUE; + v1_new->data=v1->data; + v1_new->location[0]=v1->location[0]; + v1_new->location[1]=v1->location[1]; + v1_new->location[2]=v1->location[2]; + v1_new->x=v1->x; + v1_new->y=v1->y; + v1_new->shadow_vertex=v1; + v1->shadow_vertex=v1_new; + v1_new->next=v1->next; + v1_new->previous=v1; + v1->next->previous=v1_new; + v1->next=v1_new; + v2_new->edge_flag=GL_TRUE; + v2_new->data=v2->data; + v2_new->location[0]=v2->location[0]; + v2_new->location[1]=v2->location[1]; + v2_new->location[2]=v2->location[2]; + v2_new->x=v2->x; + v2_new->y=v2->y; + v2_new->shadow_vertex=v2; + v2->shadow_vertex=v2_new; + v2_new->next=v2->next; + v2_new->previous=v2; + v2->next->previous=v2_new; + v2->next=v2_new; + /* link together the two lists */ + v1->next=v2_new; + v2_new->previous=v1; + v2->next=v1_new; + v1_new->previous=v2; + /* update the vertex count of the contour */ + contour->vertex_cnt += hole->vertex_cnt+2; + /* remove the INTERIOR contour */ + contour->next=hole->next; + if(hole->next!=NULL) + hole->next->previous=contour; + free(hole); + /* update tobj structure */ + --(tobj->contour_cnt); + if(contour->last_vertex==v1) + contour->last_vertex=v1_new; + /* mark two vertices with edge_flag */ + v2->edge_flag=GL_FALSE; + v1->edge_flag=GL_FALSE; + return GLU_NO_ERROR; +} |