Example #1
0
    /// <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.
        MeshUtils.Edge up = face._anEdge;
        Debug.Assert(up._Lnext != up && up._Lnext._Lnext != up);

        while (Geom.VertLeq(up._Dst, up._Org))
        {
            up = up._Lprev;
        }

        while (Geom.VertLeq(up._Org, up._Dst))
        {
            up = up._Lnext;
        }

        MeshUtils.Edge 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(_pool, 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(_pool, 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(_pool, lo._Lnext, lo)._Sym;
        }
    }