Ejemplo n.º 1
0
    // Assumes that the vertices are located in the same plane
    // https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
    // Return null if triangulation does not succeed
    // can potential also delete edges (if edges of length 0 is found)
    public List <Face> Triangulate(bool step = false, StringWriter debug = null)
    {
#if HMDebug
        if (debug != null)
        {
            debug.WriteLine("Triangulate face with debug");
        }
#endif
        Debug.Assert(!IsDestroyed());
        List <Face> res = new List <Face>();
        res.Add(this);

        var face = this;

        bool isVerticesLinear = IsVerticesLinear();
#if HMDebug
        if (debug != null)
        {
            debug.WriteLine("isVerticesLinear " + isVerticesLinear);
        }
#endif
        if (isVerticesLinear)
        {
            return(TriangulateFaceOnLine());
        }
        var halfedges = Circulate();
        var normal    = GetNormal();

        int iterations = halfedges.Count * 2;
        while (halfedges.Count > 3 && iterations > 0)
        {
            iterations--;

            Halfedge zeroEdge       = null;
            Halfedge degenerateEdge = null;
            Halfedge sharpCorner    = null;
            double   minimumAngle   = 999;

            // find ear to clip
            for (int i = 0; i < halfedges.Count; i++)
            {
                var      he1      = halfedges[i];
                var      he2      = he1.next;
                var      dir1     = he1.GetDirection();
                var      dir2     = he2.GetDirection();
                Vector3D crossDir = Vector3D.Cross(dir1, dir2);
                var      angle    = Vector3D.Angle(dir1, dir2);

                if (dir1.sqrMagnitude < face.hmesh.zeroMagnitudeTresholdSqr)
                {
                    zeroEdge = he1;
                    break;
                }

                bool isParallel      = Vector3D.IsParallelDist(dir1, dir2, hmesh.zeroMagnitudeTresholdSqr);
                bool isSameDir       = Vector3D.Dot(dir1, dir2) > 0;
                bool isNormalSameDir = Vector3D.Dot(crossDir, normal) > 0;
                if (isParallel && isSameDir == false)
                {
                    degenerateEdge = he1;
                    break;
                }
                bool intersection = false;
                if (isNormalSameDir && !isParallel)
                {
                    for (int j = i + 2; j < i + halfedges.Count; j++)
                    {
                        var heJ = halfedges[j % halfedges.Count];
                        if (PointInTriangle(heJ.prev.vert.positionD, he1.prev.vert.positionD, he1.vert.positionD, he2.vert.positionD) ||
                            PointInTriangle(heJ.vert.positionD, he1.prev.vert.positionD, he1.vert.positionD, he2.vert.positionD))
                        {
                            intersection = true;
                            break; // break intersection test
                        }
                    }
                    // clip ear
                    if (intersection == false)
                    {
                        if (angle < minimumAngle)
                        {
                            minimumAngle = angle;
                            sharpCorner  = he1;
                        }
                    }
                }
            }

            if (zeroEdge != null)
            {
#if HMDebug
                if (debug != null)
                {
                    debug.WriteLine("zeroEdge " + zeroEdge);
                }
#endif
                zeroEdge.Collapse();
                if (face.IsDestroyed())
                {
                    Debug.LogError("Fix");
                    return(new List <Face>());       // Fix
                }
                halfedges = face.Circulate();
            }
            else if (degenerateEdge != null)
            {
                // the main idea, is to cut away the degenerate edge (and collapse the degenerate face)
                //
                //
                //  Example (4 vertices)
                //  ||
                //  ||
                //  |\
                //  | \
                //  ----
#if HMDebug
                if (debug != null)
                {
                    debug.WriteLine("degenerateEdge " + degenerateEdge);
                }
#endif
                var he1 = degenerateEdge;
                var he2 = he1.next;
                // degenerate edges detected
                Halfedge next     = he2.next;
                double   thisDist = he1.GetDirection().magnitude;
                double   nextDist = he2.GetDirection().magnitude;
                Vertex   nextVert = he2.vert;
                Vertex   prevVert = he1.prev.vert;

                if (Math.Abs(nextDist - thisDist) <= face.hmesh.zeroMagnitudeTreshold)
                {
                    // cut from nextVert to prevVert
                }
                else if (nextDist > thisDist)
                {
                    nextVert           = he2.Split();
                    nextVert.positionD = prevVert.positionD;
                }
                else if (thisDist > nextDist)
                {
                    prevVert           = he1.Split();
                    prevVert.positionD = nextVert.positionD;
                }
                var cutFace    = face.Cut(prevVert, nextVert);
                var sharedEdge = nextVert.GetSharedEdge(prevVert);

                sharedEdge.Collapse();
                if (!cutFace.IsDestroyed())
                {
#if HMDebug
                    Debug.LogWarning("Cut face survived \n" + cutFace.ExportLocalNeighbourhoodToObj());
#endif
                    return(new List <Face>());       // Fix
                }

                halfedges = face.Circulate();
            }
            else if (sharpCorner != null)
            {
#if HMDebug
                if (debug != null)
                {
                    debug.WriteLine("sharpCorner ");
                }
#endif
                var he1 = sharpCorner;
                var he2 = he1.next;

                var newFace = face.Cut(he1.prev.vert, he2.vert);
                newFace.label = label;
                res.Add(newFace);
                if (newFace.Circulate().Count > 3)
                {
                    face = newFace;
                }
                halfedges = face.Circulate();
            }
            if (step)
            {
                return(res);
            }
        }
        for (int i = res.Count - 1; i >= 0; i--)
        {
            if (res[i] == null || res[i].IsDestroyed())
            {
                res.RemoveAt(i);
            }
        }

        return(res);
    }