/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region * (what else would it do??) The region must consist of a single * loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this * case means that any vertical line intersects the interior of the * region in a single interval. * * Tessellation consists of adding interior edges (actually pairs of * half-edges), to split the region into non-overlapping triangles. * * The basic idea is explained in Preparata and Shamos (which I don''t * have handy right now), although their implementation is more * complicated than this one. The are two edge chains, an upper chain * and a lower chain. We process all vertices from both chains in order, * from right to left. * * The algorithm ensures that the following invariant holds after each * vertex is processed: the untessellated region consists of two * chains, where one chain (say the upper) is a single edge, and * the other chain is concave. The left vertex of the single edge * is always to the left of all vertices in the concave chain. * * Each step consists of adding the rightmost unprocessed vertex to one * of the two chains, and forming a fan of triangles from the rightmost * of two chain endpoints. Determining whether we can add each triangle * to the fan is a simple orientation test. By making the fan as large * as possible, we restore the invariant (check it yourself). */ int __gl_meshTessellateMonoRegion( GLUface *face ) { GLUhalfEdge *up, *lo; /* All edges are oriented CCW around the boundary of the region. * First, find the half-edge whose origin vertex is rightmost. * Since the sweep goes from left to right, face->anEdge should * be close to the edge we want. */ up = face->anEdge; assert( up->Lnext != up && up->Lnext->Lnext != up ); for( ; VertLeq( up->Dst, up->Org ); up = up->Lprev ) ; for( ; VertLeq( up->Org, up->Dst ); up = up->Lnext ) ; lo = up->Lprev; while( up->Lnext != lo ) { if( VertLeq( up->Dst, lo->Org )) { /* up->Dst is on the left. It is safe to form triangles from lo->Org. * The EdgeGoesLeft test guarantees progress even when some triangles * are CW, given that the upper and lower chains are truly monotone. */ while( lo->Lnext != up && (EdgeGoesLeft( lo->Lnext ) || EdgeSign( lo->Org, lo->Dst, lo->Lnext->Dst ) <= 0 )) { GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo ); if (tempHalfEdge == NULL) return 0; lo = tempHalfEdge->Sym; } lo = lo->Lprev; } else { /* lo->Org is on the left. We can make CCW triangles from up->Dst. */ while( lo->Lnext != up && (EdgeGoesRight( up->Lprev ) || EdgeSign( up->Dst, up->Org, up->Lprev->Org ) >= 0 )) { GLUhalfEdge *tempHalfEdge= __gl_meshConnect( up, up->Lprev ); if (tempHalfEdge == NULL) return 0; up = tempHalfEdge->Sym; } up = up->Lnext; } } /* Now lo->Org == up->Dst == the leftmost vertex. The remaining region * can be tessellated in a fan from this leftmost vertex. */ assert( lo->Lnext != up ); while( lo->Lnext->Lnext != up ) { GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo ); if (tempHalfEdge == NULL) return 0; lo = tempHalfEdge->Sym; } return 1; }
static GLUhalfEdge *FinishLeftRegions( GLUtesselator *tess, ActiveRegion *regFirst, ActiveRegion *regLast ) /* * We are given a vertex with one or more left-going edges. All affected * edges should be in the edge dictionary. Starting at regFirst->eUp, * we walk down deleting all regions where both edges have the same * origin vOrg. At the same time we copy the "inside" flag from the * active region to the face, since at this point each face will belong * to at most one region (this was not necessarily true until this point * in the sweep). The walk stops at the region above regLast; if regLast * is NULL we walk as far as possible. At the same time we relink the * mesh if necessary, so that the ordering of edges around vOrg is the * same as in the dictionary. */ { ActiveRegion *reg, *regPrev; GLUhalfEdge *e, *ePrev; regPrev = regFirst; ePrev = regFirst->eUp; while( regPrev != regLast ) { regPrev->fixUpperEdge = FALSE; /* placement was OK */ reg = RegionBelow( regPrev ); e = reg->eUp; if( e->Org != ePrev->Org ) { if( ! reg->fixUpperEdge ) { /* Remove the last left-going edge. Even though there are no further * edges in the dictionary with this origin, there may be further * such edges in the mesh (if we are adding left edges to a vertex * that has already been processed). Thus it is important to call * FinishRegion rather than just DeleteRegion. */ FinishRegion( tess, regPrev ); break; } /* If the edge below was a temporary edge introduced by * ConnectRightVertex, now is the time to fix it. */ e = __gl_meshConnect( ePrev->Lprev, e->Sym ); if (e == NULL) longjmp(tess->env,1); if ( !FixUpperEdge( reg, e ) ) longjmp(tess->env,1); } /* Relink edges so that ePrev->Onext == e */ if( ePrev->Onext != e ) { if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1); if ( !__gl_meshSplice( ePrev, e ) ) longjmp(tess->env,1); } FinishRegion( tess, regPrev ); /* may change reg->eUp */ ePrev = reg->eUp; regPrev = reg; } return ePrev; }
static ActiveRegion *TopLeftRegion( ActiveRegion *reg ) { GLUvertex *org = reg->eUp->Org; GLUhalfEdge *e; /* Find the region above the uppermost edge with the same origin */ do { reg = RegionAbove( reg ); } while( reg->eUp->Org == org ); /* If the edge above was a temporary edge introduced by ConnectRightVertex, * now is the time to fix it. */ if( reg->fixUpperEdge ) { e = __gl_meshConnect( RegionBelow(reg)->eUp->Sym, reg->eUp->Lnext ); if (e == NULL) return NULL; if ( !FixUpperEdge( reg, e ) ) return NULL; reg = RegionAbove( reg ); } return reg; }
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 ); }
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 ); */ /* 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 )); // micah fix: (seem to happen to happen on a lot of detail or small areas. possibly use doubles instead of floats if( regUp == 0 ) { //printf( "f**k\n"); return; } 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 ); } }