📄 sweep.c
字号:
tmp.eUp = vEvent->anEdge->Sym; /* __GL_DICTLISTKEY */ /* __gl_dictListSearch */ regUp = (ActiveRegion *)dictKey( dictSearch( tess->dict, &tmp )); regLo = RegionBelow( regUp ); eUp = regUp->eUp; eLo = regLo->eUp; /* Try merging with U or L first */ if( EdgeSign( eUp->Dst, vEvent, eUp->Org ) == 0 ) { ConnectLeftDegenerate( tess, regUp, vEvent ); return; } /* Connect vEvent to rightmost processed vertex of either chain. * e->Dst is the vertex that we will connect to vEvent. */ reg = VertLeq( eLo->Dst, eUp->Dst ) ? regUp : regLo; if( regUp->inside || reg->fixUpperEdge) { if( reg == regUp ) { eNew = __gl_meshConnect( vEvent->anEdge->Sym, eUp->Lnext ); if (eNew == NULL) longjmp(tess->env,1); } else { GLUhalfEdge *tempHalfEdge= __gl_meshConnect( eLo->Dnext, vEvent->anEdge); if (tempHalfEdge == NULL) longjmp(tess->env,1); eNew = tempHalfEdge->Sym; } if( reg->fixUpperEdge ) { if ( !FixUpperEdge( reg, eNew ) ) longjmp(tess->env,1); } else { ComputeWinding( tess, AddRegionBelow( tess, regUp, eNew )); } SweepEvent( tess, vEvent ); } else { /* The new vertex is in a region which does not belong to the polygon. * We don''t need to connect this vertex to the rest of the mesh. */ AddRightEdges( tess, regUp, vEvent->anEdge, vEvent->anEdge, NULL, TRUE ); }}static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent )/* * Does everything necessary when the sweep line crosses a vertex. * Updates the mesh and the edge dictionary. */{ ActiveRegion *regUp, *reg; GLUhalfEdge *e, *eTopLeft, *eBottomLeft; tess->event = vEvent; /* for access in EdgeLeq() */ DebugEvent( tess ); /* Check if this vertex is the right endpoint of an edge that is * already in the dictionary. In this case we don't need to waste * time searching for the location to insert new edges. */ e = vEvent->anEdge; while( e->activeRegion == NULL ) { e = e->Onext; if( e == vEvent->anEdge ) { /* All edges go right -- not incident to any processed edges */ ConnectLeftVertex( tess, vEvent ); return; } } /* Processing consists of two phases: first we "finish" all the * active regions where both the upper and lower edges terminate * at vEvent (ie. vEvent is closing off these regions). * We mark these faces "inside" or "outside" the polygon according * to their winding number, and delete the edges from the dictionary. * This takes care of all the left-going edges from vEvent. */ regUp = TopLeftRegion( e->activeRegion ); if (regUp == NULL) longjmp(tess->env,1); reg = RegionBelow( regUp ); eTopLeft = reg->eUp; eBottomLeft = FinishLeftRegions( tess, reg, NULL ); /* Next we process all the right-going edges from vEvent. This * involves adding the edges to the dictionary, and creating the * associated "active regions" which record information about the * regions between adjacent dictionary edges. */ if( eBottomLeft->Onext == eTopLeft ) { /* No right-going edges -- add a temporary "fixable" edge */ ConnectRightVertex( tess, regUp, eBottomLeft ); } else { AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE ); }}/* Make the sentinel coordinates big enough that they will never be * merged with real input features. (Even with the largest possible * input contour and the maximum tolerance of 1.0, no merging will be * done with coordinates larger than 3 * GLU_TESS_MAX_COORD). */#define SENTINEL_COORD (4 * GLU_TESS_MAX_COORD)static void AddSentinel( GLUtesselator *tess, GLdouble t )/* * We add two sentinel edges above and below all other edges, * to avoid special cases at the top and bottom. */{ GLUhalfEdge *e; ActiveRegion *reg = (ActiveRegion *)memAlloc( sizeof( ActiveRegion )); if (reg == NULL) longjmp(tess->env,1); e = __gl_meshMakeEdge( tess->mesh ); if (e == NULL) longjmp(tess->env,1); e->Org->s = SENTINEL_COORD; e->Org->t = t; e->Dst->s = -SENTINEL_COORD; e->Dst->t = t; tess->event = e->Dst; /* initialize it */ reg->eUp = e; reg->windingNumber = 0; reg->inside = FALSE; reg->fixUpperEdge = FALSE; reg->sentinel = TRUE; reg->dirty = FALSE; reg->nodeUp = dictInsert( tess->dict, reg ); /* __gl_dictListInsertBefore */ if (reg->nodeUp == NULL) longjmp(tess->env,1);}static void InitEdgeDict( GLUtesselator *tess )/* * We maintain an ordering of edge intersections with the sweep line. * This order is maintained in a dynamic dictionary. */{ /* __gl_dictListNewDict */ tess->dict = dictNewDict( tess, (int (*)(void *, DictKey, DictKey)) EdgeLeq ); if (tess->dict == NULL) longjmp(tess->env,1); AddSentinel( tess, -SENTINEL_COORD ); AddSentinel( tess, SENTINEL_COORD );}static void DoneEdgeDict( GLUtesselator *tess ){ ActiveRegion *reg;#ifndef NDEBUG int fixedEdges = 0;#endif /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ while( (reg = (ActiveRegion *)dictKey( dictMin( tess->dict ))) != NULL ) { /* * At the end of all processing, the dictionary should contain * only the two sentinel edges, plus at most one "fixable" edge * created by ConnectRightVertex(). */ if( ! reg->sentinel ) { assert( reg->fixUpperEdge ); assert( ++fixedEdges == 1 ); } assert( reg->windingNumber == 0 ); DeleteRegion( tess, reg );/* __gl_meshDelete( reg->eUp );*/ } dictDeleteDict( tess->dict ); /* __gl_dictListDeleteDict */}static void RemoveDegenerateEdges( GLUtesselator *tess )/* * Remove zero-length edges, and contours with fewer than 3 vertices. */{ GLUhalfEdge *e, *eNext, *eLnext; GLUhalfEdge *eHead = &tess->mesh->eHead; /*LINTED*/ for( e = eHead->next; e != eHead; e = eNext ) { eNext = e->next; eLnext = e->Lnext; if( VertEq( e->Org, e->Dst ) && e->Lnext->Lnext != e ) { /* Zero-length edge, contour has at least 3 edges */ SpliceMergeVertices( tess, eLnext, e ); /* deletes e->Org */ if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1); /* e is a self-loop */ e = eLnext; eLnext = e->Lnext; } if( eLnext->Lnext == e ) { /* Degenerate contour (one or two edges) */ if( eLnext != e ) { if( eLnext == eNext || eLnext == eNext->Sym ) { eNext = eNext->next; } if ( !__gl_meshDelete( eLnext ) ) longjmp(tess->env,1); } if( e == eNext || e == eNext->Sym ) { eNext = eNext->next; } if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1); } }}static int InitPriorityQ( GLUtesselator *tess )/* * Insert all vertices into the priority queue which determines the * order in which vertices cross the sweep line. */{ PriorityQ *pq; GLUvertex *v, *vHead; /* __gl_pqSortNewPriorityQ */ pq = tess->pq = pqNewPriorityQ( (int (*)(PQkey, PQkey)) __gl_vertLeq ); if (pq == NULL) return 0; vHead = &tess->mesh->vHead; for( v = vHead->next; v != vHead; v = v->next ) { v->pqHandle = pqInsert( pq, v ); /* __gl_pqSortInsert */ if (v->pqHandle == LONG_MAX) break; } if (v != vHead || !pqInit( pq ) ) { /* __gl_pqSortInit */ pqDeletePriorityQ(tess->pq); /* __gl_pqSortDeletePriorityQ */ tess->pq = NULL; return 0; } return 1;}static void DonePriorityQ( GLUtesselator *tess ){ pqDeletePriorityQ( tess->pq ); /* __gl_pqSortDeletePriorityQ */}static int RemoveDegenerateFaces( GLUmesh *mesh )/* * Delete any degenerate faces with only two edges. WalkDirtyRegions() * will catch almost all of these, but it won't catch degenerate faces * produced by splice operations on already-processed edges. * The two places this can happen are in FinishLeftRegions(), when * we splice in a "temporary" edge produced by ConnectRightVertex(), * and in CheckForLeftSplice(), where we splice already-processed * edges to ensure that our dictionary invariants are not violated * by numerical errors. * * In both these cases it is *very* dangerous to delete the offending * edge at the time, since one of the routines further up the stack * will sometimes be keeping a pointer to that edge. */{ GLUface *f, *fNext; GLUhalfEdge *e; /*LINTED*/ for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) { fNext = f->next; e = f->anEdge; assert( e->Lnext != e ); if( e->Lnext->Lnext == e ) { /* A face with only two edges */ AddWinding( e->Onext, e ); if ( !__gl_meshDelete( e ) ) return 0; } } return 1;}int __gl_computeInterior( GLUtesselator *tess )/* * __gl_computeInterior( tess ) computes the planar arrangement specified * by the given contours, and further subdivides this arrangement * into regions. Each region is marked "inside" if it belongs * to the polygon, according to the rule given by tess->windingRule. * Each interior region is guaranteed be monotone. */{ GLUvertex *v, *vNext; tess->fatalError = FALSE; /* Each vertex defines an event for our sweep line. Start by inserting * all the vertices in a priority queue. Events are processed in * lexicographic order, ie. * * e1 < e2 iff e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y) */ RemoveDegenerateEdges( tess ); if ( !InitPriorityQ( tess ) ) return 0; /* if error */ InitEdgeDict( tess ); /* __gl_pqSortExtractMin */ while( (v = (GLUvertex *)pqExtractMin( tess->pq )) != NULL ) { for( ;; ) { vNext = (GLUvertex *)pqMinimum( tess->pq ); /* __gl_pqSortMinimum */ if( vNext == NULL || ! VertEq( vNext, v )) break; /* Merge together all vertices at exactly the same location. * This is more efficient than processing them one at a time, * simplifies the code (see ConnectLeftDegenerate), and is also * important for correct handling of certain degenerate cases. * For example, suppose there are two identical edges A and B * that belong to different contours (so without this code they would * be processed by separate sweep events). Suppose another edge C * crosses A and B from above. When A is processed, we split it * at its intersection point with C. However this also splits C, * so when we insert B we may compute a slightly different * intersection point. This might leave two edges with a small * gap between them. This kind of error is especially obvious * when using boundary extraction (GLU_TESS_BOUNDARY_ONLY). */ vNext = (GLUvertex *)pqExtractMin( tess->pq ); /* __gl_pqSortExtractMin*/ SpliceMergeVertices( tess, v->anEdge, vNext->anEdge ); } SweepEvent( tess, v ); } /* Set tess->event for debugging purposes */ /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ tess->event = ((ActiveRegion *) dictKey( dictMin( tess->dict )))->eUp->Org; DebugEvent( tess ); DoneEdgeDict( tess ); DonePriorityQ( tess ); if ( !RemoveDegenerateFaces( tess->mesh ) ) return 0; __gl_meshCheckMesh( tess->mesh ); return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -