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 | }
|
---|