📄 sweep.c
字号:
}
if( (! VertEq( dstUp, tess->event )
&& EdgeSign( dstUp, tess->event, &isect ) >= 0)
|| (! VertEq( dstLo, tess->event )
&& EdgeSign( dstLo, tess->event, &isect ) <= 0 ))
{
/* Very unusual -- the new upper or lower edge would pass on the
* wrong side of the sweep event, or through it. This can happen
* due to very small numerical errors in the intersection calculation.
*/
if( dstLo == tess->event ) {
/* Splice dstLo into eUp, and process the new region(s) */
if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1);
if ( !__gl_meshSplice( eLo->Sym, eUp ) ) longjmp(tess->env,1);
regUp = TopLeftRegion( regUp );
if (regUp == NULL) longjmp(tess->env,1);
eUp = RegionBelow(regUp)->eUp;
FinishLeftRegions( tess, RegionBelow(regUp), regLo );
AddRightEdges( tess, regUp, eUp->Oprev, eUp, eUp, TRUE );
return TRUE;
}
if( dstUp == tess->event ) {
/* Splice dstUp into eLo, and process the new region(s) */
if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1);
if ( !__gl_meshSplice( eUp->Lnext, eLo->Oprev ) ) longjmp(tess->env,1);
regLo = regUp;
regUp = TopRightRegion( regUp );
e = RegionBelow(regUp)->eUp->Rprev;
regLo->eUp = eLo->Oprev;
eLo = FinishLeftRegions( tess, regLo, NULL );
AddRightEdges( tess, regUp, eLo->Onext, eUp->Rprev, e, TRUE );
return TRUE;
}
/* Special case: called from ConnectRightVertex. If either
* edge passes on the wrong side of tess->event, split it
* (and wait for ConnectRightVertex to splice it appropriately).
*/
if( EdgeSign( dstUp, tess->event, &isect ) >= 0 ) {
RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1);
eUp->Org->s = tess->event->s;
eUp->Org->t = tess->event->t;
}
if( EdgeSign( dstLo, tess->event, &isect ) <= 0 ) {
regUp->dirty = regLo->dirty = TRUE;
if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1);
eLo->Org->s = tess->event->s;
eLo->Org->t = tess->event->t;
}
/* leave the rest for ConnectRightVertex */
return FALSE;
}
/* General case -- split both edges, splice into new vertex.
* When we do the splice operation, the order of the arguments is
* arbitrary as far as correctness goes. However, when the operation
* creates a new face, the work done is proportional to the size of
* the new face. We expect the faces in the processed part of
* the mesh (ie. eUp->Lface) to be smaller than the faces in the
* unprocessed original contours (which will be eLo->Oprev->Lface).
*/
if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1);
if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1);
if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1);
eUp->Org->s = isect.s;
eUp->Org->t = isect.t;
eUp->Org->pqHandle = pqInsert( tess->pq, eUp->Org ); /* __gl_pqSortInsert */
if (eUp->Org->pqHandle == LONG_MAX) {
pqDeletePriorityQ(tess->pq); /* __gl_pqSortDeletePriorityQ */
tess->pq = NULL;
longjmp(tess->env,1);
}
GetIntersectData( tess, eUp->Org, orgUp, dstUp, orgLo, dstLo );
RegionAbove(regUp)->dirty = regUp->dirty = regLo->dirty = TRUE;
return FALSE;
}
static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp )
/*
* When the upper or lower edge of any region changes, the region is
* marked "dirty". This routine walks through all the dirty regions
* and makes sure that the dictionary invariants are satisfied
* (see the comments at the beginning of this file). Of course
* new dirty regions can be created as we make changes to restore
* the invariants.
*/
{
ActiveRegion *regLo = RegionBelow(regUp);
GLUhalfEdge *eUp, *eLo;
for( ;; ) {
/* Find the lowest dirty region (we walk from the bottom up). */
while( regLo->dirty ) {
regUp = regLo;
regLo = RegionBelow(regLo);
}
if( ! regUp->dirty ) {
regLo = regUp;
regUp = RegionAbove( regUp );
if( regUp == NULL || ! regUp->dirty ) {
/* We've walked all the dirty regions */
return;
}
}
regUp->dirty = FALSE;
eUp = regUp->eUp;
eLo = regLo->eUp;
if( eUp->Dst != eLo->Dst ) {
/* Check that the edge ordering is obeyed at the Dst vertices. */
if( CheckForLeftSplice( tess, regUp )) {
/* If the upper or lower edge was marked fixUpperEdge, then
* we no longer need it (since these edges are needed only for
* vertices which otherwise have no right-going edges).
*/
if( regLo->fixUpperEdge ) {
DeleteRegion( tess, regLo );
if ( !__gl_meshDelete( eLo ) ) longjmp(tess->env,1);
regLo = RegionBelow( regUp );
eLo = regLo->eUp;
} else if( regUp->fixUpperEdge ) {
DeleteRegion( tess, regUp );
if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1);
regUp = RegionAbove( regLo );
eUp = regUp->eUp;
}
}
}
if( eUp->Org != eLo->Org ) {
if( eUp->Dst != eLo->Dst
&& ! regUp->fixUpperEdge && ! regLo->fixUpperEdge
&& (eUp->Dst == tess->event || eLo->Dst == tess->event) )
{
/* When all else fails in CheckForIntersect(), it uses tess->event
* as the intersection location. To make this possible, it requires
* that tess->event lie between the upper and lower edges, and also
* that neither of these is marked fixUpperEdge (since in the worst
* case it might splice one of these edges into tess->event, and
* violate the invariant that fixable edges are the only right-going
* edge from their associated vertex).
*/
if( CheckForIntersect( tess, regUp )) {
/* WalkDirtyRegions() was called recursively; we're done */
return;
}
} else {
/* Even though we can't use CheckForIntersect(), the Org vertices
* may violate the dictionary edge ordering. Check and correct this.
*/
(void) CheckForRightSplice( tess, regUp );
}
}
if( eUp->Org == eLo->Org && eUp->Dst == eLo->Dst ) {
/* A degenerate loop consisting of only two edges -- delete it. */
AddWinding( eLo, eUp );
DeleteRegion( tess, regUp );
if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1);
regUp = RegionAbove( regLo );
}
}
}
static void ConnectRightVertex( GLUtesselator *tess, ActiveRegion *regUp,
GLUhalfEdge *eBottomLeft )
/*
* Purpose: connect a "right" vertex vEvent (one where all edges go left)
* to the unprocessed portion of the mesh. Since there are no right-going
* edges, two regions (one above vEvent and one below) are being merged
* into one. "regUp" is the upper of these two regions.
*
* There are two reasons for doing this (adding a right-going edge):
* - if the two regions being merged are "inside", we must add an edge
* to keep them separated (the combined region would not be monotone).
* - in any case, we must leave some record of vEvent in the dictionary,
* so that we can merge vEvent with features that we have not seen yet.
* For example, maybe there is a vertical edge which passes just to
* the right of vEvent; we would like to splice vEvent into this edge.
*
* However, we don't want to connect vEvent to just any vertex. We don''t
* want the new edge to cross any other edges; otherwise we will create
* intersection vertices even when the input data had no self-intersections.
* (This is a bad thing; if the user's input data has no intersections,
* we don't want to generate any false intersections ourselves.)
*
* Our eventual goal is to connect vEvent to the leftmost unprocessed
* vertex of the combined region (the union of regUp and regLo).
* But because of unseen vertices with all right-going edges, and also
* new vertices which may be created by edge intersections, we don''t
* know where that leftmost unprocessed vertex is. In the meantime, we
* connect vEvent to the closest vertex of either chain, and mark the region
* as "fixUpperEdge". This flag says to delete and reconnect this edge
* to the next processed vertex on the boundary of the combined region.
* Quite possibly the vertex we connected to will turn out to be the
* closest one, in which case we won''t need to make any changes.
*/
{
GLUhalfEdge *eNew;
GLUhalfEdge *eTopLeft = eBottomLeft->Onext;
ActiveRegion *regLo = RegionBelow(regUp);
GLUhalfEdge *eUp = regUp->eUp;
GLUhalfEdge *eLo = regLo->eUp;
int degenerate = FALSE;
if( eUp->Dst != eLo->Dst ) {
(void) CheckForIntersect( tess, regUp );
}
/* Possible new degeneracies: upper or lower edge of regUp may pass
* through vEvent, or may coincide with new intersection vertex
*/
if( VertEq( eUp->Org, tess->event )) {
if ( !__gl_meshSplice( eTopLeft->Oprev, eUp ) ) longjmp(tess->env,1);
regUp = TopLeftRegion( regUp );
if (regUp == NULL) longjmp(tess->env,1);
eTopLeft = RegionBelow( regUp )->eUp;
FinishLeftRegions( tess, RegionBelow(regUp), regLo );
degenerate = TRUE;
}
if( VertEq( eLo->Org, tess->event )) {
if ( !__gl_meshSplice( eBottomLeft, eLo->Oprev ) ) longjmp(tess->env,1);
eBottomLeft = FinishLeftRegions( tess, regLo, NULL );
degenerate = TRUE;
}
if( degenerate ) {
AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE );
return;
}
/* Non-degenerate situation -- need to add a temporary, fixable edge.
* Connect to the closer of eLo->Org, eUp->Org.
*/
if( VertLeq( eLo->Org, eUp->Org )) {
eNew = eLo->Oprev;
} else {
eNew = eUp;
}
eNew = __gl_meshConnect( eBottomLeft->Lprev, eNew );
if (eNew == NULL) longjmp(tess->env,1);
/* Prevent cleanup, otherwise eNew might disappear before we've even
* had a chance to mark it as a temporary edge.
*/
AddRightEdges( tess, regUp, eNew, eNew->Onext, eNew->Onext, FALSE );
eNew->Sym->activeRegion->fixUpperEdge = TRUE;
WalkDirtyRegions( tess, regUp );
}
/* Because vertices at exactly the same location are merged together
* before we process the sweep event, some degenerate cases can't occur.
* However if someone eventually makes the modifications required to
* merge features which are close together, the cases below marked
* TOLERANCE_NONZERO will be useful. They were debugged before the
* code to merge identical vertices in the main loop was added.
*/
#define TOLERANCE_NONZERO FALSE
static void ConnectLeftDegenerate( GLUtesselator *tess,
ActiveRegion *regUp, GLUvertex *vEvent )
/*
* The event vertex lies exacty on an already-processed edge or vertex.
* Adding the new vertex involves splicing it into the already-processed
* part of the mesh.
*/
{
GLUhalfEdge *e, *eTopLeft, *eTopRight, *eLast;
ActiveRegion *reg;
e = regUp->eUp;
if( VertEq( e->Org, vEvent )) {
/* e->Org is an unprocessed vertex - just combine them, and wait
* for e->Org to be pulled from the queue
*/
assert( TOLERANCE_NONZERO );
SpliceMergeVertices( tess, e, vEvent->anEdge );
return;
}
if( ! VertEq( e->Dst, vEvent )) {
/* General case -- splice vEvent into edge e which passes through it */
if (__gl_meshSplitEdge( e->Sym ) == NULL) longjmp(tess->env,1);
if( regUp->fixUpperEdge ) {
/* This edge was fixable -- delete unused portion of original edge */
if ( !__gl_meshDelete( e->Onext ) ) longjmp(tess->env,1);
regUp->fixUpperEdge = FALSE;
}
if ( !__gl_meshSplice( vEvent->anEdge, e ) ) longjmp(tess->env,1);
SweepEvent( tess, vEvent ); /* recurse */
return;
}
/* vEvent coincides with e->Dst, which has already been processed.
* Splice in the additional right-going edges.
*/
assert( TOLERANCE_NONZERO );
regUp = TopRightRegion( regUp );
reg = RegionBelow( regUp );
eTopRight = reg->eUp->Sym;
eTopLeft = eLast = eTopRight->Onext;
if( reg->fixUpperEdge ) {
/* Here e->Dst has only a single fixable edge going right.
* We can delete it since now we have some real right-going edges.
*/
assert( eTopLeft != eTopRight ); /* there are some left edges too */
DeleteRegion( tess, reg );
if ( !__gl_meshDelete( eTopRight ) ) longjmp(tess->env,1);
eTopRight = eTopLeft->Oprev;
}
if ( !__gl_meshSplice( vEvent->anEdge, eTopRight ) ) longjmp(tess->env,1);
if( ! EdgeGoesLeft( eTopLeft )) {
/* e->Dst had no left-going edges -- indicate this to AddRightEdges() */
eTopLeft = NULL;
}
AddRightEdges( tess, regUp, eTopRight->Onext, eLast, eTopLeft, TRUE );
}
static void ConnectLeftVertex( GLUtesselator *tess, GLUvertex *vEvent )
/*
* Purpose: connect a "left" vertex (one where both edges go right)
* to the processed portion of the mesh. Let R be the active region
* containing vEvent, and let U and L be the upper and lower edge
* chains of R. There are two possibilities:
*
* - the normal case: split R into two regions, by connecting vEvent to
* the rightmost vertex of U or L lying to the left of the sweep line
*
* - the degenerate case: if vEvent is close enough to U or L, we
* merge vEvent into that edge chain. The subcases are:
* - merging with the rightmost vertex of U or L
* - merging with the active edge of U or L
* - merging with an already-processed portion of U or L
*/
{
ActiveRegion *regUp, *regLo, *reg;
GLUhalfEdge *eUp, *eLo, *eNew;
ActiveRegion tmp;
/* assert( vEvent->anEdge->Onext->Onext == vEvent->anEdge ); */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -