/// <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;
}
}