📄 sweep.c
字号:
* 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; } if( (! VertEq( dstUp, tess->event )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -