Esempio n. 1
0
        public Face trail; /* "stack" for conversion to strips */

        #endregion Fields

        #region Methods

        public static void AddToTrail(ref Face f, ref Face t)
        {
            f.trail = t;

            t = f;
            f.marked = true;
        }
Esempio n. 2
0
 public static void FreeTrail(ref Face t)
 {
     while (t != null)
     {
         t.marked = false;
         t = t.trail;
     }
 }
Esempio n. 3
0
        /* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left
        * face of all edges in the face loop to which eOrig belongs.  "fNext" gives
        * a place to insert the new face in the global face list.  We insert
        * the new face *before* fNext so that algorithms which walk the face
        * list will not see the newly created faces.
        */
        private static void MakeFace(Face newFace, HalfEdge eOrig, Face fNext)
        {
            HalfEdge e;
            Face fPrev;
            Face fNew = newFace;

            fNew.indexDebug = faceIndex++;
            // insert in circular doubly-linked list before fNext

            fPrev = fNext.prevFace;
            fNew.prevFace = fPrev;
            fPrev.nextFace = fNew;
            fNew.nextFace = fNext;
            fNext.prevFace = fNew;

            fNew.halfEdgeThisIsLeftFaceOf = eOrig;
            fNew.trail = null;
            fNew.marked = false;

            // The new face is marked "inside" if the old one was.  This is a
            // convenience for the common case where a face has been split in two.
            fNew.isInterior = fNext.isInterior;

            // fix other edges on this face loop
            e = eOrig;
            do
            {
                e.leftFace = fNew;
                e = e.nextEdgeCCWAroundLeftFace;
            } while (e != eOrig);
        }
Esempio n. 4
0
        /* KillFace( fDel ) destroys a face and removes it from the global face
        * list.  It updates the face loop to point to a given new face.
        */
        private static void KillFace(Face fDel, Face newLface)
        {
            HalfEdge e, eStart = fDel.halfEdgeThisIsLeftFaceOf;
            Face fPrev, fNext;

            /* change the left face of all affected edges */
            e = eStart;
            do
            {
                e.leftFace = newLface;
                e = e.nextEdgeCCWAroundLeftFace;
            } while (e != eStart);

            /* delete from circular doubly-linked list */
            fPrev = fDel.prevFace;
            fNext = fDel.nextFace;
            fNext.prevFace = fPrev;
            fPrev.nextFace = fNext;
        }
Esempio n. 5
0
        // __gl_meshMakeEdge creates one edge, two vertices, and a loop (face).
        // The loop consists of the two new half-edges.
        public HalfEdge MakeEdge()
        {
            var newVertex1 = new ContourVertex();
            var newVertex2 = new ContourVertex();
            var newFace = new Face();
            HalfEdge e;

            e = MakeEdge(halfEdgeHead);

            MakeVertex(newVertex1, e, vertexHead);
            MakeVertex(newVertex2, e.otherHalfOfThisEdge, vertexHead);
            MakeFace(newFace, e, faceHead);
            return e;
        }
Esempio n. 6
0
        /* __gl_meshZapFace( fZap ) destroys a face and removes it from the
        * global face list.  All edges of fZap will have a null pointer as their
        * left face.  Any edges which also have a null pointer as their right face
        * are deleted entirely (along with any isolated vertices this produces).
        * An entire mesh can be deleted by zapping its faces, one at a time,
        * in any order.  Zapped faces cannot be used in further mesh operations!
        */
        public static void meshZapFace(Face fZap)
        {
            HalfEdge eStart = fZap.halfEdgeThisIsLeftFaceOf;
            HalfEdge e, eNext, eSym;
            Face fPrev, fNext;

            /* walk around face, deleting edges whose right face is also null */
            eNext = eStart.nextEdgeCCWAroundLeftFace;
            do
            {
                e = eNext;
                eNext = e.nextEdgeCCWAroundLeftFace;

                e.leftFace = null;
                if (e.rightFace == null)
                {
                    /* delete the edge -- see __gl_MeshDelete above */

                    if (e.nextEdgeCCWAroundOrigin == e)
                    {
                        KillVertex(e.originVertex, null);
                    }
                    else
                    {
                        /* Make sure that e.Org points to a valid half-edge */
                        e.originVertex.edgeThisIsOriginOf = e.nextEdgeCCWAroundOrigin;
                        Splice(e, e.Oprev);
                    }
                    eSym = e.otherHalfOfThisEdge;
                    if (eSym.nextEdgeCCWAroundOrigin == eSym)
                    {
                        KillVertex(eSym.originVertex, null);
                    }
                    else
                    {
                        /* Make sure that eSym.Org points to a valid half-edge */
                        eSym.originVertex.edgeThisIsOriginOf = eSym.nextEdgeCCWAroundOrigin;
                        Splice(eSym, eSym.Oprev);
                    }
                    KillEdge(e);
                }
            } while (e != eStart);

            /* delete from circular doubly-linked list */
            fPrev = fZap.prevFace;
            fNext = fZap.nextFace;
            fNext.prevFace = fPrev;
            fPrev.nextFace = fNext;

            fZap = null;
        }
Esempio n. 7
0
        /* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the
        * mesh connectivity and topology.  It changes the mesh so that
        *	eOrg.Onext <- OLD( eDst.Onext )
        *	eDst.Onext <- OLD( eOrg.Onext )
        * where OLD(...) means the value before the meshSplice operation.
        *
        * This can have two effects on the vertex structure:
        *  - if eOrg.Org != eDst.Org, the two vertices are merged together
        *  - if eOrg.Org == eDst.Org, the origin is split into two vertices
        * In both cases, eDst.Org is changed and eOrg.Org is untouched.
        *
        * Similarly (and independently) for the face structure,
        *  - if eOrg.Lface == eDst.Lface, one loop is split into two
        *  - if eOrg.Lface != eDst.Lface, two distinct loops are joined into one
        * In both cases, eDst.Lface is changed and eOrg.Lface is unaffected.
        *
        * Some special cases:
        * If eDst == eOrg, the operation has no effect.
        * If eDst == eOrg.Lnext, the new face will have a single edge.
        * If eDst == eOrg.Lprev, the old face will have a single edge.
        * If eDst == eOrg.Onext, the new vertex will have a single edge.
        * If eDst == eOrg.Oprev, the old vertex will have a single edge.
        */
        public static void meshSplice(HalfEdge eOrg, HalfEdge eDst)
        {
            bool joiningLoops = false;
            bool joiningVertices = false;

            if (eOrg == eDst) return;

            if (eDst.originVertex != eOrg.originVertex)
            {
                /* We are merging two disjoint vertices -- destroy eDst.Org */
                joiningVertices = true;
                KillVertex(eDst.originVertex, eOrg.originVertex);
            }
            if (eDst.leftFace != eOrg.leftFace)
            {
                /* We are connecting two disjoint loops -- destroy eDst.Lface */
                joiningLoops = true;
                KillFace(eDst.leftFace, eOrg.leftFace);
            }

            /* Change the edge structure */
            Splice(eDst, eOrg);

            if (!joiningVertices)
            {
                var newVertex = new ContourVertex();

                /* We split one vertex into two -- the new vertex is eDst.Org.
                * Make sure the old vertex points to a valid half-edge.
                */
                MakeVertex(newVertex, eDst, eOrg.originVertex);
                eOrg.originVertex.edgeThisIsOriginOf = eOrg;
            }
            if (!joiningLoops)
            {
                var newFace = new Face();

                /* We split one loop into two -- the new loop is eDst.Lface.
                * Make sure the old face points to a valid half-edge.
                */
                MakeFace(newFace, eDst, eOrg.leftFace);
                eOrg.leftFace.halfEdgeThisIsLeftFaceOf = eOrg;
            }
        }
