/// <summary> /// TessellateMonoRegion( 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). /// </summary> private void TessellateMonoRegion(MeshUtils.Face face) { // 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. var up = face._anEdge; Debug.Assert(up._Lnext != up && up._Lnext._Lnext != up); int dummy = 0; for (; Geom.VertLeq(up._Dst, up._Org); up = up._Lprev) { dummy += 1; } for (; Geom.VertLeq(up._Org, up._Dst); up = up._Lnext) { dummy += 1; } var lo = up._Lprev; while (up._Lnext != lo) { if (Geom.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 && (Geom.EdgeGoesLeft(lo._Lnext) || Geom.EdgeSign(lo._Org, lo._Dst, lo._Lnext._Dst) <= 0.0f)) { lo = _mesh.Connect(lo._Lnext, lo)._Sym; } lo = lo._Lprev; } else { // lo.Org is on the left. We can make CCW triangles from up.Dst. while (lo._Lnext != up && (Geom.EdgeGoesRight(up._Lprev) || Geom.EdgeSign(up._Dst, up._Org, up._Lprev._Org) >= 0.0f)) { up = _mesh.Connect(up, up._Lprev)._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. Debug.Assert(lo._Lnext != up); while (lo._Lnext._Lnext != up) { lo = _mesh.Connect(lo._Lnext, lo)._Sym; } }
private void TessellateMonoRegion(MeshUtils.Face face) { MeshUtils.Edge edge = face._anEdge; while (Geom.VertLeq(edge._Dst, edge._Org)) { edge = edge._Lprev; } while (Geom.VertLeq(edge._Org, edge._Dst)) { edge = edge._Lnext; } MeshUtils.Edge edge2 = edge._Lprev; while (edge._Lnext != edge2) { if (Geom.VertLeq(edge._Dst, edge2._Org)) { while (edge2._Lnext != edge && (Geom.EdgeGoesLeft(edge2._Lnext) || Geom.EdgeSign(edge2._Org, edge2._Dst, edge2._Lnext._Dst) <= 0f)) { edge2 = _mesh.Connect(edge2._Lnext, edge2)._Sym; } edge2 = edge2._Lprev; } else { while (edge2._Lnext != edge && (Geom.EdgeGoesRight(edge._Lprev) || Geom.EdgeSign(edge._Dst, edge._Org, edge._Lprev._Org) >= 0f)) { Mesh mesh = _mesh; MeshUtils.Edge edge3 = edge; edge = mesh.Connect(edge3, edge3._Lprev)._Sym; } edge = edge._Lnext; } } while (edge2._Lnext._Lnext != edge) { edge2 = _mesh.Connect(edge2._Lnext, edge2)._Sym; } }