/* __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). */ internal static bool __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 (; Geom.VertLeq(up.Sym.Org, up.Org); up = up.Onext.Sym) { ; } for (; Geom.VertLeq(up.Org, up.Sym.Org); up = up.Lnext) { ; } lo = up.Onext.Sym; while (up.Lnext != lo) { if (Geom.VertLeq(up.Sym.Org, lo.Org)) { /* up.Sym.Org 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 && (Geom.EdgeGoesLeft(lo.Lnext) || Geom.EdgeSign(lo.Org, lo.Sym.Org, lo.Lnext.Sym.Org) <= 0)) { GLUhalfEdge tempHalfEdge = Mesh.__gl_meshConnect(lo.Lnext, lo); if (tempHalfEdge == null) { return(false); } lo = tempHalfEdge.Sym; } lo = lo.Onext.Sym; } else { /* lo.Org is on the left. We can make CCW triangles from up.Sym.Org. */ while (lo.Lnext != up && (Geom.EdgeGoesRight(up.Onext.Sym) || Geom.EdgeSign(up.Sym.Org, up.Org, up.Onext.Sym.Org) >= 0)) { GLUhalfEdge tempHalfEdge = Mesh.__gl_meshConnect(up, up.Onext.Sym); if (tempHalfEdge == null) { return(false); } up = tempHalfEdge.Sym; } up = up.Lnext; } } /* Now lo.Org == up.Sym.Org == 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 = Mesh.__gl_meshConnect(lo.Lnext, lo); if (tempHalfEdge == null) { return(false); } lo = tempHalfEdge.Sym; } return(true); }
/* __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). */ internal static bool __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 (; Geom.VertLeq(up.Sym.Org, up.Org); up = up.Onext.Sym) ; for (; Geom.VertLeq(up.Org, up.Sym.Org); up = up.Lnext) ; lo = up.Onext.Sym; while (up.Lnext != lo) { if (Geom.VertLeq(up.Sym.Org, lo.Org)) { /* up.Sym.Org 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 && (Geom.EdgeGoesLeft(lo.Lnext) || Geom.EdgeSign(lo.Org, lo.Sym.Org, lo.Lnext.Sym.Org) <= 0)) { GLUhalfEdge tempHalfEdge = Mesh.__gl_meshConnect(lo.Lnext, lo); if (tempHalfEdge == null) return false; lo = tempHalfEdge.Sym; } lo = lo.Onext.Sym; } else { /* lo.Org is on the left. We can make CCW triangles from up.Sym.Org. */ while (lo.Lnext != up && (Geom.EdgeGoesRight(up.Onext.Sym) || Geom.EdgeSign(up.Sym.Org, up.Org, up.Onext.Sym.Org) >= 0)) { GLUhalfEdge tempHalfEdge = Mesh.__gl_meshConnect(up, up.Onext.Sym); if (tempHalfEdge == null) return false; up = tempHalfEdge.Sym; } up = up.Lnext; } } /* Now lo.Org == up.Sym.Org == 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 = Mesh.__gl_meshConnect(lo.Lnext, lo); if (tempHalfEdge == null) return false; lo = tempHalfEdge.Sym; } return true; }