Esempio n. 8
0
        /* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg.Dst
        * to eDst.Org, and returns the corresponding half-edge eNew.
        * If eOrg.Lface == eDst.Lface, this splits one loop into two,
        * and the newly created loop is eNew.Lface.  Otherwise, two disjoint
        * loops are merged into one, and the loop eDst.Lface is destroyed.
        *
        * If (eOrg == eDst), the new face will have only two edges.
        * If (eOrg.Lnext == eDst), the old face is reduced to a single edge.
        * If (eOrg.Lnext.Lnext == eDst), the old face is reduced to two edges.
        */
        public static HalfEdge meshConnect(HalfEdge eOrg, HalfEdge eDst)
        {
            HalfEdge eNewSym;
            bool joiningLoops = false;
            HalfEdge eNew = MakeEdge(eOrg);

            eNewSym = eNew.otherHalfOfThisEdge;

            if (eDst.leftFace != eOrg.leftFace)
            {
                /* We are connecting two disjoint loops -- destroy eDst.Lface */
                joiningLoops = true;
                KillFace(eDst.leftFace, eOrg.leftFace);
            }

            /* Connect the new edge appropriately */
            Splice(eNew, eOrg.nextEdgeCCWAroundLeftFace);
            Splice(eNewSym, eDst);

            /* Set the vertex and face information */
            eNew.originVertex = eOrg.directionVertex;
            eNewSym.originVertex = eDst.originVertex;
            eNew.leftFace = eNewSym.leftFace = eOrg.leftFace;

            /* Make sure the old face points to a valid half-edge */
            eOrg.leftFace.halfEdgeThisIsLeftFaceOf = eNewSym;

            if (!joiningLoops)
            {
                var newFace = new Face();

                /* We split one loop into two -- the new loop is eNew.Lface */
                MakeFace(newFace, eNew, eOrg.leftFace);
            }
            return eNew;
        }
Esempio n. 9
0
        /* __gl_meshDelete( eDel ) removes the edge eDel.  There are several cases:
        * if (eDel.Lface != eDel.Rface), we join two loops into one; the loop
        * eDel.Lface is deleted.  Otherwise, we are splitting one loop into two;
        * the newly created loop will contain eDel.Dst.  If the deletion of eDel
        * would create isolated vertices, those are deleted as well.
        *
        * This function could be implemented as two calls to __gl_meshSplice
        * plus a few calls to free, but this would allocate and delete
        * unnecessary vertices and faces.
        */
        public static void DeleteHalfEdge(HalfEdge edgeToDelete)
        {
            HalfEdge otherHalfOfEdgeToDelete = edgeToDelete.otherHalfOfThisEdge;
            bool joiningLoops = false;

            // First step: disconnect the origin vertex eDel.Org.  We make all
            // changes to get a consistent mesh in this "intermediate" state.
            if (edgeToDelete.leftFace != edgeToDelete.rightFace)
            {
                // We are joining two loops into one -- remove the left face
                joiningLoops = true;
                KillFace(edgeToDelete.leftFace, edgeToDelete.rightFace);
            }

            if (edgeToDelete.nextEdgeCCWAroundOrigin == edgeToDelete)
            {
                KillVertex(edgeToDelete.originVertex, null);
            }
            else
            {
                // Make sure that eDel.Org and eDel.Rface point to valid half-edges
                edgeToDelete.rightFace.halfEdgeThisIsLeftFaceOf = edgeToDelete.Oprev;
                edgeToDelete.originVertex.edgeThisIsOriginOf = edgeToDelete.nextEdgeCCWAroundOrigin;

                Splice(edgeToDelete, edgeToDelete.Oprev);
                if (!joiningLoops)
                {
                    var newFace = new Face();

                    // We are splitting one loop into two -- create a new loop for eDel.
                    MakeFace(newFace, edgeToDelete, edgeToDelete.leftFace);
                }
            }

            // Claim: the mesh is now in a consistent state, except that eDel.Org
            // may have been deleted.  Now we disconnect eDel.Dst.
            if (otherHalfOfEdgeToDelete.nextEdgeCCWAroundOrigin == otherHalfOfEdgeToDelete)
            {
                KillVertex(otherHalfOfEdgeToDelete.originVertex, null);
                KillFace(otherHalfOfEdgeToDelete.leftFace, null);
            }
            else
            {
                // Make sure that eDel.Dst and eDel.Lface point to valid half-edges
                edgeToDelete.leftFace.halfEdgeThisIsLeftFaceOf = otherHalfOfEdgeToDelete.Oprev;
                otherHalfOfEdgeToDelete.originVertex.edgeThisIsOriginOf =
                    otherHalfOfEdgeToDelete.nextEdgeCCWAroundOrigin;
                Splice(otherHalfOfEdgeToDelete, otherHalfOfEdgeToDelete.Oprev);
            }

            // Any isolated vertices or faces have already been freed.
            KillEdge(edgeToDelete);
        }
