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

			t = f;
			f.marked = true;
		}
Beispiel #2
0
		static public void FreeTrail(ref Face t)
		{
			while (t != null)
			{
				t.marked = false;
				t = t.trail;
			}
		}
Beispiel #3
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;
		}
Beispiel #4
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)
			{
				Face newFace = new Face();

				/* We split one loop into two -- the new loop is eNew.Lface */
				MakeFace(newFace, eNew, eOrg.leftFace);
			}
			return eNew;
		}
Beispiel #5
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)
				{
					Face 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);
		}
Beispiel #6
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)
			{
				ContourVertex 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)
			{
				Face 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;
			}
		}
Beispiel #7
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;
		}
Beispiel #8
0
		// __gl_meshMakeEdge creates one edge, two vertices, and a loop (face).
		// The loop consists of the two new half-edges.
		public HalfEdge MakeEdge()
		{
			ContourVertex newVertex1 = new ContourVertex();
			ContourVertex newVertex2 = new ContourVertex();
			Face newFace = new Face();
			HalfEdge e;

			e = MakeEdge(this.halfEdgeHead);

			MakeVertex(newVertex1, e, this.vertexHead);
			MakeVertex(newVertex2, e.otherHalfOfThisEdge, this.vertexHead);
			MakeFace(newFace, e, this.faceHead);
			return e;
		}
Beispiel #9
0
		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);
		}
Beispiel #10
0
        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;
            FaceCount max = new FaceCount(1, e, new FaceCount.RenderDelegate(RenderTriangle));
            FaceCount newFace;

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

            if (!this.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);
        }
Beispiel #11
0
        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;

            this.CallBegin(Tesselator.TriangleListType.Triangles);

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

                e = f.halfEdgeThisIsLeftFaceOf;
                do
                {
                    if (this.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;
                            this.CallEdegFlag(edgeState);
                        }
                    }

                    this.CallVertex(e.originVertex.clientIndex);

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

            this.CallEnd();
        }
Beispiel #12
0
        /************************ Strips and Fans decomposition ******************/
        /* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle
        * fans, strips, and separate triangles.  A substantial effort is made
        * to use as few rendering primitives as possible (ie. to make the fans
        * and strips as large as possible).
        *
        * The rendering output is provided as callbacks (see the api).
        */
        public void RenderMesh(Mesh mesh)
        {
            Face f;

            /* Make a list of separate triangles so we can render them all at once */
            this.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 System.Exception();
                    }
                }
            }
            if (this.lonelyTriList != null)
            {
                RenderLonelyTriangles(this.lonelyTriList);
                this.lonelyTriList = null;
            }
        }
Beispiel #13
0
        /* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency.
         */
        public void CheckMesh()
        {
            Face <T>          fHead = this.faceHead;
            ContourVertex <T> vHead = this.vertexHead;
            HalfEdge <T>      eHead = this.halfEdgeHead;
            Face <T>          f, fPrev;
            ContourVertex <T> v, vPrev;
            HalfEdge <T>      e, ePrev;

            fPrev = fHead;
            for (fPrev = fHead; (f = fPrev.nextFace) != fHead; fPrev = f)
            {
                if (f.prevFace != fPrev)
                {
                    throw new Exception();
                }
                e = f.halfEdgeThisIsLeftFaceOf;
                do
                {
                    if (e.otherHalfOfThisEdge == e)
                    {
                        throw new Exception();
                    }
                    if (e.otherHalfOfThisEdge.otherHalfOfThisEdge != e)
                    {
                        throw new Exception();
                    }
                    if (e.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge != e)
                    {
                        throw new Exception();
                    }
                    if (e.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace != e)
                    {
                        throw new Exception();
                    }
                    if (e.leftFace != f)
                    {
                        throw new Exception();
                    }
                    e = e.nextEdgeCCWAroundLeftFace;
                } while (e != f.halfEdgeThisIsLeftFaceOf);
            }
            if (f.prevFace != fPrev || f.halfEdgeThisIsLeftFaceOf != null)
            {
                throw new Exception();
            }

            vPrev = vHead;
            for (vPrev = vHead; (v = vPrev.nextVertex) != vHead; vPrev = v)
            {
                if (v.prevVertex != vPrev)
                {
                    throw new Exception();
                }
                e = v.edgeThisIsOriginOf;
                do
                {
                    if (e.otherHalfOfThisEdge == e)
                    {
                        throw new Exception();
                    }
                    if (e.otherHalfOfThisEdge.otherHalfOfThisEdge != e)
                    {
                        throw new Exception();
                    }
                    if (e.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge != e)
                    {
                        throw new Exception();
                    }
                    if (e.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace != e)
                    {
                        throw new Exception();
                    }
                    if (e.originVertex != v)
                    {
                        throw new Exception();
                    }
                    e = e.nextEdgeCCWAroundOrigin;
                } while (e != v.edgeThisIsOriginOf);
            }
            if (v.prevVertex != vPrev || v.edgeThisIsOriginOf != null || v.clientIndex != 0)
            {
                throw new Exception();
            }

            ePrev = eHead;
            for (ePrev = eHead; (e = ePrev.nextHalfEdge) != eHead; ePrev = e)
            {
                if (e.otherHalfOfThisEdge.nextHalfEdge != ePrev.otherHalfOfThisEdge)
                {
                    throw new Exception();
                }
                if (e.otherHalfOfThisEdge == e)
                {
                    throw new Exception();
                }
                if (e.otherHalfOfThisEdge.otherHalfOfThisEdge != e)
                {
                    throw new Exception();
                }
                if (e.originVertex == null)
                {
                    throw new Exception();
                }
                if (e.directionVertex == null)
                {
                    throw new Exception();
                }
                if (e.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge != e)
                {
                    throw new Exception();
                }
                if (e.nextEdgeCCWAroundOrigin.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace != e)
                {
                    throw new Exception();
                }
            }
            if (e.otherHalfOfThisEdge.nextHalfEdge != ePrev.otherHalfOfThisEdge ||
                e.otherHalfOfThisEdge != this.otherHalfOfThisEdgeHead ||
                e.otherHalfOfThisEdge.otherHalfOfThisEdge != e ||
                e.originVertex != null || e.directionVertex != null ||
                e.leftFace != null || e.rightFace != null)
            {
                throw new Exception();
            }
        }
        FaceCount MaximumStrip(HalfEdge eOrig)
        {
            /* Here we are looking for a maximal strip that contains the vertices
             * eOrig.Org, eOrig.Dst, eOrig.Lnext.Dst (in that order or the
             * reverse, such that all triangles are oriented CCW).
             *
             * Again we walk forward and backward as far as possible.  However for
             * strips there is a twist: to get CCW orientations, there must be
             * an *even* number of triangles in the strip on one side of eOrig.
             * We walk the strip starting on a side with an even number of triangles;
             * if both side have an odd number, we are forced to shorten one side.
             */
            FaceCount newFace = new FaceCount(0, null, RenderStrip);
            int       headSize = 0, tailSize = 0;
            Face      trail = null;
            HalfEdge  e, eTail, eHead;

            for (e = eOrig; !e.leftFace.Marked(); ++tailSize, e = e.nextEdgeCCWAroundOrigin)
            {
                Face.AddToTrail(ref e.leftFace, ref trail);
                ++tailSize;
                e = e.Dprev;
                if (e.leftFace.Marked())
                {
                    break;
                }
                Face.AddToTrail(ref e.leftFace, ref trail);
            }
            eTail = e;
            for (e = eOrig; !e.rightFace.Marked(); ++headSize, e = e.Dnext)
            {
                Face f = e.rightFace;
                Face.AddToTrail(ref f, ref trail);
                e.rightFace = f;
                ++headSize;
                e = e.Oprev;
                if (e.rightFace.Marked())
                {
                    break;
                }
                f = e.rightFace;
                Face.AddToTrail(ref f, ref trail);
                e.rightFace = f;
            }
            eHead        = e;
            newFace.size = tailSize + headSize;
            if (IsEven(tailSize))
            {
                newFace.eStart = eTail.otherHalfOfThisEdge;
            }
            else if (IsEven(headSize))
            {
                newFace.eStart = eHead;
            }
            else
            {
                /* Both sides have odd length, we must shorten one of them.  In fact,
                 * we must start from eHead to guarantee inclusion of eOrig.Lface.
                 */
                --newFace.size;
                newFace.eStart = eHead.nextEdgeCCWAroundOrigin;
            }

            Face.FreeTrail(ref trail);
            return(newFace);
        }