📄 sweep.c
字号:
/* Get a pointer to the active region containing vEvent */
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 + -