| 1 | /* $Id: sweep.c,v 1.1 2000-02-09 08:47:37 jeroen Exp $ */
|
|---|
| 2 | /*
|
|---|
| 3 | ** License Applicability. Except to the extent portions of this file are
|
|---|
| 4 | ** made subject to an alternative license as permitted in the SGI Free
|
|---|
| 5 | ** Software License B, Version 1.0 (the "License"), the contents of this
|
|---|
| 6 | ** file are subject only to the provisions of the License. You may not use
|
|---|
| 7 | ** this file except in compliance with the License. You may obtain a copy
|
|---|
| 8 | ** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
|
|---|
| 9 | ** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
|
|---|
| 10 | **
|
|---|
| 11 | ** http://oss.sgi.com/projects/FreeB
|
|---|
| 12 | **
|
|---|
| 13 | ** Note that, as provided in the License, the Software is distributed on an
|
|---|
| 14 | ** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
|
|---|
| 15 | ** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
|
|---|
| 16 | ** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
|
|---|
| 17 | ** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
|
|---|
| 18 | **
|
|---|
| 19 | ** Original Code. The Original Code is: OpenGL Sample Implementation,
|
|---|
| 20 | ** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
|
|---|
| 21 | ** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
|
|---|
| 22 | ** Copyright in any portions created by third parties is as indicated
|
|---|
| 23 | ** elsewhere herein. All Rights Reserved.
|
|---|
| 24 | **
|
|---|
| 25 | ** Additional Notice Provisions: The application programming interfaces
|
|---|
| 26 | ** established by SGI in conjunction with the Original Code are The
|
|---|
| 27 | ** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
|
|---|
| 28 | ** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
|
|---|
| 29 | ** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
|
|---|
| 30 | ** Window System(R) (Version 1.3), released October 19, 1998. This software
|
|---|
| 31 | ** was created using the OpenGL(R) version 1.2.1 Sample Implementation
|
|---|
| 32 | ** published by SGI, but has not been independently verified as being
|
|---|
| 33 | ** compliant with the OpenGL(R) version 1.2.1 Specification.
|
|---|
| 34 | **
|
|---|
| 35 | */
|
|---|
| 36 | /*
|
|---|
| 37 | ** Author: Eric Veach, July 1994.
|
|---|
| 38 | **
|
|---|
| 39 | ** $Date: 2000-02-09 08:47:37 $ $Revision: 1.1 $
|
|---|
| 40 | ** $Header: /home/ktk/tmp/odin/2007/netlabs.cvs/odin32/src/opengl/glu/tess/sweep.c,v 1.1 2000-02-09 08:47:37 jeroen Exp $
|
|---|
| 41 | */
|
|---|
| 42 |
|
|---|
| 43 | #include "gluos.h"
|
|---|
| 44 | #include <assert.h>
|
|---|
| 45 | #include <stddef.h>
|
|---|
| 46 | #include <setjmp.h> /* longjmp */
|
|---|
| 47 | #include <limits.h> /* LONG_MAX */
|
|---|
| 48 |
|
|---|
| 49 | #include "mesh.h"
|
|---|
| 50 | #include "geom.h"
|
|---|
| 51 | #include "tess.h"
|
|---|
| 52 | #include "dict.h"
|
|---|
| 53 | #include "priorityq.h"
|
|---|
| 54 | #include "memalloc.h"
|
|---|
| 55 | #include "sweep.h"
|
|---|
| 56 |
|
|---|
| 57 | #define TRUE 1
|
|---|
| 58 | #define FALSE 0
|
|---|
| 59 |
|
|---|
| 60 | #ifdef FOR_TRITE_TEST_PROGRAM
|
|---|
| 61 | extern void DebugEvent( GLUtesselator *tess );
|
|---|
| 62 | #else
|
|---|
| 63 | #define DebugEvent( tess )
|
|---|
| 64 | #endif
|
|---|
| 65 |
|
|---|
| 66 | /*
|
|---|
| 67 | * Invariants for the Edge Dictionary.
|
|---|
| 68 | * - each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2)
|
|---|
| 69 | * at any valid location of the sweep event
|
|---|
| 70 | * - if EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2
|
|---|
| 71 | * share a common endpoint
|
|---|
| 72 | * - for each e, e->Dst has been processed, but not e->Org
|
|---|
| 73 | * - each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org)
|
|---|
| 74 | * where "event" is the current sweep line event.
|
|---|
| 75 | * - no edge e has zero length
|
|---|
| 76 | *
|
|---|
| 77 | * Invariants for the Mesh (the processed portion).
|
|---|
| 78 | * - the portion of the mesh left of the sweep line is a planar graph,
|
|---|
| 79 | * ie. there is *some* way to embed it in the plane
|
|---|
| 80 | * - no processed edge has zero length
|
|---|
| 81 | * - no two processed vertices have identical coordinates
|
|---|
| 82 | * - each "inside" region is monotone, ie. can be broken into two chains
|
|---|
| 83 | * of monotonically increasing vertices according to VertLeq(v1,v2)
|
|---|
| 84 | * - a non-invariant: these chains may intersect (very slightly)
|
|---|
| 85 | *
|
|---|
| 86 | * Invariants for the Sweep.
|
|---|
| 87 | * - if none of the edges incident to the event vertex have an activeRegion
|
|---|
| 88 | * (ie. none of these edges are in the edge dictionary), then the vertex
|
|---|
| 89 | * has only right-going edges.
|
|---|
| 90 | * - if an edge is marked "fixUpperEdge" (it is a temporary edge introduced
|
|---|
| 91 | * by ConnectRightVertex), then it is the only right-going edge from
|
|---|
| 92 | * its associated vertex. (This says that these edges exist only
|
|---|
| 93 | * when it is necessary.)
|
|---|
| 94 | */
|
|---|
| 95 |
|
|---|
| 96 | #ifndef __WIN32OS2__
|
|---|
| 97 | #define MAX(x,y) ((x) >= (y) ? (x) : (y))
|
|---|
| 98 | #define MIN(x,y) ((x) <= (y) ? (x) : (y))
|
|---|
| 99 | #endif
|
|---|
| 100 |
|
|---|
| 101 | /* When we merge two edges into one, we need to compute the combined
|
|---|
| 102 | * winding of the new edge.
|
|---|
| 103 | */
|
|---|
| 104 | #define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \
|
|---|
| 105 | eDst->Sym->winding += eSrc->Sym->winding)
|
|---|
| 106 |
|
|---|
| 107 | static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent );
|
|---|
| 108 | static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp );
|
|---|
| 109 | static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp );
|
|---|
| 110 |
|
|---|
| 111 | static int EdgeLeq( GLUtesselator *tess, ActiveRegion *reg1,
|
|---|
| 112 | ActiveRegion *reg2 )
|
|---|
| 113 | /*
|
|---|
| 114 | * Both edges must be directed from right to left (this is the canonical
|
|---|
| 115 | * direction for the upper edge of each region).
|
|---|
| 116 | *
|
|---|
| 117 | * The strategy is to evaluate a "t" value for each edge at the
|
|---|
| 118 | * current sweep line position, given by tess->event. The calculations
|
|---|
| 119 | * are designed to be very stable, but of course they are not perfect.
|
|---|
| 120 | *
|
|---|
| 121 | * Special case: if both edge destinations are at the sweep event,
|
|---|
| 122 | * we sort the edges by slope (they would otherwise compare equally).
|
|---|
| 123 | */
|
|---|
| 124 | {
|
|---|
| 125 | GLUvertex *event = tess->event;
|
|---|
| 126 | GLUhalfEdge *e1, *e2;
|
|---|
| 127 | GLdouble t1, t2;
|
|---|
| 128 |
|
|---|
| 129 | e1 = reg1->eUp;
|
|---|
| 130 | e2 = reg2->eUp;
|
|---|
| 131 |
|
|---|
| 132 | if( e1->Dst == event ) {
|
|---|
| 133 | if( e2->Dst == event ) {
|
|---|
| 134 | /* Two edges right of the sweep line which meet at the sweep event.
|
|---|
| 135 | * Sort them by slope.
|
|---|
| 136 | */
|
|---|
| 137 | if( VertLeq( e1->Org, e2->Org )) {
|
|---|
| 138 | return EdgeSign( e2->Dst, e1->Org, e2->Org ) <= 0;
|
|---|
| 139 | }
|
|---|
| 140 | return EdgeSign( e1->Dst, e2->Org, e1->Org ) >= 0;
|
|---|
| 141 | }
|
|---|
| 142 | return EdgeSign( e2->Dst, event, e2->Org ) <= 0;
|
|---|
| 143 | }
|
|---|
| 144 | if( e2->Dst == event ) {
|
|---|
| 145 | return EdgeSign( e1->Dst, event, e1->Org ) >= 0;
|
|---|
| 146 | }
|
|---|
| 147 |
|
|---|
| 148 | /* General case - compute signed distance *from* e1, e2 to event */
|
|---|
| 149 | t1 = EdgeEval( e1->Dst, event, e1->Org );
|
|---|
| 150 | t2 = EdgeEval( e2->Dst, event, e2->Org );
|
|---|
| 151 | return (t1 >= t2);
|
|---|
| 152 | }
|
|---|
| 153 |
|
|---|
| 154 |
|
|---|
| 155 | static void DeleteRegion( GLUtesselator *tess, ActiveRegion *reg )
|
|---|
| 156 | {
|
|---|
| 157 | if( reg->fixUpperEdge ) {
|
|---|
| 158 | /* It was created with zero winding number, so it better be
|
|---|
| 159 | * deleted with zero winding number (ie. it better not get merged
|
|---|
| 160 | * with a real edge).
|
|---|
| 161 | */
|
|---|
| 162 | assert( reg->eUp->winding == 0 );
|
|---|
| 163 | }
|
|---|
| 164 | reg->eUp->activeRegion = NULL;
|
|---|
| 165 | dictDelete( tess->dict, reg->nodeUp ); /* __gl_dictListDelete */
|
|---|
| 166 | memFree( reg );
|
|---|
| 167 | }
|
|---|
| 168 |
|
|---|
| 169 |
|
|---|
| 170 | static int FixUpperEdge( ActiveRegion *reg, GLUhalfEdge *newEdge )
|
|---|
| 171 | /*
|
|---|
| 172 | * Replace an upper edge which needs fixing (see ConnectRightVertex).
|
|---|
| 173 | */
|
|---|
| 174 | {
|
|---|
| 175 | assert( reg->fixUpperEdge );
|
|---|
| 176 | if ( !__gl_meshDelete( reg->eUp ) ) return 0;
|
|---|
| 177 | reg->fixUpperEdge = FALSE;
|
|---|
| 178 | reg->eUp = newEdge;
|
|---|
| 179 | newEdge->activeRegion = reg;
|
|---|
| 180 |
|
|---|
| 181 | return 1;
|
|---|
| 182 | }
|
|---|
| 183 |
|
|---|
| 184 | static ActiveRegion *TopLeftRegion( ActiveRegion *reg )
|
|---|
| 185 | {
|
|---|
| 186 | GLUvertex *org = reg->eUp->Org;
|
|---|
| 187 | GLUhalfEdge *e;
|
|---|
| 188 |
|
|---|
| 189 | /* Find the region above the uppermost edge with the same origin */
|
|---|
| 190 | do {
|
|---|
| 191 | reg = RegionAbove( reg );
|
|---|
| 192 | } while( reg->eUp->Org == org );
|
|---|
| 193 |
|
|---|
| 194 | /* If the edge above was a temporary edge introduced by ConnectRightVertex,
|
|---|
| 195 | * now is the time to fix it.
|
|---|
| 196 | */
|
|---|
| 197 | if( reg->fixUpperEdge ) {
|
|---|
| 198 | e = __gl_meshConnect( RegionBelow(reg)->eUp->Sym, reg->eUp->Lnext );
|
|---|
| 199 | if (e == NULL) return NULL;
|
|---|
| 200 | if ( !FixUpperEdge( reg, e ) ) return NULL;
|
|---|
| 201 | reg = RegionAbove( reg );
|
|---|
| 202 | }
|
|---|
| 203 | return reg;
|
|---|
| 204 | }
|
|---|
| 205 |
|
|---|
| 206 | static ActiveRegion *TopRightRegion( ActiveRegion *reg )
|
|---|
| 207 | {
|
|---|
| 208 | GLUvertex *dst = reg->eUp->Dst;
|
|---|
| 209 |
|
|---|
| 210 | /* Find the region above the uppermost edge with the same destination */
|
|---|
| 211 | do {
|
|---|
| 212 | reg = RegionAbove( reg );
|
|---|
| 213 | } while( reg->eUp->Dst == dst );
|
|---|
| 214 | return reg;
|
|---|
| 215 | }
|
|---|
| 216 |
|
|---|
| 217 | static ActiveRegion *AddRegionBelow( GLUtesselator *tess,
|
|---|
| 218 | ActiveRegion *regAbove,
|
|---|
| 219 | GLUhalfEdge *eNewUp )
|
|---|
| 220 | /*
|
|---|
| 221 | * Add a new active region to the sweep line, *somewhere* below "regAbove"
|
|---|
| 222 | * (according to where the new edge belongs in the sweep-line dictionary).
|
|---|
| 223 | * The upper edge of the new region will be "eNewUp".
|
|---|
| 224 | * Winding number and "inside" flag are not updated.
|
|---|
| 225 | */
|
|---|
| 226 | {
|
|---|
| 227 | ActiveRegion *regNew = (ActiveRegion *)memAlloc( sizeof( ActiveRegion ));
|
|---|
| 228 | if (regNew == NULL) longjmp(tess->env,1);
|
|---|
| 229 |
|
|---|
| 230 | regNew->eUp = eNewUp;
|
|---|
| 231 | /* __gl_dictListInsertBefore */
|
|---|
| 232 | regNew->nodeUp = dictInsertBefore( tess->dict, regAbove->nodeUp, regNew );
|
|---|
| 233 | if (regNew->nodeUp == NULL) longjmp(tess->env,1);
|
|---|
| 234 | regNew->fixUpperEdge = FALSE;
|
|---|
| 235 | regNew->sentinel = FALSE;
|
|---|
| 236 | regNew->dirty = FALSE;
|
|---|
| 237 |
|
|---|
| 238 | eNewUp->activeRegion = regNew;
|
|---|
| 239 | return regNew;
|
|---|
| 240 | }
|
|---|
| 241 |
|
|---|
| 242 | static GLboolean IsWindingInside( GLUtesselator *tess, int n )
|
|---|
| 243 | {
|
|---|
| 244 | switch( tess->windingRule ) {
|
|---|
| 245 | case GLU_TESS_WINDING_ODD:
|
|---|
| 246 | return (n & 1);
|
|---|
| 247 | case GLU_TESS_WINDING_NONZERO:
|
|---|
| 248 | return (n != 0);
|
|---|
| 249 | case GLU_TESS_WINDING_POSITIVE:
|
|---|
| 250 | return (n > 0);
|
|---|
| 251 | case GLU_TESS_WINDING_NEGATIVE:
|
|---|
| 252 | return (n < 0);
|
|---|
| 253 | case GLU_TESS_WINDING_ABS_GEQ_TWO:
|
|---|
| 254 | return (n >= 2) || (n <= -2);
|
|---|
| 255 | }
|
|---|
| 256 | /*LINTED*/
|
|---|
| 257 | assert( FALSE );
|
|---|
| 258 | /*NOTREACHED*/
|
|---|
| 259 |
|
|---|
| 260 | return 0; /* Elim compiler warning */
|
|---|
| 261 | }
|
|---|
| 262 |
|
|---|
| 263 |
|
|---|
| 264 | static void ComputeWinding( GLUtesselator *tess, ActiveRegion *reg )
|
|---|
| 265 | {
|
|---|
| 266 | reg->windingNumber = RegionAbove(reg)->windingNumber + reg->eUp->winding;
|
|---|
| 267 | reg->inside = IsWindingInside( tess, reg->windingNumber );
|
|---|
| 268 | }
|
|---|
| 269 |
|
|---|
| 270 |
|
|---|
| 271 | static void FinishRegion( GLUtesselator *tess, ActiveRegion *reg )
|
|---|
| 272 | /*
|
|---|
| 273 | * Delete a region from the sweep line. This happens when the upper
|
|---|
| 274 | * and lower chains of a region meet (at a vertex on the sweep line).
|
|---|
| 275 | * The "inside" flag is copied to the appropriate mesh face (we could
|
|---|
| 276 | * not do this before -- since the structure of the mesh is always
|
|---|
| 277 | * changing, this face may not have even existed until now).
|
|---|
| 278 | */
|
|---|
| 279 | {
|
|---|
| 280 | GLUhalfEdge *e = reg->eUp;
|
|---|
| 281 | GLUface *f = e->Lface;
|
|---|
| 282 |
|
|---|
| 283 | f->inside = reg->inside;
|
|---|
| 284 | f->anEdge = e; /* optimization for __gl_meshTessellateMonoRegion() */
|
|---|
| 285 | DeleteRegion( tess, reg );
|
|---|
| 286 | }
|
|---|
| 287 |
|
|---|
| 288 |
|
|---|
| 289 | static GLUhalfEdge *FinishLeftRegions( GLUtesselator *tess,
|
|---|
| 290 | ActiveRegion *regFirst, ActiveRegion *regLast )
|
|---|
| 291 | /*
|
|---|
| 292 | * We are given a vertex with one or more left-going edges. All affected
|
|---|
| 293 | * edges should be in the edge dictionary. Starting at regFirst->eUp,
|
|---|
| 294 | * we walk down deleting all regions where both edges have the same
|
|---|
| 295 | * origin vOrg. At the same time we copy the "inside" flag from the
|
|---|
| 296 | * active region to the face, since at this point each face will belong
|
|---|
| 297 | * to at most one region (this was not necessarily true until this point
|
|---|
| 298 | * in the sweep). The walk stops at the region above regLast; if regLast
|
|---|
| 299 | * is NULL we walk as far as possible. At the same time we relink the
|
|---|
| 300 | * mesh if necessary, so that the ordering of edges around vOrg is the
|
|---|
| 301 | * same as in the dictionary.
|
|---|
| 302 | */
|
|---|
| 303 | {
|
|---|
| 304 | ActiveRegion *reg, *regPrev;
|
|---|
| 305 | GLUhalfEdge *e, *ePrev;
|
|---|
| 306 |
|
|---|
| 307 | regPrev = regFirst;
|
|---|
| 308 | ePrev = regFirst->eUp;
|
|---|
| 309 | while( regPrev != regLast ) {
|
|---|
| 310 | regPrev->fixUpperEdge = FALSE; /* placement was OK */
|
|---|
| 311 | reg = RegionBelow( regPrev );
|
|---|
| 312 | e = reg->eUp;
|
|---|
| 313 | if( e->Org != ePrev->Org ) {
|
|---|
| 314 | if( ! reg->fixUpperEdge ) {
|
|---|
| 315 | /* Remove the last left-going edge. Even though there are no further
|
|---|
| 316 | * edges in the dictionary with this origin, there may be further
|
|---|
| 317 | * such edges in the mesh (if we are adding left edges to a vertex
|
|---|
| 318 | * that has already been processed). Thus it is important to call
|
|---|
| 319 | * FinishRegion rather than just DeleteRegion.
|
|---|
| 320 | */
|
|---|
| 321 | FinishRegion( tess, regPrev );
|
|---|
| 322 | break;
|
|---|
| 323 | }
|
|---|
| 324 | /* If the edge below was a temporary edge introduced by
|
|---|
| 325 | * ConnectRightVertex, now is the time to fix it.
|
|---|
| 326 | */
|
|---|
| 327 | e = __gl_meshConnect( ePrev->Lprev, e->Sym );
|
|---|
| 328 | if (e == NULL) longjmp(tess->env,1);
|
|---|
| 329 | if ( !FixUpperEdge( reg, e ) ) longjmp(tess->env,1);
|
|---|
| 330 | }
|
|---|
| 331 |
|
|---|
| 332 | /* Relink edges so that ePrev->Onext == e */
|
|---|
| 333 | if( ePrev->Onext != e ) {
|
|---|
| 334 | if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1);
|
|---|
| 335 | if ( !__gl_meshSplice( ePrev, e ) ) longjmp(tess->env,1);
|
|---|
| 336 | }
|
|---|
| 337 | FinishRegion( tess, regPrev ); /* may change reg->eUp */
|
|---|
| 338 | ePrev = reg->eUp;
|
|---|
| 339 | regPrev = reg;
|
|---|
| 340 | }
|
|---|
| 341 | return ePrev;
|
|---|
| 342 | }
|
|---|
| 343 |
|
|---|
| 344 |
|
|---|
| 345 | static void AddRightEdges( GLUtesselator *tess, ActiveRegion *regUp,
|
|---|
| 346 | GLUhalfEdge *eFirst, GLUhalfEdge *eLast, GLUhalfEdge *eTopLeft,
|
|---|
| 347 | GLboolean cleanUp )
|
|---|
| 348 | /*
|
|---|
| 349 | * Purpose: insert right-going edges into the edge dictionary, and update
|
|---|
| 350 | * winding numbers and mesh connectivity appropriately. All right-going
|
|---|
| 351 | * edges share a common origin vOrg. Edges are inserted CCW starting at
|
|---|
| 352 | * eFirst; the last edge inserted is eLast->Oprev. If vOrg has any
|
|---|
| 353 | * left-going edges already processed, then eTopLeft must be the edge
|
|---|
| 354 | * such that an imaginary upward vertical segment from vOrg would be
|
|---|
| 355 | * contained between eTopLeft->Oprev and eTopLeft; otherwise eTopLeft
|
|---|
| 356 | * should be NULL.
|
|---|
| 357 | */
|
|---|
| 358 | {
|
|---|
| 359 | ActiveRegion *reg, *regPrev;
|
|---|
| 360 | GLUhalfEdge *e, *ePrev;
|
|---|
| 361 | int firstTime = TRUE;
|
|---|
| 362 |
|
|---|
| 363 | /* Insert the new right-going edges in the dictionary */
|
|---|
| 364 | e = eFirst;
|
|---|
| 365 | do {
|
|---|
| 366 | assert( VertLeq( e->Org, e->Dst ));
|
|---|
| 367 | AddRegionBelow( tess, regUp, e->Sym );
|
|---|
| 368 | e = e->Onext;
|
|---|
| 369 | } while ( e != eLast );
|
|---|
| 370 |
|
|---|
| 371 | /* Walk *all* right-going edges from e->Org, in the dictionary order,
|
|---|
| 372 | * updating the winding numbers of each region, and re-linking the mesh
|
|---|
| 373 | * edges to match the dictionary ordering (if necessary).
|
|---|
| 374 | */
|
|---|
| 375 | if( eTopLeft == NULL ) {
|
|---|
| 376 | eTopLeft = RegionBelow( regUp )->eUp->Rprev;
|
|---|
| 377 | }
|
|---|
| 378 | regPrev = regUp;
|
|---|
| 379 | ePrev = eTopLeft;
|
|---|
| 380 | for( ;; ) {
|
|---|
| 381 | reg = RegionBelow( regPrev );
|
|---|
| 382 | e = reg->eUp->Sym;
|
|---|
| 383 | if( e->Org != ePrev->Org ) break;
|
|---|
| 384 |
|
|---|
| 385 | if( e->Onext != ePrev ) {
|
|---|
| 386 | /* Unlink e from its current position, and relink below ePrev */
|
|---|
| 387 | if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1);
|
|---|
| 388 | if ( !__gl_meshSplice( ePrev->Oprev, e ) ) longjmp(tess->env,1);
|
|---|
| 389 | }
|
|---|
| 390 | /* Compute the winding number and "inside" flag for the new regions */
|
|---|
| 391 | reg->windingNumber = regPrev->windingNumber - e->winding;
|
|---|
| 392 | reg->inside = IsWindingInside( tess, reg->windingNumber );
|
|---|
| 393 |
|
|---|
| 394 | /* Check for two outgoing edges with same slope -- process these
|
|---|
| 395 | * before any intersection tests (see example in __gl_computeInterior).
|
|---|
| 396 | */
|
|---|
| 397 | regPrev->dirty = TRUE;
|
|---|
| 398 | if( ! firstTime && CheckForRightSplice( tess, regPrev )) {
|
|---|
| 399 | AddWinding( e, ePrev );
|
|---|
| 400 | DeleteRegion( tess, regPrev );
|
|---|
| 401 | if ( !__gl_meshDelete( ePrev ) ) longjmp(tess->env,1);
|
|---|
| 402 | }
|
|---|
| 403 | firstTime = FALSE;
|
|---|
| 404 | regPrev = reg;
|
|---|
| 405 | ePrev = e;
|
|---|
| 406 | }
|
|---|
| 407 | regPrev->dirty = TRUE;
|
|---|
| 408 | assert( regPrev->windingNumber - e->winding == reg->windingNumber );
|
|---|
| 409 |
|
|---|
| 410 | if( cleanUp ) {
|
|---|
| 411 | /* Check for intersections between newly adjacent edges. */
|
|---|
| 412 | WalkDirtyRegions( tess, regPrev );
|
|---|
| 413 | }
|
|---|
| 414 | }
|
|---|
| 415 |
|
|---|
| 416 |
|
|---|
| 417 | static void CallCombine( GLUtesselator *tess, GLUvertex *isect,
|
|---|
| 418 | void *data[4], GLfloat weights[4], int needed )
|
|---|
| 419 | {
|
|---|
| 420 | GLdouble coords[3];
|
|---|
| 421 |
|
|---|
| 422 | /* Copy coord data in case the callback changes it. */
|
|---|
| 423 | coords[0] = isect->coords[0];
|
|---|
| 424 | coords[1] = isect->coords[1];
|
|---|
| 425 | coords[2] = isect->coords[2];
|
|---|
| 426 |
|
|---|
| 427 | isect->data = NULL;
|
|---|
| 428 | CALL_COMBINE_OR_COMBINE_DATA( coords, data, weights, &isect->data );
|
|---|
| 429 | if( isect->data == NULL ) {
|
|---|
| 430 | if( ! needed ) {
|
|---|
| 431 | isect->data = data[0];
|
|---|
| 432 | } else if( ! tess->fatalError ) {
|
|---|
| 433 | /* The only way fatal error is when two edges are found to intersect,
|
|---|
| 434 | * but the user has not provided the callback necessary to handle
|
|---|
| 435 | * generated intersection points.
|
|---|
| 436 | */
|
|---|
| 437 | CALL_ERROR_OR_ERROR_DATA( GLU_TESS_NEED_COMBINE_CALLBACK );
|
|---|
| 438 | tess->fatalError = TRUE;
|
|---|
| 439 | }
|
|---|
| 440 | }
|
|---|
| 441 | }
|
|---|
| 442 |
|
|---|
| 443 | static void SpliceMergeVertices( GLUtesselator *tess, GLUhalfEdge *e1,
|
|---|
| 444 | GLUhalfEdge *e2 )
|
|---|
| 445 | /*
|
|---|
| 446 | * Two vertices with idential coordinates are combined into one.
|
|---|
| 447 | * e1->Org is kept, while e2->Org is discarded.
|
|---|
| 448 | */
|
|---|
| 449 | {
|
|---|
| 450 | void *data[4] = { NULL, NULL, NULL, NULL };
|
|---|
| 451 | GLfloat weights[4] = { 0.5, 0.5, 0.0, 0.0 };
|
|---|
| 452 |
|
|---|
| 453 | data[0] = e1->Org->data;
|
|---|
| 454 | data[1] = e2->Org->data;
|
|---|
| 455 | CallCombine( tess, e1->Org, data, weights, FALSE );
|
|---|
| 456 | if ( !__gl_meshSplice( e1, e2 ) ) longjmp(tess->env,1);
|
|---|
| 457 | }
|
|---|
| 458 |
|
|---|
| 459 | static void VertexWeights( GLUvertex *isect, GLUvertex *org, GLUvertex *dst,
|
|---|
| 460 | GLfloat *weights )
|
|---|
| 461 | /*
|
|---|
| 462 | * Find some weights which describe how the intersection vertex is
|
|---|
| 463 | * a linear combination of "org" and "dest". Each of the two edges
|
|---|
| 464 | * which generated "isect" is allocated 50% of the weight; each edge
|
|---|
| 465 | * splits the weight between its org and dst according to the
|
|---|
| 466 | * relative distance to "isect".
|
|---|
| 467 | */
|
|---|
| 468 | {
|
|---|
| 469 | GLdouble t1 = VertL1dist( org, isect );
|
|---|
| 470 | GLdouble t2 = VertL1dist( dst, isect );
|
|---|
| 471 |
|
|---|
| 472 | weights[0] = 0.5 * t2 / (t1 + t2);
|
|---|
| 473 | weights[1] = 0.5 * t1 / (t1 + t2);
|
|---|
| 474 | isect->coords[0] += weights[0]*org->coords[0] + weights[1]*dst->coords[0];
|
|---|
| 475 | isect->coords[1] += weights[0]*org->coords[1] + weights[1]*dst->coords[1];
|
|---|
| 476 | isect->coords[2] += weights[0]*org->coords[2] + weights[1]*dst->coords[2];
|
|---|
| 477 | }
|
|---|
| 478 |
|
|---|
| 479 |
|
|---|
| 480 | static void GetIntersectData( GLUtesselator *tess, GLUvertex *isect,
|
|---|
| 481 | GLUvertex *orgUp, GLUvertex *dstUp,
|
|---|
| 482 | GLUvertex *orgLo, GLUvertex *dstLo )
|
|---|
| 483 | /*
|
|---|
| 484 | * We've computed a new intersection point, now we need a "data" pointer
|
|---|
| 485 | * from the user so that we can refer to this new vertex in the
|
|---|
| 486 | * rendering callbacks.
|
|---|
| 487 | */
|
|---|
| 488 | {
|
|---|
| 489 | void *data[4];
|
|---|
| 490 | GLfloat weights[4];
|
|---|
| 491 |
|
|---|
| 492 | data[0] = orgUp->data;
|
|---|
| 493 | data[1] = dstUp->data;
|
|---|
| 494 | data[2] = orgLo->data;
|
|---|
| 495 | data[3] = dstLo->data;
|
|---|
| 496 |
|
|---|
| 497 | isect->coords[0] = isect->coords[1] = isect->coords[2] = 0;
|
|---|
| 498 | VertexWeights( isect, orgUp, dstUp, &weights[0] );
|
|---|
| 499 | VertexWeights( isect, orgLo, dstLo, &weights[2] );
|
|---|
| 500 |
|
|---|
| 501 | CallCombine( tess, isect, data, weights, TRUE );
|
|---|
| 502 | }
|
|---|
| 503 |
|
|---|
| 504 | static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp )
|
|---|
| 505 | /*
|
|---|
| 506 | * Check the upper and lower edge of "regUp", to make sure that the
|
|---|
| 507 | * eUp->Org is above eLo, or eLo->Org is below eUp (depending on which
|
|---|
| 508 | * origin is leftmost).
|
|---|
| 509 | *
|
|---|
| 510 | * The main purpose is to splice right-going edges with the same
|
|---|
| 511 | * dest vertex and nearly identical slopes (ie. we can't distinguish
|
|---|
| 512 | * the slopes numerically). However the splicing can also help us
|
|---|
| 513 | * to recover from numerical errors. For example, suppose at one
|
|---|
| 514 | * point we checked eUp and eLo, and decided that eUp->Org is barely
|
|---|
| 515 | * above eLo. Then later, we split eLo into two edges (eg. from
|
|---|
| 516 | * a splice operation like this one). This can change the result of
|
|---|
| 517 | * our test so that now eUp->Org is incident to eLo, or barely below it.
|
|---|
| 518 | * We must correct this condition to maintain the dictionary invariants.
|
|---|
| 519 | *
|
|---|
| 520 | * One possibility is to check these edges for intersection again
|
|---|
| 521 | * (ie. CheckForIntersect). This is what we do if possible. However
|
|---|
| 522 | * CheckForIntersect requires that tess->event lies between eUp and eLo,
|
|---|
| 523 | * so that it has something to fall back on when the intersection
|
|---|
| 524 | * calculation gives us an unusable answer. So, for those cases where
|
|---|
| 525 | * we can't check for intersection, this routine fixes the problem
|
|---|
| 526 | * by just splicing the offending vertex into the other edge.
|
|---|
| 527 | * This is a guaranteed solution, no matter how degenerate things get.
|
|---|
| 528 | * Basically this is a combinatorial solution to a numerical problem.
|
|---|
| 529 | */
|
|---|
| 530 | {
|
|---|
| 531 | ActiveRegion *regLo = RegionBelow(regUp);
|
|---|
| 532 | GLUhalfEdge *eUp = regUp->eUp;
|
|---|
| 533 | GLUhalfEdge *eLo = regLo->eUp;
|
|---|
| 534 |
|
|---|
| 535 | if( VertLeq( eUp->Org, eLo->Org )) {
|
|---|
| 536 | if( EdgeSign( eLo->Dst, eUp->Org, eLo->Org ) > 0 ) return FALSE;
|
|---|
| 537 |
|
|---|
| 538 | /* eUp->Org appears to be below eLo */
|
|---|
| 539 | if( ! VertEq( eUp->Org, eLo->Org )) {
|
|---|
| 540 | /* Splice eUp->Org into eLo */
|
|---|
| 541 | if ( __gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1);
|
|---|
| 542 | if ( !__gl_meshSplice( eUp, eLo->Oprev ) ) longjmp(tess->env,1);
|
|---|
| 543 | regUp->dirty = regLo->dirty = TRUE;
|
|---|
| 544 |
|
|---|
| 545 | } else if( eUp->Org != eLo->Org ) {
|
|---|
| 546 | /* merge the two vertices, discarding eUp->Org */
|
|---|
| 547 | pqDelete( tess->pq, eUp->Org->pqHandle ); /* __gl_pqSortDelete */
|
|---|
| 548 | SpliceMergeVertices( tess, eLo->Oprev, eUp );
|
|---|
| 549 | }
|
|---|
| 550 | } else {
|
|---|
| 551 | if( EdgeSign( eUp->Dst, eLo->Org, eUp->Org ) < 0 ) return FALSE;
|
|---|
| 552 |
|
|---|
| 553 | /* eLo->Org appears to be above eUp, so splice eLo->Org into eUp */
|
|---|
| 554 | RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
|
|---|
| 555 | if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1);
|
|---|
| 556 | if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1);
|
|---|
| 557 | }
|
|---|
| 558 | return TRUE;
|
|---|
| 559 | }
|
|---|
| 560 |
|
|---|
| 561 | static int CheckForLeftSplice( GLUtesselator *tess, ActiveRegion *regUp )
|
|---|
| 562 | /*
|
|---|
| 563 | * Check the upper and lower edge of "regUp", to make sure that the
|
|---|
| 564 | * eUp->Dst is above eLo, or eLo->Dst is below eUp (depending on which
|
|---|
| 565 | * destination is rightmost).
|
|---|
| 566 | *
|
|---|
| 567 | * Theoretically, this should always be true. However, splitting an edge
|
|---|
| 568 | * into two pieces can change the results of previous tests. For example,
|
|---|
| 569 | * suppose at one point we checked eUp and eLo, and decided that eUp->Dst
|
|---|
| 570 | * is barely above eLo. Then later, we split eLo into two edges (eg. from
|
|---|
| 571 | * a splice operation like this one). This can change the result of
|
|---|
| 572 | * the test so that now eUp->Dst is incident to eLo, or barely below it.
|
|---|
| 573 | * We must correct this condition to maintain the dictionary invariants
|
|---|
| 574 | * (otherwise new edges might get inserted in the wrong place in the
|
|---|
| 575 | * dictionary, and bad stuff will happen).
|
|---|
| 576 | *
|
|---|
| 577 | * We fix the problem by just splicing the offending vertex into the
|
|---|
| 578 | * other edge.
|
|---|
| 579 | */
|
|---|
| 580 | {
|
|---|
| 581 | ActiveRegion *regLo = RegionBelow(regUp);
|
|---|
| 582 | GLUhalfEdge *eUp = regUp->eUp;
|
|---|
| 583 | GLUhalfEdge *eLo = regLo->eUp;
|
|---|
| 584 | GLUhalfEdge *e;
|
|---|
| 585 |
|
|---|
| 586 | assert( ! VertEq( eUp->Dst, eLo->Dst ));
|
|---|
| 587 |
|
|---|
| 588 | if( VertLeq( eUp->Dst, eLo->Dst )) {
|
|---|
| 589 | if( EdgeSign( eUp->Dst, eLo->Dst, eUp->Org ) < 0 ) return FALSE;
|
|---|
| 590 |
|
|---|
| 591 | /* eLo->Dst is above eUp, so splice eLo->Dst into eUp */
|
|---|
| 592 | RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
|
|---|
| 593 | e = __gl_meshSplitEdge( eUp );
|
|---|
| 594 | if (e == NULL) longjmp(tess->env,1);
|
|---|
| 595 | if ( !__gl_meshSplice( eLo->Sym, e ) ) longjmp(tess->env,1);
|
|---|
| 596 | e->Lface->inside = regUp->inside;
|
|---|
| 597 | } else {
|
|---|
| 598 | if( EdgeSign( eLo->Dst, eUp->Dst, eLo->Org ) > 0 ) return FALSE;
|
|---|
| 599 |
|
|---|
| 600 | /* eUp->Dst is below eLo, so splice eUp->Dst into eLo */
|
|---|
| 601 | regUp->dirty = regLo->dirty = TRUE;
|
|---|
| 602 | e = __gl_meshSplitEdge( eLo );
|
|---|
| 603 | if (e == NULL) longjmp(tess->env,1);
|
|---|
| 604 | if ( !__gl_meshSplice( eUp->Lnext, eLo->Sym ) ) longjmp(tess->env,1);
|
|---|
| 605 | e->Rface->inside = regUp->inside;
|
|---|
| 606 | }
|
|---|
| 607 | return TRUE;
|
|---|
| 608 | }
|
|---|
| 609 |
|
|---|
| 610 |
|
|---|
| 611 | static int CheckForIntersect( GLUtesselator *tess, ActiveRegion *regUp )
|
|---|
| 612 | /*
|
|---|
| 613 | * Check the upper and lower edges of the given region to see if
|
|---|
| 614 | * they intersect. If so, create the intersection and add it
|
|---|
| 615 | * to the data structures.
|
|---|
| 616 | *
|
|---|
| 617 | * Returns TRUE if adding the new intersection resulted in a recursive
|
|---|
| 618 | * call to AddRightEdges(); in this case all "dirty" regions have been
|
|---|
| 619 | * checked for intersections, and possibly regUp has been deleted.
|
|---|
| 620 | */
|
|---|
| 621 | {
|
|---|
| 622 | ActiveRegion *regLo = RegionBelow(regUp);
|
|---|
| 623 | GLUhalfEdge *eUp = regUp->eUp;
|
|---|
| 624 | GLUhalfEdge *eLo = regLo->eUp;
|
|---|
| 625 | GLUvertex *orgUp = eUp->Org;
|
|---|
| 626 | GLUvertex *orgLo = eLo->Org;
|
|---|
| 627 | GLUvertex *dstUp = eUp->Dst;
|
|---|
| 628 | GLUvertex *dstLo = eLo->Dst;
|
|---|
| 629 | GLdouble tMinUp, tMaxLo;
|
|---|
| 630 | GLUvertex isect, *orgMin;
|
|---|
| 631 | GLUhalfEdge *e;
|
|---|
| 632 |
|
|---|
| 633 | assert( ! VertEq( dstLo, dstUp ));
|
|---|
| 634 | assert( EdgeSign( dstUp, tess->event, orgUp ) <= 0 );
|
|---|
| 635 | assert( EdgeSign( dstLo, tess->event, orgLo ) >= 0 );
|
|---|
| 636 | assert( orgUp != tess->event && orgLo != tess->event );
|
|---|
| 637 | assert( ! regUp->fixUpperEdge && ! regLo->fixUpperEdge );
|
|---|
| 638 |
|
|---|
| 639 | if( orgUp == orgLo ) return FALSE; /* right endpoints are the same */
|
|---|
| 640 |
|
|---|
| 641 | tMinUp = MIN( orgUp->t, dstUp->t );
|
|---|
| 642 | tMaxLo = MAX( orgLo->t, dstLo->t );
|
|---|
| 643 | if( tMinUp > tMaxLo ) return FALSE; /* t ranges do not overlap */
|
|---|
| 644 |
|
|---|
| 645 | if( VertLeq( orgUp, orgLo )) {
|
|---|
| 646 | if( EdgeSign( dstLo, orgUp, orgLo ) > 0 ) return FALSE;
|
|---|
| 647 | } else {
|
|---|
| 648 | if( EdgeSign( dstUp, orgLo, orgUp ) < 0 ) return FALSE;
|
|---|
| 649 | }
|
|---|
| 650 |
|
|---|
| 651 | /* At this point the edges intersect, at least marginally */
|
|---|
| 652 | DebugEvent( tess );
|
|---|
| 653 |
|
|---|
| 654 | __gl_edgeIntersect( dstUp, orgUp, dstLo, orgLo, &isect );
|
|---|
| 655 | /* The following properties are guaranteed: */
|
|---|
| 656 | assert( MIN( orgUp->t, dstUp->t ) <= isect.t );
|
|---|
| 657 | assert( isect.t <= MAX( orgLo->t, dstLo->t ));
|
|---|
| 658 | assert( MIN( dstLo->s, dstUp->s ) <= isect.s );
|
|---|
| 659 | assert( isect.s <= MAX( orgLo->s, orgUp->s ));
|
|---|
| 660 |
|
|---|
| 661 | if( VertLeq( &isect, tess->event )) {
|
|---|
| 662 | /* The intersection point lies slightly to the left of the sweep line,
|
|---|
| 663 | * so move it until it''s slightly to the right of the sweep line.
|
|---|
| 664 | * (If we had perfect numerical precision, this would never happen
|
|---|
| 665 | * in the first place). The easiest and safest thing to do is
|
|---|
| 666 | * replace the intersection by tess->event.
|
|---|
| 667 | */
|
|---|
| 668 | isect.s = tess->event->s;
|
|---|
| 669 | isect.t = tess->event->t;
|
|---|
| 670 | }
|
|---|
| 671 | /* Similarly, if the computed intersection lies to the right of the
|
|---|
| 672 | * rightmost origin (which should rarely happen), it can cause
|
|---|
| 673 | * unbelievable inefficiency on sufficiently degenerate inputs.
|
|---|
| 674 | * (If you have the test program, try running test54.d with the
|
|---|
| 675 | * "X zoom" option turned on).
|
|---|
| 676 | */
|
|---|
| 677 | orgMin = VertLeq( orgUp, orgLo ) ? orgUp : orgLo;
|
|---|
| 678 | if( VertLeq( orgMin, &isect )) {
|
|---|
| 679 | isect.s = orgMin->s;
|
|---|
| 680 | isect.t = orgMin->t;
|
|---|
| 681 | }
|
|---|
| 682 |
|
|---|
| 683 | if( VertEq( &isect, orgUp ) || VertEq( &isect, orgLo )) {
|
|---|
| 684 | /* Easy case -- intersection at one of the right endpoints */
|
|---|
| 685 | (void) CheckForRightSplice( tess, regUp );
|
|---|
| 686 | return FALSE;
|
|---|
| 687 | }
|
|---|
| 688 |
|
|---|
| 689 | if( (! VertEq( dstUp, tess->event )
|
|---|
| 690 | && EdgeSign( dstUp, tess->event, &isect ) >= 0)
|
|---|
| 691 | || (! VertEq( dstLo, tess->event )
|
|---|
| 692 | && EdgeSign( dstLo, tess->event, &isect ) <= 0 ))
|
|---|
| 693 | {
|
|---|
| 694 | /* Very unusual -- the new upper or lower edge would pass on the
|
|---|
| 695 | * wrong side of the sweep event, or through it. This can happen
|
|---|
| 696 | * due to very small numerical errors in the intersection calculation.
|
|---|
| 697 | */
|
|---|
| 698 | if( dstLo == tess->event ) {
|
|---|
| 699 | /* Splice dstLo into eUp, and process the new region(s) */
|
|---|
| 700 | if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1);
|
|---|
| 701 | if ( !__gl_meshSplice( eLo->Sym, eUp ) ) longjmp(tess->env,1);
|
|---|
| 702 | regUp = TopLeftRegion( regUp );
|
|---|
| 703 | if (regUp == NULL) longjmp(tess->env,1);
|
|---|
| 704 | eUp = RegionBelow(regUp)->eUp;
|
|---|
| 705 | FinishLeftRegions( tess, RegionBelow(regUp), regLo );
|
|---|
| 706 | AddRightEdges( tess, regUp, eUp->Oprev, eUp, eUp, TRUE );
|
|---|
| 707 | return TRUE;
|
|---|
| 708 | }
|
|---|
| 709 | if( dstUp == tess->event ) {
|
|---|
| 710 | /* Splice dstUp into eLo, and process the new region(s) */
|
|---|
| 711 | if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1);
|
|---|
| 712 | if ( !__gl_meshSplice( eUp->Lnext, eLo->Oprev ) ) longjmp(tess->env,1);
|
|---|
| 713 | regLo = regUp;
|
|---|
| 714 | regUp = TopRightRegion( regUp );
|
|---|
| 715 | e = RegionBelow(regUp)->eUp->Rprev;
|
|---|
| 716 | regLo->eUp = eLo->Oprev;
|
|---|
| 717 | eLo = FinishLeftRegions( tess, regLo, NULL );
|
|---|
| 718 | AddRightEdges( tess, regUp, eLo->Onext, eUp->Rprev, e, TRUE );
|
|---|
| 719 | return TRUE;
|
|---|
| 720 | }
|
|---|
| 721 | /* Special case: called from ConnectRightVertex. If either
|
|---|
| 722 | * edge passes on the wrong side of tess->event, split it
|
|---|
| 723 | * (and wait for ConnectRightVertex to splice it appropriately).
|
|---|
| 724 | */
|
|---|
| 725 | if( EdgeSign( dstUp, tess->event, &isect ) >= 0 ) {
|
|---|
| 726 | RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
|
|---|
| 727 | if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1);
|
|---|
| 728 | eUp->Org->s = tess->event->s;
|
|---|
| 729 | eUp->Org->t = tess->event->t;
|
|---|
| 730 | }
|
|---|
| 731 | if( EdgeSign( dstLo, tess->event, &isect ) <= 0 ) {
|
|---|
| 732 | regUp->dirty = regLo->dirty = TRUE;
|
|---|
| 733 | if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1);
|
|---|
| 734 | eLo->Org->s = tess->event->s;
|
|---|
| 735 | eLo->Org->t = tess->event->t;
|
|---|
| 736 | }
|
|---|
| 737 | /* leave the rest for ConnectRightVertex */
|
|---|
| 738 | return FALSE;
|
|---|
| 739 | }
|
|---|
| 740 |
|
|---|
| 741 | /* General case -- split both edges, splice into new vertex.
|
|---|
| 742 | * When we do the splice operation, the order of the arguments is
|
|---|
| 743 | * arbitrary as far as correctness goes. However, when the operation
|
|---|
| 744 | * creates a new face, the work done is proportional to the size of
|
|---|
| 745 | * the new face. We expect the faces in the processed part of
|
|---|
| 746 | * the mesh (ie. eUp->Lface) to be smaller than the faces in the
|
|---|
| 747 | * unprocessed original contours (which will be eLo->Oprev->Lface).
|
|---|
| 748 | */
|
|---|
| 749 | if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1);
|
|---|
| 750 | if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1);
|
|---|
| 751 | if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1);
|
|---|
| 752 | eUp->Org->s = isect.s;
|
|---|
| 753 | eUp->Org->t = isect.t;
|
|---|
| 754 | eUp->Org->pqHandle = pqInsert( tess->pq, eUp->Org ); /* __gl_pqSortInsert */
|
|---|
| 755 | if (eUp->Org->pqHandle == LONG_MAX) {
|
|---|
| 756 | pqDeletePriorityQ(tess->pq); /* __gl_pqSortDeletePriorityQ */
|
|---|
| 757 | tess->pq = NULL;
|
|---|
| 758 | longjmp(tess->env,1);
|
|---|
| 759 | }
|
|---|
| 760 | GetIntersectData( tess, eUp->Org, orgUp, dstUp, orgLo, dstLo );
|
|---|
| 761 | RegionAbove(regUp)->dirty = regUp->dirty = regLo->dirty = TRUE;
|
|---|
| 762 | return FALSE;
|
|---|
| 763 | }
|
|---|
| 764 |
|
|---|
| 765 | static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp )
|
|---|
| 766 | /*
|
|---|
| 767 | * When the upper or lower edge of any region changes, the region is
|
|---|
| 768 | * marked "dirty". This routine walks through all the dirty regions
|
|---|
| 769 | * and makes sure that the dictionary invariants are satisfied
|
|---|
| 770 | * (see the comments at the beginning of this file). Of course
|
|---|
| 771 | * new dirty regions can be created as we make changes to restore
|
|---|
| 772 | * the invariants.
|
|---|
| 773 | */
|
|---|
| 774 | {
|
|---|
| 775 | ActiveRegion *regLo = RegionBelow(regUp);
|
|---|
| 776 | GLUhalfEdge *eUp, *eLo;
|
|---|
| 777 |
|
|---|
| 778 | for( ;; ) {
|
|---|
| 779 | /* Find the lowest dirty region (we walk from the bottom up). */
|
|---|
| 780 | while( regLo->dirty ) {
|
|---|
| 781 | regUp = regLo;
|
|---|
| 782 | regLo = RegionBelow(regLo);
|
|---|
| 783 | }
|
|---|
| 784 | if( ! regUp->dirty ) {
|
|---|
| 785 | regLo = regUp;
|
|---|
| 786 | regUp = RegionAbove( regUp );
|
|---|
| 787 | if( regUp == NULL || ! regUp->dirty ) {
|
|---|
| 788 | /* We've walked all the dirty regions */
|
|---|
| 789 | return;
|
|---|
| 790 | }
|
|---|
| 791 | }
|
|---|
| 792 | regUp->dirty = FALSE;
|
|---|
| 793 | eUp = regUp->eUp;
|
|---|
| 794 | eLo = regLo->eUp;
|
|---|
| 795 |
|
|---|
| 796 | if( eUp->Dst != eLo->Dst ) {
|
|---|
| 797 | /* Check that the edge ordering is obeyed at the Dst vertices. */
|
|---|
| 798 | if( CheckForLeftSplice( tess, regUp )) {
|
|---|
| 799 |
|
|---|
| 800 | /* If the upper or lower edge was marked fixUpperEdge, then
|
|---|
| 801 | * we no longer need it (since these edges are needed only for
|
|---|
| 802 | * vertices which otherwise have no right-going edges).
|
|---|
| 803 | */
|
|---|
| 804 | if( regLo->fixUpperEdge ) {
|
|---|
| 805 | DeleteRegion( tess, regLo );
|
|---|
| 806 | if ( !__gl_meshDelete( eLo ) ) longjmp(tess->env,1);
|
|---|
| 807 | regLo = RegionBelow( regUp );
|
|---|
| 808 | eLo = regLo->eUp;
|
|---|
| 809 | } else if( regUp->fixUpperEdge ) {
|
|---|
| 810 | DeleteRegion( tess, regUp );
|
|---|
| 811 | if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1);
|
|---|
| 812 | regUp = RegionAbove( regLo );
|
|---|
| 813 | eUp = regUp->eUp;
|
|---|
| 814 | }
|
|---|
| 815 | }
|
|---|
| 816 | }
|
|---|
| 817 | if( eUp->Org != eLo->Org ) {
|
|---|
| 818 | if( eUp->Dst != eLo->Dst
|
|---|
| 819 | && ! regUp->fixUpperEdge && ! regLo->fixUpperEdge
|
|---|
| 820 | && (eUp->Dst == tess->event || eLo->Dst == tess->event) )
|
|---|
| 821 | {
|
|---|
| 822 | /* When all else fails in CheckForIntersect(), it uses tess->event
|
|---|
| 823 | * as the intersection location. To make this possible, it requires
|
|---|
| 824 | * that tess->event lie between the upper and lower edges, and also
|
|---|
| 825 | * that neither of these is marked fixUpperEdge (since in the worst
|
|---|
| 826 | * case it might splice one of these edges into tess->event, and
|
|---|
| 827 | * violate the invariant that fixable edges are the only right-going
|
|---|
| 828 | * edge from their associated vertex).
|
|---|
| 829 | */
|
|---|
| 830 | if( CheckForIntersect( tess, regUp )) {
|
|---|
| 831 | /* WalkDirtyRegions() was called recursively; we're done */
|
|---|
| 832 | return;
|
|---|
| 833 | }
|
|---|
| 834 | } else {
|
|---|
| 835 | /* Even though we can't use CheckForIntersect(), the Org vertices
|
|---|
| 836 | * may violate the dictionary edge ordering. Check and correct this.
|
|---|
| 837 | */
|
|---|
| 838 | (void) CheckForRightSplice( tess, regUp );
|
|---|
| 839 | }
|
|---|
| 840 | }
|
|---|
| 841 | if( eUp->Org == eLo->Org && eUp->Dst == eLo->Dst ) {
|
|---|
| 842 | /* A degenerate loop consisting of only two edges -- delete it. */
|
|---|
| 843 | AddWinding( eLo, eUp );
|
|---|
| 844 | DeleteRegion( tess, regUp );
|
|---|
| 845 | if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1);
|
|---|
| 846 | regUp = RegionAbove( regLo );
|
|---|
| 847 | }
|
|---|
| 848 | }
|
|---|
| 849 | }
|
|---|
| 850 |
|
|---|
| 851 |
|
|---|
| 852 | static void ConnectRightVertex( GLUtesselator *tess, ActiveRegion *regUp,
|
|---|
| 853 | GLUhalfEdge *eBottomLeft )
|
|---|
| 854 | /*
|
|---|
| 855 | * Purpose: connect a "right" vertex vEvent (one where all edges go left)
|
|---|
| 856 | * to the unprocessed portion of the mesh. Since there are no right-going
|
|---|
| 857 | * edges, two regions (one above vEvent and one below) are being merged
|
|---|
| 858 | * into one. "regUp" is the upper of these two regions.
|
|---|
| 859 | *
|
|---|
| 860 | * There are two reasons for doing this (adding a right-going edge):
|
|---|
| 861 | * - if the two regions being merged are "inside", we must add an edge
|
|---|
| 862 | * to keep them separated (the combined region would not be monotone).
|
|---|
| 863 | * - in any case, we must leave some record of vEvent in the dictionary,
|
|---|
| 864 | * so that we can merge vEvent with features that we have not seen yet.
|
|---|
| 865 | * For example, maybe there is a vertical edge which passes just to
|
|---|
| 866 | * the right of vEvent; we would like to splice vEvent into this edge.
|
|---|
| 867 | *
|
|---|
| 868 | * However, we don't want to connect vEvent to just any vertex. We don''t
|
|---|
| 869 | * want the new edge to cross any other edges; otherwise we will create
|
|---|
| 870 | * intersection vertices even when the input data had no self-intersections.
|
|---|
| 871 | * (This is a bad thing; if the user's input data has no intersections,
|
|---|
| 872 | * we don't want to generate any false intersections ourselves.)
|
|---|
| 873 | *
|
|---|
| 874 | * Our eventual goal is to connect vEvent to the leftmost unprocessed
|
|---|
| 875 | * vertex of the combined region (the union of regUp and regLo).
|
|---|
| 876 | * But because of unseen vertices with all right-going edges, and also
|
|---|
| 877 | * new vertices which may be created by edge intersections, we don''t
|
|---|
| 878 | * know where that leftmost unprocessed vertex is. In the meantime, we
|
|---|
| 879 | * connect vEvent to the closest vertex of either chain, and mark the region
|
|---|
| 880 | * as "fixUpperEdge". This flag says to delete and reconnect this edge
|
|---|
| 881 | * to the next processed vertex on the boundary of the combined region.
|
|---|
| 882 | * Quite possibly the vertex we connected to will turn out to be the
|
|---|
| 883 | * closest one, in which case we won''t need to make any changes.
|
|---|
| 884 | */
|
|---|
| 885 | {
|
|---|
| 886 | GLUhalfEdge *eNew;
|
|---|
| 887 | GLUhalfEdge *eTopLeft = eBottomLeft->Onext;
|
|---|
| 888 | ActiveRegion *regLo = RegionBelow(regUp);
|
|---|
| 889 | GLUhalfEdge *eUp = regUp->eUp;
|
|---|
| 890 | GLUhalfEdge *eLo = regLo->eUp;
|
|---|
| 891 | int degenerate = FALSE;
|
|---|
| 892 |
|
|---|
| 893 | if( eUp->Dst != eLo->Dst ) {
|
|---|
| 894 | (void) CheckForIntersect( tess, regUp );
|
|---|
| 895 | }
|
|---|
| 896 |
|
|---|
| 897 | /* Possible new degeneracies: upper or lower edge of regUp may pass
|
|---|
| 898 | * through vEvent, or may coincide with new intersection vertex
|
|---|
| 899 | */
|
|---|
| 900 | if( VertEq( eUp->Org, tess->event )) {
|
|---|
| 901 | if ( !__gl_meshSplice( eTopLeft->Oprev, eUp ) ) longjmp(tess->env,1);
|
|---|
| 902 | regUp = TopLeftRegion( regUp );
|
|---|
| 903 | if (regUp == NULL) longjmp(tess->env,1);
|
|---|
| 904 | eTopLeft = RegionBelow( regUp )->eUp;
|
|---|
| 905 | FinishLeftRegions( tess, RegionBelow(regUp), regLo );
|
|---|
| 906 | degenerate = TRUE;
|
|---|
| 907 | }
|
|---|
| 908 | if( VertEq( eLo->Org, tess->event )) {
|
|---|
| 909 | if ( !__gl_meshSplice( eBottomLeft, eLo->Oprev ) ) longjmp(tess->env,1);
|
|---|
| 910 | eBottomLeft = FinishLeftRegions( tess, regLo, NULL );
|
|---|
| 911 | degenerate = TRUE;
|
|---|
| 912 | }
|
|---|
| 913 | if( degenerate ) {
|
|---|
| 914 | AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE );
|
|---|
| 915 | return;
|
|---|
| 916 | }
|
|---|
| 917 |
|
|---|
| 918 | /* Non-degenerate situation -- need to add a temporary, fixable edge.
|
|---|
| 919 | * Connect to the closer of eLo->Org, eUp->Org.
|
|---|
| 920 | */
|
|---|
| 921 | if( VertLeq( eLo->Org, eUp->Org )) {
|
|---|
| 922 | eNew = eLo->Oprev;
|
|---|
| 923 | } else {
|
|---|
| 924 | eNew = eUp;
|
|---|
| 925 | }
|
|---|
| 926 | eNew = __gl_meshConnect( eBottomLeft->Lprev, eNew );
|
|---|
| 927 | if (eNew == NULL) longjmp(tess->env,1);
|
|---|
| 928 |
|
|---|
| 929 | /* Prevent cleanup, otherwise eNew might disappear before we've even
|
|---|
| 930 | * had a chance to mark it as a temporary edge.
|
|---|
| 931 | */
|
|---|
| 932 | AddRightEdges( tess, regUp, eNew, eNew->Onext, eNew->Onext, FALSE );
|
|---|
| 933 | eNew->Sym->activeRegion->fixUpperEdge = TRUE;
|
|---|
| 934 | WalkDirtyRegions( tess, regUp );
|
|---|
| 935 | }
|
|---|
| 936 |
|
|---|
| 937 | /* Because vertices at exactly the same location are merged together
|
|---|
| 938 | * before we process the sweep event, some degenerate cases can't occur.
|
|---|
| 939 | * However if someone eventually makes the modifications required to
|
|---|
| 940 | * merge features which are close together, the cases below marked
|
|---|
| 941 | * TOLERANCE_NONZERO will be useful. They were debugged before the
|
|---|
| 942 | * code to merge identical vertices in the main loop was added.
|
|---|
| 943 | */
|
|---|
| 944 | #define TOLERANCE_NONZERO FALSE
|
|---|
| 945 |
|
|---|
| 946 | static void ConnectLeftDegenerate( GLUtesselator *tess,
|
|---|
| 947 | ActiveRegion *regUp, GLUvertex *vEvent )
|
|---|
| 948 | /*
|
|---|
| 949 | * The event vertex lies exacty on an already-processed edge or vertex.
|
|---|
| 950 | * Adding the new vertex involves splicing it into the already-processed
|
|---|
| 951 | * part of the mesh.
|
|---|
| 952 | */
|
|---|
| 953 | {
|
|---|
| 954 | GLUhalfEdge *e, *eTopLeft, *eTopRight, *eLast;
|
|---|
| 955 | ActiveRegion *reg;
|
|---|
| 956 |
|
|---|
| 957 | e = regUp->eUp;
|
|---|
| 958 | if( VertEq( e->Org, vEvent )) {
|
|---|
| 959 | /* e->Org is an unprocessed vertex - just combine them, and wait
|
|---|
| 960 | * for e->Org to be pulled from the queue
|
|---|
| 961 | */
|
|---|
| 962 | assert( TOLERANCE_NONZERO );
|
|---|
| 963 | SpliceMergeVertices( tess, e, vEvent->anEdge );
|
|---|
| 964 | return;
|
|---|
| 965 | }
|
|---|
| 966 |
|
|---|
| 967 | if( ! VertEq( e->Dst, vEvent )) {
|
|---|
| 968 | /* General case -- splice vEvent into edge e which passes through it */
|
|---|
| 969 | if (__gl_meshSplitEdge( e->Sym ) == NULL) longjmp(tess->env,1);
|
|---|
| 970 | if( regUp->fixUpperEdge ) {
|
|---|
| 971 | /* This edge was fixable -- delete unused portion of original edge */
|
|---|
| 972 | if ( !__gl_meshDelete( e->Onext ) ) longjmp(tess->env,1);
|
|---|
| 973 | regUp->fixUpperEdge = FALSE;
|
|---|
| 974 | }
|
|---|
| 975 | if ( !__gl_meshSplice( vEvent->anEdge, e ) ) longjmp(tess->env,1);
|
|---|
| 976 | SweepEvent( tess, vEvent ); /* recurse */
|
|---|
| 977 | return;
|
|---|
| 978 | }
|
|---|
| 979 |
|
|---|
| 980 | /* vEvent coincides with e->Dst, which has already been processed.
|
|---|
| 981 | * Splice in the additional right-going edges.
|
|---|
| 982 | */
|
|---|
| 983 | assert( TOLERANCE_NONZERO );
|
|---|
| 984 | regUp = TopRightRegion( regUp );
|
|---|
| 985 | reg = RegionBelow( regUp );
|
|---|
| 986 | eTopRight = reg->eUp->Sym;
|
|---|
| 987 | eTopLeft = eLast = eTopRight->Onext;
|
|---|
| 988 | if( reg->fixUpperEdge ) {
|
|---|
| 989 | /* Here e->Dst has only a single fixable edge going right.
|
|---|
| 990 | * We can delete it since now we have some real right-going edges.
|
|---|
| 991 | */
|
|---|
| 992 | assert( eTopLeft != eTopRight ); /* there are some left edges too */
|
|---|
| 993 | DeleteRegion( tess, reg );
|
|---|
| 994 | if ( !__gl_meshDelete( eTopRight ) ) longjmp(tess->env,1);
|
|---|
| 995 | eTopRight = eTopLeft->Oprev;
|
|---|
| 996 | }
|
|---|
| 997 | if ( !__gl_meshSplice( vEvent->anEdge, eTopRight ) ) longjmp(tess->env,1);
|
|---|
| 998 | if( ! EdgeGoesLeft( eTopLeft )) {
|
|---|
| 999 | /* e->Dst had no left-going edges -- indicate this to AddRightEdges() */
|
|---|
| 1000 | eTopLeft = NULL;
|
|---|
| 1001 | }
|
|---|
| 1002 | AddRightEdges( tess, regUp, eTopRight->Onext, eLast, eTopLeft, TRUE );
|
|---|
| 1003 | }
|
|---|
| 1004 |
|
|---|
| 1005 |
|
|---|
| 1006 | static void ConnectLeftVertex( GLUtesselator *tess, GLUvertex *vEvent )
|
|---|
| 1007 | /*
|
|---|
| 1008 | * Purpose: connect a "left" vertex (one where both edges go right)
|
|---|
| 1009 | * to the processed portion of the mesh. Let R be the active region
|
|---|
| 1010 | * containing vEvent, and let U and L be the upper and lower edge
|
|---|
| 1011 | * chains of R. There are two possibilities:
|
|---|
| 1012 | *
|
|---|
| 1013 | * - the normal case: split R into two regions, by connecting vEvent to
|
|---|
| 1014 | * the rightmost vertex of U or L lying to the left of the sweep line
|
|---|
| 1015 | *
|
|---|
| 1016 | * - the degenerate case: if vEvent is close enough to U or L, we
|
|---|
| 1017 | * merge vEvent into that edge chain. The subcases are:
|
|---|
| 1018 | * - merging with the rightmost vertex of U or L
|
|---|
| 1019 | * - merging with the active edge of U or L
|
|---|
| 1020 | * - merging with an already-processed portion of U or L
|
|---|
| 1021 | */
|
|---|
| 1022 | {
|
|---|
| 1023 | ActiveRegion *regUp, *regLo, *reg;
|
|---|
| 1024 | GLUhalfEdge *eUp, *eLo, *eNew;
|
|---|
| 1025 | ActiveRegion tmp;
|
|---|
| 1026 |
|
|---|
| 1027 | /* assert( vEvent->anEdge->Onext->Onext == vEvent->anEdge ); */
|
|---|
| 1028 |
|
|---|
| 1029 | /* Get a pointer to the active region containing vEvent */
|
|---|
| 1030 | tmp.eUp = vEvent->anEdge->Sym;
|
|---|
| 1031 | /* __GL_DICTLISTKEY */ /* __gl_dictListSearch */
|
|---|
| 1032 | regUp = (ActiveRegion *)dictKey( dictSearch( tess->dict, &tmp ));
|
|---|
| 1033 | regLo = RegionBelow( regUp );
|
|---|
| 1034 | eUp = regUp->eUp;
|
|---|
| 1035 | eLo = regLo->eUp;
|
|---|
| 1036 |
|
|---|
| 1037 | /* Try merging with U or L first */
|
|---|
| 1038 | if( EdgeSign( eUp->Dst, vEvent, eUp->Org ) == 0 ) {
|
|---|
| 1039 | ConnectLeftDegenerate( tess, regUp, vEvent );
|
|---|
| 1040 | return;
|
|---|
| 1041 | }
|
|---|
| 1042 |
|
|---|
| 1043 | /* Connect vEvent to rightmost processed vertex of either chain.
|
|---|
| 1044 | * e->Dst is the vertex that we will connect to vEvent.
|
|---|
| 1045 | */
|
|---|
| 1046 | reg = VertLeq( eLo->Dst, eUp->Dst ) ? regUp : regLo;
|
|---|
| 1047 |
|
|---|
| 1048 | if( regUp->inside || reg->fixUpperEdge) {
|
|---|
| 1049 | if( reg == regUp ) {
|
|---|
| 1050 | eNew = __gl_meshConnect( vEvent->anEdge->Sym, eUp->Lnext );
|
|---|
| 1051 | if (eNew == NULL) longjmp(tess->env,1);
|
|---|
| 1052 | } else {
|
|---|
| 1053 | GLUhalfEdge *tempHalfEdge= __gl_meshConnect( eLo->Dnext, vEvent->anEdge);
|
|---|
| 1054 | if (tempHalfEdge == NULL) longjmp(tess->env,1);
|
|---|
| 1055 |
|
|---|
| 1056 | eNew = tempHalfEdge->Sym;
|
|---|
| 1057 | }
|
|---|
| 1058 | if( reg->fixUpperEdge ) {
|
|---|
| 1059 | if ( !FixUpperEdge( reg, eNew ) ) longjmp(tess->env,1);
|
|---|
| 1060 | } else {
|
|---|
| 1061 | ComputeWinding( tess, AddRegionBelow( tess, regUp, eNew ));
|
|---|
| 1062 | }
|
|---|
| 1063 | SweepEvent( tess, vEvent );
|
|---|
| 1064 | } else {
|
|---|
| 1065 | /* The new vertex is in a region which does not belong to the polygon.
|
|---|
| 1066 | * We don''t need to connect this vertex to the rest of the mesh.
|
|---|
| 1067 | */
|
|---|
| 1068 | AddRightEdges( tess, regUp, vEvent->anEdge, vEvent->anEdge, NULL, TRUE );
|
|---|
| 1069 | }
|
|---|
| 1070 | }
|
|---|
| 1071 |
|
|---|
| 1072 |
|
|---|
| 1073 | static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent )
|
|---|
| 1074 | /*
|
|---|
| 1075 | * Does everything necessary when the sweep line crosses a vertex.
|
|---|
| 1076 | * Updates the mesh and the edge dictionary.
|
|---|
| 1077 | */
|
|---|
| 1078 | {
|
|---|
| 1079 | ActiveRegion *regUp, *reg;
|
|---|
| 1080 | GLUhalfEdge *e, *eTopLeft, *eBottomLeft;
|
|---|
| 1081 |
|
|---|
| 1082 | tess->event = vEvent; /* for access in EdgeLeq() */
|
|---|
| 1083 | DebugEvent( tess );
|
|---|
| 1084 |
|
|---|
| 1085 | /* Check if this vertex is the right endpoint of an edge that is
|
|---|
| 1086 | * already in the dictionary. In this case we don't need to waste
|
|---|
| 1087 | * time searching for the location to insert new edges.
|
|---|
| 1088 | */
|
|---|
| 1089 | e = vEvent->anEdge;
|
|---|
| 1090 | while( e->activeRegion == NULL ) {
|
|---|
| 1091 | e = e->Onext;
|
|---|
| 1092 | if( e == vEvent->anEdge ) {
|
|---|
| 1093 | /* All edges go right -- not incident to any processed edges */
|
|---|
| 1094 | ConnectLeftVertex( tess, vEvent );
|
|---|
| 1095 | return;
|
|---|
| 1096 | }
|
|---|
| 1097 | }
|
|---|
| 1098 |
|
|---|
| 1099 | /* Processing consists of two phases: first we "finish" all the
|
|---|
| 1100 | * active regions where both the upper and lower edges terminate
|
|---|
| 1101 | * at vEvent (ie. vEvent is closing off these regions).
|
|---|
| 1102 | * We mark these faces "inside" or "outside" the polygon according
|
|---|
| 1103 | * to their winding number, and delete the edges from the dictionary.
|
|---|
| 1104 | * This takes care of all the left-going edges from vEvent.
|
|---|
| 1105 | */
|
|---|
| 1106 | regUp = TopLeftRegion( e->activeRegion );
|
|---|
| 1107 | if (regUp == NULL) longjmp(tess->env,1);
|
|---|
| 1108 | reg = RegionBelow( regUp );
|
|---|
| 1109 | eTopLeft = reg->eUp;
|
|---|
| 1110 | eBottomLeft = FinishLeftRegions( tess, reg, NULL );
|
|---|
| 1111 |
|
|---|
| 1112 | /* Next we process all the right-going edges from vEvent. This
|
|---|
| 1113 | * involves adding the edges to the dictionary, and creating the
|
|---|
| 1114 | * associated "active regions" which record information about the
|
|---|
| 1115 | * regions between adjacent dictionary edges.
|
|---|
| 1116 | */
|
|---|
| 1117 | if( eBottomLeft->Onext == eTopLeft ) {
|
|---|
| 1118 | /* No right-going edges -- add a temporary "fixable" edge */
|
|---|
| 1119 | ConnectRightVertex( tess, regUp, eBottomLeft );
|
|---|
| 1120 | } else {
|
|---|
| 1121 | AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE );
|
|---|
| 1122 | }
|
|---|
| 1123 | }
|
|---|
| 1124 |
|
|---|
| 1125 |
|
|---|
| 1126 | /* Make the sentinel coordinates big enough that they will never be
|
|---|
| 1127 | * merged with real input features. (Even with the largest possible
|
|---|
| 1128 | * input contour and the maximum tolerance of 1.0, no merging will be
|
|---|
| 1129 | * done with coordinates larger than 3 * GLU_TESS_MAX_COORD).
|
|---|
| 1130 | */
|
|---|
| 1131 | #define SENTINEL_COORD (4 * GLU_TESS_MAX_COORD)
|
|---|
| 1132 |
|
|---|
| 1133 | static void AddSentinel( GLUtesselator *tess, GLdouble t )
|
|---|
| 1134 | /*
|
|---|
| 1135 | * We add two sentinel edges above and below all other edges,
|
|---|
| 1136 | * to avoid special cases at the top and bottom.
|
|---|
| 1137 | */
|
|---|
| 1138 | {
|
|---|
| 1139 | GLUhalfEdge *e;
|
|---|
| 1140 | ActiveRegion *reg = (ActiveRegion *)memAlloc( sizeof( ActiveRegion ));
|
|---|
| 1141 | if (reg == NULL) longjmp(tess->env,1);
|
|---|
| 1142 |
|
|---|
| 1143 | e = __gl_meshMakeEdge( tess->mesh );
|
|---|
| 1144 | if (e == NULL) longjmp(tess->env,1);
|
|---|
| 1145 |
|
|---|
| 1146 | e->Org->s = SENTINEL_COORD;
|
|---|
| 1147 | e->Org->t = t;
|
|---|
| 1148 | e->Dst->s = -SENTINEL_COORD;
|
|---|
| 1149 | e->Dst->t = t;
|
|---|
| 1150 | tess->event = e->Dst; /* initialize it */
|
|---|
| 1151 |
|
|---|
| 1152 | reg->eUp = e;
|
|---|
| 1153 | reg->windingNumber = 0;
|
|---|
| 1154 | reg->inside = FALSE;
|
|---|
| 1155 | reg->fixUpperEdge = FALSE;
|
|---|
| 1156 | reg->sentinel = TRUE;
|
|---|
| 1157 | reg->dirty = FALSE;
|
|---|
| 1158 | reg->nodeUp = dictInsert( tess->dict, reg ); /* __gl_dictListInsertBefore */
|
|---|
| 1159 | if (reg->nodeUp == NULL) longjmp(tess->env,1);
|
|---|
| 1160 | }
|
|---|
| 1161 |
|
|---|
| 1162 |
|
|---|
| 1163 | static void InitEdgeDict( GLUtesselator *tess )
|
|---|
| 1164 | /*
|
|---|
| 1165 | * We maintain an ordering of edge intersections with the sweep line.
|
|---|
| 1166 | * This order is maintained in a dynamic dictionary.
|
|---|
| 1167 | */
|
|---|
| 1168 | {
|
|---|
| 1169 | /* __gl_dictListNewDict */
|
|---|
| 1170 | tess->dict = dictNewDict( tess, (int (*)(void *, DictKey, DictKey)) EdgeLeq );
|
|---|
| 1171 | if (tess->dict == NULL) longjmp(tess->env,1);
|
|---|
| 1172 |
|
|---|
| 1173 | AddSentinel( tess, -SENTINEL_COORD );
|
|---|
| 1174 | AddSentinel( tess, SENTINEL_COORD );
|
|---|
| 1175 | }
|
|---|
| 1176 |
|
|---|
| 1177 |
|
|---|
| 1178 | static void DoneEdgeDict( GLUtesselator *tess )
|
|---|
| 1179 | {
|
|---|
| 1180 | ActiveRegion *reg;
|
|---|
| 1181 | int fixedEdges = 0;
|
|---|
| 1182 |
|
|---|
| 1183 | /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */
|
|---|
| 1184 | while( (reg = (ActiveRegion *)dictKey( dictMin( tess->dict ))) != NULL ) {
|
|---|
| 1185 | /*
|
|---|
| 1186 | * At the end of all processing, the dictionary should contain
|
|---|
| 1187 | * only the two sentinel edges, plus at most one "fixable" edge
|
|---|
| 1188 | * created by ConnectRightVertex().
|
|---|
| 1189 | */
|
|---|
| 1190 | if( ! reg->sentinel ) {
|
|---|
| 1191 | assert( reg->fixUpperEdge );
|
|---|
| 1192 | assert( ++fixedEdges == 1 );
|
|---|
| 1193 | }
|
|---|
| 1194 | assert( reg->windingNumber == 0 );
|
|---|
| 1195 | DeleteRegion( tess, reg );
|
|---|
| 1196 | /* __gl_meshDelete( reg->eUp );*/
|
|---|
| 1197 | }
|
|---|
| 1198 | dictDeleteDict( tess->dict ); /* __gl_dictListDeleteDict */
|
|---|
| 1199 | }
|
|---|
| 1200 |
|
|---|
| 1201 |
|
|---|
| 1202 | static void RemoveDegenerateEdges( GLUtesselator *tess )
|
|---|
| 1203 | /*
|
|---|
| 1204 | * Remove zero-length edges, and contours with fewer than 3 vertices.
|
|---|
| 1205 | */
|
|---|
| 1206 | {
|
|---|
| 1207 | GLUhalfEdge *e, *eNext, *eLnext;
|
|---|
| 1208 | GLUhalfEdge *eHead = &tess->mesh->eHead;
|
|---|
| 1209 |
|
|---|
| 1210 | /*LINTED*/
|
|---|
| 1211 | for( e = eHead->next; e != eHead; e = eNext ) {
|
|---|
| 1212 | eNext = e->next;
|
|---|
| 1213 | eLnext = e->Lnext;
|
|---|
| 1214 |
|
|---|
| 1215 | if( VertEq( e->Org, e->Dst ) && e->Lnext->Lnext != e ) {
|
|---|
| 1216 | /* Zero-length edge, contour has at least 3 edges */
|
|---|
| 1217 |
|
|---|
| 1218 | SpliceMergeVertices( tess, eLnext, e ); /* deletes e->Org */
|
|---|
| 1219 | if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1); /* e is a self-loop */
|
|---|
| 1220 | e = eLnext;
|
|---|
| 1221 | eLnext = e->Lnext;
|
|---|
| 1222 | }
|
|---|
| 1223 | if( eLnext->Lnext == e ) {
|
|---|
| 1224 | /* Degenerate contour (one or two edges) */
|
|---|
| 1225 |
|
|---|
| 1226 | if( eLnext != e ) {
|
|---|
| 1227 | if( eLnext == eNext || eLnext == eNext->Sym ) { eNext = eNext->next; }
|
|---|
| 1228 | if ( !__gl_meshDelete( eLnext ) ) longjmp(tess->env,1);
|
|---|
| 1229 | }
|
|---|
| 1230 | if( e == eNext || e == eNext->Sym ) { eNext = eNext->next; }
|
|---|
| 1231 | if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1);
|
|---|
| 1232 | }
|
|---|
| 1233 | }
|
|---|
| 1234 | }
|
|---|
| 1235 |
|
|---|
| 1236 | static int InitPriorityQ( GLUtesselator *tess )
|
|---|
| 1237 | /*
|
|---|
| 1238 | * Insert all vertices into the priority queue which determines the
|
|---|
| 1239 | * order in which vertices cross the sweep line.
|
|---|
| 1240 | */
|
|---|
| 1241 | {
|
|---|
| 1242 | PriorityQ *pq;
|
|---|
| 1243 | GLUvertex *v, *vHead;
|
|---|
| 1244 |
|
|---|
| 1245 | /* __gl_pqSortNewPriorityQ */
|
|---|
| 1246 | pq = tess->pq = pqNewPriorityQ( (int (*)(PQkey, PQkey)) __gl_vertLeq );
|
|---|
| 1247 | if (pq == NULL) return 0;
|
|---|
| 1248 |
|
|---|
| 1249 | vHead = &tess->mesh->vHead;
|
|---|
| 1250 | for( v = vHead->next; v != vHead; v = v->next ) {
|
|---|
| 1251 | v->pqHandle = pqInsert( pq, v ); /* __gl_pqSortInsert */
|
|---|
| 1252 | if (v->pqHandle == LONG_MAX) break;
|
|---|
| 1253 | }
|
|---|
| 1254 | if (v != vHead || !pqInit( pq ) ) { /* __gl_pqSortInit */
|
|---|
| 1255 | pqDeletePriorityQ(tess->pq); /* __gl_pqSortDeletePriorityQ */
|
|---|
| 1256 | tess->pq = NULL;
|
|---|
| 1257 | return 0;
|
|---|
| 1258 | }
|
|---|
| 1259 |
|
|---|
| 1260 | return 1;
|
|---|
| 1261 | }
|
|---|
| 1262 |
|
|---|
| 1263 |
|
|---|
| 1264 | static void DonePriorityQ( GLUtesselator *tess )
|
|---|
| 1265 | {
|
|---|
| 1266 | pqDeletePriorityQ( tess->pq ); /* __gl_pqSortDeletePriorityQ */
|
|---|
| 1267 | }
|
|---|
| 1268 |
|
|---|
| 1269 |
|
|---|
| 1270 | static int RemoveDegenerateFaces( GLUmesh *mesh )
|
|---|
| 1271 | /*
|
|---|
| 1272 | * Delete any degenerate faces with only two edges. WalkDirtyRegions()
|
|---|
| 1273 | * will catch almost all of these, but it won't catch degenerate faces
|
|---|
| 1274 | * produced by splice operations on already-processed edges.
|
|---|
| 1275 | * The two places this can happen are in FinishLeftRegions(), when
|
|---|
| 1276 | * we splice in a "temporary" edge produced by ConnectRightVertex(),
|
|---|
| 1277 | * and in CheckForLeftSplice(), where we splice already-processed
|
|---|
| 1278 | * edges to ensure that our dictionary invariants are not violated
|
|---|
| 1279 | * by numerical errors.
|
|---|
| 1280 | *
|
|---|
| 1281 | * In both these cases it is *very* dangerous to delete the offending
|
|---|
| 1282 | * edge at the time, since one of the routines further up the stack
|
|---|
| 1283 | * will sometimes be keeping a pointer to that edge.
|
|---|
| 1284 | */
|
|---|
| 1285 | {
|
|---|
| 1286 | GLUface *f, *fNext;
|
|---|
| 1287 | GLUhalfEdge *e;
|
|---|
| 1288 |
|
|---|
| 1289 | /*LINTED*/
|
|---|
| 1290 | for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) {
|
|---|
| 1291 | fNext = f->next;
|
|---|
| 1292 | e = f->anEdge;
|
|---|
| 1293 | assert( e->Lnext != e );
|
|---|
| 1294 |
|
|---|
| 1295 | if( e->Lnext->Lnext == e ) {
|
|---|
| 1296 | /* A face with only two edges */
|
|---|
| 1297 | AddWinding( e->Onext, e );
|
|---|
| 1298 | if ( !__gl_meshDelete( e ) ) return 0;
|
|---|
| 1299 | }
|
|---|
| 1300 | }
|
|---|
| 1301 | return 1;
|
|---|
| 1302 | }
|
|---|
| 1303 |
|
|---|
| 1304 | int __gl_computeInterior( GLUtesselator *tess )
|
|---|
| 1305 | /*
|
|---|
| 1306 | * __gl_computeInterior( tess ) computes the planar arrangement specified
|
|---|
| 1307 | * by the given contours, and further subdivides this arrangement
|
|---|
| 1308 | * into regions. Each region is marked "inside" if it belongs
|
|---|
| 1309 | * to the polygon, according to the rule given by tess->windingRule.
|
|---|
| 1310 | * Each interior region is guaranteed be monotone.
|
|---|
| 1311 | */
|
|---|
| 1312 | {
|
|---|
| 1313 | GLUvertex *v, *vNext;
|
|---|
| 1314 |
|
|---|
| 1315 | tess->fatalError = FALSE;
|
|---|
| 1316 |
|
|---|
| 1317 | /* Each vertex defines an event for our sweep line. Start by inserting
|
|---|
| 1318 | * all the vertices in a priority queue. Events are processed in
|
|---|
| 1319 | * lexicographic order, ie.
|
|---|
| 1320 | *
|
|---|
| 1321 | * e1 < e2 iff e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y)
|
|---|
| 1322 | */
|
|---|
| 1323 | RemoveDegenerateEdges( tess );
|
|---|
| 1324 | if ( !InitPriorityQ( tess ) ) return 0; /* if error */
|
|---|
| 1325 | InitEdgeDict( tess );
|
|---|
| 1326 |
|
|---|
| 1327 | /* __gl_pqSortExtractMin */
|
|---|
| 1328 | while( (v = (GLUvertex *)pqExtractMin( tess->pq )) != NULL ) {
|
|---|
| 1329 | for( ;; ) {
|
|---|
| 1330 | vNext = (GLUvertex *)pqMinimum( tess->pq ); /* __gl_pqSortMinimum */
|
|---|
| 1331 | if( vNext == NULL || ! VertEq( vNext, v )) break;
|
|---|
| 1332 |
|
|---|
| 1333 | /* Merge together all vertices at exactly the same location.
|
|---|
| 1334 | * This is more efficient than processing them one at a time,
|
|---|
| 1335 | * simplifies the code (see ConnectLeftDegenerate), and is also
|
|---|
| 1336 | * important for correct handling of certain degenerate cases.
|
|---|
| 1337 | * For example, suppose there are two identical edges A and B
|
|---|
| 1338 | * that belong to different contours (so without this code they would
|
|---|
| 1339 | * be processed by separate sweep events). Suppose another edge C
|
|---|
| 1340 | * crosses A and B from above. When A is processed, we split it
|
|---|
| 1341 | * at its intersection point with C. However this also splits C,
|
|---|
| 1342 | * so when we insert B we may compute a slightly different
|
|---|
| 1343 | * intersection point. This might leave two edges with a small
|
|---|
| 1344 | * gap between them. This kind of error is especially obvious
|
|---|
| 1345 | * when using boundary extraction (GLU_TESS_BOUNDARY_ONLY).
|
|---|
| 1346 | */
|
|---|
| 1347 | vNext = (GLUvertex *)pqExtractMin( tess->pq ); /* __gl_pqSortExtractMin*/
|
|---|
| 1348 | SpliceMergeVertices( tess, v->anEdge, vNext->anEdge );
|
|---|
| 1349 | }
|
|---|
| 1350 | SweepEvent( tess, v );
|
|---|
| 1351 | }
|
|---|
| 1352 |
|
|---|
| 1353 | /* Set tess->event for debugging purposes */
|
|---|
| 1354 | /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */
|
|---|
| 1355 | tess->event = ((ActiveRegion *) dictKey( dictMin( tess->dict )))->eUp->Org;
|
|---|
| 1356 | DebugEvent( tess );
|
|---|
| 1357 | DoneEdgeDict( tess );
|
|---|
| 1358 | DonePriorityQ( tess );
|
|---|
| 1359 |
|
|---|
| 1360 | if ( !RemoveDegenerateFaces( tess->mesh ) ) return 0;
|
|---|
| 1361 | __gl_meshCheckMesh( tess->mesh );
|
|---|
| 1362 |
|
|---|
| 1363 | return 1;
|
|---|
| 1364 | }
|
|---|