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 */ regUp->dirty = TRUE; void* valid_ptr_check = RegionAbove(regUp);//->dirty if ( valid_ptr_check ) { RegionAbove(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; }
/* tessMeshTessellateMonoRegion( 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 tessMeshTessellateMonoRegion( TESSmesh *mesh, TESSface *face ) { TESShalfEdge *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; if(!( up->Lnext != up && up->Lnext->Lnext != up )) return 1; 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 )) { TESShalfEdge *tempHalfEdge= tessMeshConnect( mesh, 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 )) { TESShalfEdge *tempHalfEdge= tessMeshConnect( mesh, 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. */ if( lo->Lnext == up ) return 1; while( lo->Lnext->Lnext != up ) { TESShalfEdge *tempHalfEdge= tessMeshConnect( mesh, lo->Lnext, lo ); if (tempHalfEdge == NULL) return 0; lo = tempHalfEdge->Sym; } return 1; }
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 */ if ( RegionAbove(regUp) ) { RegionAbove(regUp)->dirty = TRUE; } 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 EdgeLeq( GLUtesselator *tess, ActiveRegion *reg1, ActiveRegion *reg2 ) /* * Both edges must be directed from right to left (this is the canonical * direction for the upper edge of each region). * * The strategy is to evaluate a "t" value for each edge at the * current sweep line position, given by tess->event. The calculations * are designed to be very stable, but of course they are not perfect. * * Special case: if both edge destinations are at the sweep event, * we sort the edges by slope (they would otherwise compare equally). */ { GLUvertex *event = tess->event; GLUhalfEdge *e1, *e2; GLdouble t1, t2; e1 = reg1->eUp; e2 = reg2->eUp; if( e1->Dst == event ) { if( e2->Dst == event ) { /* Two edges right of the sweep line which meet at the sweep event. * Sort them by slope. */ if( VertLeq( e1->Org, e2->Org )) { return EdgeSign( e2->Dst, e1->Org, e2->Org ) <= 0; } return EdgeSign( e1->Dst, e2->Org, e1->Org ) >= 0; } return EdgeSign( e2->Dst, event, e2->Org ) <= 0; } if( e2->Dst == event ) { return EdgeSign( e1->Dst, event, e1->Org ) >= 0; } /* General case - compute signed distance *from* e1, e2 to event */ t1 = EdgeEval( e1->Dst, event, e1->Org ); t2 = EdgeEval( e2->Dst, event, e2->Org ); return (t1 >= t2); }
void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1, GLUvertex *o2, GLUvertex *d2, GLUvertex *v ) /* Given edges (o1,d1) and (o2,d2), compute their point of intersection. * The computed point is guaranteed to lie in the intersection of the * bounding rectangles defined by each edge. */ { GLdouble z1, z2; /* This is certainly not the most efficient way to find the intersection * of two line segments, but it is very numerically stable. * * Strategy: find the two middle vertices in the VertLeq ordering, * and interpolate the intersection s-value from these. Then repeat * using the TransLeq ordering to find the intersection t-value. */ if( ! VertLeq( o1, d1 )) { Swap( o1, d1 ); } if( ! VertLeq( o2, d2 )) { Swap( o2, d2 ); } if( ! VertLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); } if( ! VertLeq( o2, d1 )) { /* Technically, no intersection -- do our best */ v->s = (o2->s + d1->s) / 2; } else if( VertLeq( d1, d2 )) { /* Interpolate between o2 and d1 */ z1 = EdgeEval( o1, o2, d1 ); z2 = EdgeEval( o2, d1, d2 ); if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } v->s = Interpolate( z1, o2->s, z2, d1->s ); } else { /* Interpolate between o2 and d2 */ z1 = EdgeSign( o1, o2, d1 ); z2 = -EdgeSign( o1, d2, d1 ); if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } v->s = Interpolate( z1, o2->s, z2, d2->s ); } /* Now repeat the process for t */ if( ! TransLeq( o1, d1 )) { Swap( o1, d1 ); } if( ! TransLeq( o2, d2 )) { Swap( o2, d2 ); } if( ! TransLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); } if( ! TransLeq( o2, d1 )) { /* Technically, no intersection -- do our best */ v->t = (o2->t + d1->t) / 2; } else if( TransLeq( d1, d2 )) { /* Interpolate between o2 and d1 */ z1 = TransEval( o1, o2, d1 ); z2 = TransEval( o2, d1, d2 ); if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } v->t = Interpolate( z1, o2->t, z2, d1->t ); } else { /* Interpolate between o2 and d2 */ z1 = TransSign( o1, o2, d1 ); z2 = -TransSign( o1, d2, d1 ); if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } v->t = Interpolate( z1, o2->t, z2, d2->t ); } }
static void ConnectLeftVertex( TESStesselator *tess, TESSvertex *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; TESShalfEdge *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 */ /* tessDictListSearch */ regUp = (ActiveRegion *)dictKey( dictSearch( tess->dict, &tmp )); regLo = RegionBelow( regUp ); if( !regLo ) { // This may happen if the input polygon is coplanar. return; } 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 = tessMeshConnect( tess->mesh, vEvent->anEdge->Sym, eUp->Lnext ); if (eNew == NULL) longjmp(tess->env,1); } else { TESShalfEdge *tempHalfEdge= tessMeshConnect( tess->mesh, eLo->Dnext, vEvent->anEdge); if (tempHalfEdge == NULL) longjmp(tess->env,1); eNew = tempHalfEdge->Sym; } if( reg->fixUpperEdge ) { if ( !FixUpperEdge( tess, 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 int CheckForIntersect( TESStesselator *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); TESShalfEdge *eUp = regUp->eUp; TESShalfEdge *eLo = regLo->eUp; TESSvertex *orgUp = eUp->Org; TESSvertex *orgLo = eLo->Org; TESSvertex *dstUp = eUp->Dst; TESSvertex *dstLo = eLo->Dst; TESSreal tMinUp, tMaxLo; TESSvertex isect, *orgMin; TESShalfEdge *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 ); tesedgeIntersect( 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 ) && 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 (tessMeshSplitEdge( tess->mesh, eUp->Sym ) == NULL) longjmp(tess->env,1); if ( !tessMeshSplice( tess->mesh, eLo->Sym, eUp ) ) longjmp(tess->env,1); regUp = TopLeftRegion( tess, 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 (tessMeshSplitEdge( tess->mesh, eLo->Sym ) == NULL) longjmp(tess->env,1); if ( !tessMeshSplice( tess->mesh, 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 (tessMeshSplitEdge( tess->mesh, 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 (tessMeshSplitEdge( tess->mesh, 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 (tessMeshSplitEdge( tess->mesh, eUp->Sym ) == NULL) longjmp(tess->env,1); if (tessMeshSplitEdge( tess->mesh, eLo->Sym ) == NULL) longjmp(tess->env,1); if ( !tessMeshSplice( tess->mesh, eLo->Oprev, eUp ) ) longjmp(tess->env,1); eUp->Org->s = isect.s; eUp->Org->t = isect.t; eUp->Org->pqHandle = pqInsert( &tess->alloc, tess->pq, eUp->Org ); if (eUp->Org->pqHandle == INV_HANDLE) { pqDeletePriorityQ( &tess->alloc, tess->pq ); 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; }