Esempio n. 10
0
        private void RenderMaximumFaceGroup(Face fOrig)
        {
            /* We want to find the largest triangle fan or strip of unmarked faces
            * which includes the given face fOrig.  There are 3 possible fans
            * passing through fOrig (one centered at each vertex), and 3 possible
            * strips (one for each CCW permutation of the vertices).  Our strategy
            * is to try all of these, and take the primitive which uses the most
            * triangles (a greedy approach).
            */
            HalfEdge e = fOrig.halfEdgeThisIsLeftFaceOf;
            var max = new FaceCount(1, e, RenderTriangle);
            FaceCount newFace;

            max.size = 1;
            max.eStart = e;

            if (!EdgeCallBackSet)
            {
                newFace = MaximumFan(e);
                if (newFace.size > max.size)
                {
                    max = newFace;
                }
                newFace = MaximumFan(e.nextEdgeCCWAroundLeftFace);
                if (newFace.size > max.size)
                {
                    max = newFace;
                }
                newFace = MaximumFan(e.Lprev);
                if (newFace.size > max.size)
                {
                    max = newFace;
                }

                newFace = MaximumStrip(e);
                if (newFace.size > max.size)
                {
                    max = newFace;
                }
                newFace = MaximumStrip(e.nextEdgeCCWAroundLeftFace);
                if (newFace.size > max.size)
                {
                    max = newFace;
                }
                newFace = MaximumStrip(e.Lprev);
                if (newFace.size > max.size)
                {
                    max = newFace;
                }
            }

            max.CallRender(this, max.eStart, max.size);
        }
Esempio n. 11
0
        private void RenderLonelyTriangles(Face f)
        {
            /* Now we render all the separate triangles which could not be
            * grouped into a triangle fan or strip.
            */
            HalfEdge e;
            bool newState = false;
            bool edgeState = false; /* force edge state output for first vertex */
            bool sentFirstEdge = false;

            CallBegin(TriangleListType.Triangles);

            for (; f != null; f = f.trail)
            {
                /* Loop once for each edge (there will always be 3 edges) */

                e = f.halfEdgeThisIsLeftFaceOf;
                do
                {
                    if (EdgeCallBackSet)
                    {
                        /* Set the "edge state" to TRUE just before we output the
                        * first vertex of each edge on the polygon boundary.
                        */
                        newState = !e.rightFace.isInterior;
                        if (edgeState != newState || !sentFirstEdge)
                        {
                            sentFirstEdge = true;
                            edgeState = newState;
                            CallEdegFlag(edgeState);
                        }
                    }

                    CallVertex(e.originVertex.clientIndex);

                    e = e.nextEdgeCCWAroundLeftFace;
                } while (e != f.halfEdgeThisIsLeftFaceOf);
            }

            CallEnd();
        }
Esempio n. 12
0
        public void RenderMesh(Mesh mesh)
        {
            Face f;

            /* Make a list of separate triangles so we can render them all at once */
            lonelyTriList = null;

            for (f = mesh.faceHead.nextFace; f != mesh.faceHead; f = f.nextFace)
            {
                f.marked = false;
            }
            for (f = mesh.faceHead.nextFace; f != mesh.faceHead; f = f.nextFace)
            {
                /* We examine all faces in an arbitrary order.  Whenever we find
                * an unprocessed face F, we output a group of faces including F
                * whose size is maximum.
                */
                if (f.isInterior && !f.marked)
                {
                    RenderMaximumFaceGroup(f);
                    if (!f.marked)
                    {
                        throw new Exception();
                    }
                }
            }
            if (lonelyTriList != null)
            {
                RenderLonelyTriangles(lonelyTriList);
                lonelyTriList = null;
            }
        }