📄 sweep.c
字号:
GLUhalfEdge *eFirst, GLUhalfEdge *eLast, GLUhalfEdge *eTopLeft,
GLboolean cleanUp )
/*
* Purpose: insert right-going edges into the edge dictionary, and update
* winding numbers and mesh connectivity appropriately. All right-going
* edges share a common origin vOrg. Edges are inserted CCW starting at
* eFirst; the last edge inserted is eLast->Oprev. If vOrg has any
* left-going edges already processed, then eTopLeft must be the edge
* such that an imaginary upward vertical segment from vOrg would be
* contained between eTopLeft->Oprev and eTopLeft; otherwise eTopLeft
* should be NULL.
*/
{
ActiveRegion *reg, *regPrev;
GLUhalfEdge *e, *ePrev;
int firstTime = TRUE;
/* Insert the new right-going edges in the dictionary */
e = eFirst;
do {
assert( VertLeq( e->Org, e->Dst ));
AddRegionBelow( tess, regUp, e->Sym );
e = e->Onext;
} while ( e != eLast );
/* Walk *all* right-going edges from e->Org, in the dictionary order,
* updating the winding numbers of each region, and re-linking the mesh
* edges to match the dictionary ordering (if necessary).
*/
if( eTopLeft == NULL ) {
eTopLeft = RegionBelow( regUp )->eUp->Rprev;
}
regPrev = regUp;
ePrev = eTopLeft;
for( ;; ) {
reg = RegionBelow( regPrev );
e = reg->eUp->Sym;
if( e->Org != ePrev->Org ) break;
if( e->Onext != ePrev ) {
/* Unlink e from its current position, and relink below ePrev */
if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1);
if ( !__gl_meshSplice( ePrev->Oprev, e ) ) longjmp(tess->env,1);
}
/* Compute the winding number and "inside" flag for the new regions */
reg->windingNumber = regPrev->windingNumber - e->winding;
reg->inside = IsWindingInside( tess, reg->windingNumber );
/* Check for two outgoing edges with same slope -- process these
* before any intersection tests (see example in __gl_computeInterior).
*/
regPrev->dirty = TRUE;
if( ! firstTime && CheckForRightSplice( tess, regPrev )) {
AddWinding( e, ePrev );
DeleteRegion( tess, regPrev );
if ( !__gl_meshDelete( ePrev ) ) longjmp(tess->env,1);
}
firstTime = FALSE;
regPrev = reg;
ePrev = e;
}
regPrev->dirty = TRUE;
assert( regPrev->windingNumber - e->winding == reg->windingNumber );
if( cleanUp ) {
/* Check for intersections between newly adjacent edges. */
WalkDirtyRegions( tess, regPrev );
}
}
static void CallCombine( GLUtesselator *tess, GLUvertex *isect,
void *data[4], GLfloat weights[4], int needed )
{
GLdouble coords[3];
/* Copy coord data in case the callback changes it. */
coords[0] = isect->coords[0];
coords[1] = isect->coords[1];
coords[2] = isect->coords[2];
isect->data = NULL;
CALL_COMBINE_OR_COMBINE_DATA( coords, data, weights, &isect->data );
if( isect->data == NULL ) {
if( ! needed ) {
isect->data = data[0];
} else if( ! tess->fatalError ) {
/* The only way fatal error is when two edges are found to intersect,
* but the user has not provided the callback necessary to handle
* generated intersection points.
*/
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_NEED_COMBINE_CALLBACK );
tess->fatalError = TRUE;
}
}
}
static void SpliceMergeVertices( GLUtesselator *tess, GLUhalfEdge *e1,
GLUhalfEdge *e2 )
/*
* Two vertices with idential coordinates are combined into one.
* e1->Org is kept, while e2->Org is discarded.
*/
{
void *data[4] = { NULL, NULL, NULL, NULL };
GLfloat weights[4] = { 0.5, 0.5, 0.0, 0.0 };
data[0] = e1->Org->data;
data[1] = e2->Org->data;
CallCombine( tess, e1->Org, data, weights, FALSE );
if ( !__gl_meshSplice( e1, e2 ) ) longjmp(tess->env,1);
}
static void VertexWeights( GLUvertex *isect, GLUvertex *org, GLUvertex *dst,
GLfloat *weights )
/*
* Find some weights which describe how the intersection vertex is
* a linear combination of "org" and "dest". Each of the two edges
* which generated "isect" is allocated 50% of the weight; each edge
* splits the weight between its org and dst according to the
* relative distance to "isect".
*/
{
GLdouble t1 = VertL1dist( org, isect );
GLdouble t2 = VertL1dist( dst, isect );
weights[0] = 0.5 * t2 / (t1 + t2);
weights[1] = 0.5 * t1 / (t1 + t2);
isect->coords[0] += weights[0]*org->coords[0] + weights[1]*dst->coords[0];
isect->coords[1] += weights[0]*org->coords[1] + weights[1]*dst->coords[1];
isect->coords[2] += weights[0]*org->coords[2] + weights[1]*dst->coords[2];
}
static void GetIntersectData( GLUtesselator *tess, GLUvertex *isect,
GLUvertex *orgUp, GLUvertex *dstUp,
GLUvertex *orgLo, GLUvertex *dstLo )
/*
* We've computed a new intersection point, now we need a "data" pointer
* from the user so that we can refer to this new vertex in the
* rendering callbacks.
*/
{
void *data[4];
GLfloat weights[4];
data[0] = orgUp->data;
data[1] = dstUp->data;
data[2] = orgLo->data;
data[3] = dstLo->data;
isect->coords[0] = isect->coords[1] = isect->coords[2] = 0;
VertexWeights( isect, orgUp, dstUp, &weights[0] );
VertexWeights( isect, orgLo, dstLo, &weights[2] );
CallCombine( tess, isect, data, weights, TRUE );
}
static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp )
/*
* Check the upper and lower edge of "regUp", to make sure that the
* eUp->Org is above eLo, or eLo->Org is below eUp (depending on which
* origin is leftmost).
*
* The main purpose is to splice right-going edges with the same
* dest vertex and nearly identical slopes (ie. we can't distinguish
* the slopes numerically). However the splicing can also help us
* to recover from numerical errors. For example, suppose at one
* point we checked eUp and eLo, and decided that eUp->Org is barely
* above eLo. Then later, we split eLo into two edges (eg. from
* a splice operation like this one). This can change the result of
* our test so that now eUp->Org is incident to eLo, or barely below it.
* We must correct this condition to maintain the dictionary invariants.
*
* One possibility is to check these edges for intersection again
* (ie. CheckForIntersect). This is what we do if possible. However
* CheckForIntersect requires that tess->event lies between eUp and eLo,
* so that it has something to fall back on when the intersection
* calculation gives us an unusable answer. So, for those cases where
* we can't check for intersection, this routine fixes the problem
* by just splicing the offending vertex into the other edge.
* This is a guaranteed solution, no matter how degenerate things get.
* Basically this is a combinatorial solution to a numerical problem.
*/
{
ActiveRegion *regLo = RegionBelow(regUp);
GLUhalfEdge *eUp = regUp->eUp;
GLUhalfEdge *eLo = regLo->eUp;
if( VertLeq( eUp->Org, eLo->Org )) {
if( EdgeSign( eLo->Dst, eUp->Org, eLo->Org ) > 0 ) return FALSE;
/* eUp->Org appears to be below eLo */
if( ! VertEq( eUp->Org, eLo->Org )) {
/* Splice eUp->Org into eLo */
if ( __gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1);
if ( !__gl_meshSplice( eUp, eLo->Oprev ) ) longjmp(tess->env,1);
regUp->dirty = regLo->dirty = TRUE;
} else if( eUp->Org != eLo->Org ) {
/* merge the two vertices, discarding eUp->Org */
pqDelete( tess->pq, eUp->Org->pqHandle ); /* __gl_pqSortDelete */
SpliceMergeVertices( tess, eLo->Oprev, eUp );
}
} else {
if( EdgeSign( eUp->Dst, eLo->Org, eUp->Org ) < 0 ) return FALSE;
/* eLo->Org appears to be above eUp, so splice eLo->Org into eUp */
RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1);
if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1);
}
return TRUE;
}
static int CheckForLeftSplice( GLUtesselator *tess, ActiveRegion *regUp )
/*
* Check the upper and lower edge of "regUp", to make sure that the
* eUp->Dst is above eLo, or eLo->Dst is below eUp (depending on which
* destination is rightmost).
*
* Theoretically, this should always be true. However, splitting an edge
* into two pieces can change the results of previous tests. For example,
* suppose at one point we checked eUp and eLo, and decided that eUp->Dst
* is barely above eLo. Then later, we split eLo into two edges (eg. from
* a splice operation like this one). This can change the result of
* the test so that now eUp->Dst is incident to eLo, or barely below it.
* We must correct this condition to maintain the dictionary invariants
* (otherwise new edges might get inserted in the wrong place in the
* dictionary, and bad stuff will happen).
*
* We fix the problem by just splicing the offending vertex into the
* other edge.
*/
{
ActiveRegion *regLo = RegionBelow(regUp);
GLUhalfEdge *eUp = regUp->eUp;
GLUhalfEdge *eLo = regLo->eUp;
GLUhalfEdge *e;
assert( ! VertEq( eUp->Dst, eLo->Dst ));
if( VertLeq( eUp->Dst, eLo->Dst )) {
if( EdgeSign( eUp->Dst, eLo->Dst, eUp->Org ) < 0 ) return FALSE;
/* eLo->Dst is above eUp, so splice eLo->Dst into eUp */
RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
e = __gl_meshSplitEdge( eUp );
if (e == NULL) longjmp(tess->env,1);
if ( !__gl_meshSplice( eLo->Sym, e ) ) longjmp(tess->env,1);
e->Lface->inside = regUp->inside;
} else {
if( EdgeSign( eLo->Dst, eUp->Dst, eLo->Org ) > 0 ) return FALSE;
/* eUp->Dst is below eLo, so splice eUp->Dst into eLo */
regUp->dirty = regLo->dirty = TRUE;
e = __gl_meshSplitEdge( eLo );
if (e == NULL) longjmp(tess->env,1);
if ( !__gl_meshSplice( eUp->Lnext, eLo->Sym ) ) longjmp(tess->env,1);
e->Rface->inside = regUp->inside;
}
return TRUE;
}
static int CheckForIntersect( GLUtesselator *tess, ActiveRegion *regUp )
/*
* Check the upper and lower edges of the given region to see if
* they intersect. If so, create the intersection and add it
* to the data structures.
*
* Returns TRUE if adding the new intersection resulted in a recursive
* call to AddRightEdges(); in this case all "dirty" regions have been
* checked for intersections, and possibly regUp has been deleted.
*/
{
ActiveRegion *regLo = RegionBelow(regUp);
GLUhalfEdge *eUp = regUp->eUp;
GLUhalfEdge *eLo = regLo->eUp;
GLUvertex *orgUp = eUp->Org;
GLUvertex *orgLo = eLo->Org;
GLUvertex *dstUp = eUp->Dst;
GLUvertex *dstLo = eLo->Dst;
GLdouble tMinUp, tMaxLo;
GLUvertex isect, *orgMin;
GLUhalfEdge *e;
assert( ! VertEq( dstLo, dstUp ));
assert( EdgeSign( dstUp, tess->event, orgUp ) <= 0 );
assert( EdgeSign( dstLo, tess->event, orgLo ) >= 0 );
assert( orgUp != tess->event && orgLo != tess->event );
assert( ! regUp->fixUpperEdge && ! regLo->fixUpperEdge );
if( orgUp == orgLo ) return FALSE; /* right endpoints are the same */
tMinUp = MIN( orgUp->t, dstUp->t );
tMaxLo = MAX( orgLo->t, dstLo->t );
if( tMinUp > tMaxLo ) return FALSE; /* t ranges do not overlap */
if( VertLeq( orgUp, orgLo )) {
if( EdgeSign( dstLo, orgUp, orgLo ) > 0 ) return FALSE;
} else {
if( EdgeSign( dstUp, orgLo, orgUp ) < 0 ) return FALSE;
}
/* At this point the edges intersect, at least marginally */
DebugEvent( tess );
__gl_edgeIntersect( dstUp, orgUp, dstLo, orgLo, &isect );
/* The following properties are guaranteed: */
assert( MIN( orgUp->t, dstUp->t ) <= isect.t );
assert( isect.t <= MAX( orgLo->t, dstLo->t ));
assert( MIN( dstLo->s, dstUp->s ) <= isect.s );
assert( isect.s <= MAX( orgLo->s, orgUp->s ));
if( VertLeq( &isect, tess->event )) {
/* The intersection point lies slightly to the left of the sweep line,
* so move it until it''s slightly to the right of the sweep line.
* (If we had perfect numerical precision, this would never happen
* in the first place). The easiest and safest thing to do is
* replace the intersection by tess->event.
*/
isect.s = tess->event->s;
isect.t = tess->event->t;
}
/* Similarly, if the computed intersection lies to the right of the
* rightmost origin (which should rarely happen), it can cause
* unbelievable inefficiency on sufficiently degenerate inputs.
* (If you have the test program, try running test54.d with the
* "X zoom" option turned on).
*/
orgMin = VertLeq( orgUp, orgLo ) ? orgUp : orgLo;
if( VertLeq( orgMin, &isect )) {
isect.s = orgMin->s;
isect.t = orgMin->t;
}
if( VertEq( &isect, orgUp ) || VertEq( &isect, orgLo )) {
/* Easy case -- intersection at one of the right endpoints */
(void) CheckForRightSplice( tess, regUp );
return FALSE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -