Esempio n. 1
0
        static void ConnectRightVertex(Tesselator tess, ActiveRegion regUp, HalfEdge eBottomLeft)
        /*
         * Purpose: connect a "right" vertex vEvent (one where all edges go left)
         * to the unprocessed portion of the mesh.  Since there are no right-going
         * edges, two regions (one above vEvent and one below) are being merged
         * into one.  "regUp" is the upper of these two regions.
         *
         * There are two reasons for doing this (adding a right-going edge):
         *  - if the two regions being merged are "inside", we must add an edge
         *    to keep them separated (the combined region would not be monotone).
         *  - in any case, we must leave some record of vEvent in the dictionary,
         *    so that we can merge vEvent with features that we have not seen yet.
         *    For example, maybe there is a vertical edge which passes just to
         *    the right of vEvent; we would like to splice vEvent into this edge.
         *
         * However, we don't want to connect vEvent to just any vertex.  We don''t
         * want the new edge to cross any other edges; otherwise we will create
         * intersection vertices even when the input data had no self-intersections.
         * (This is a bad thing; if the user's input data has no intersections,
         * we don't want to generate any false intersections ourselves.)
         *
         * Our eventual goal is to connect vEvent to the leftmost unprocessed
         * vertex of the combined region (the union of regUp and regLo).
         * But because of unseen vertices with all right-going edges, and also
         * new vertices which may be created by edge intersections, we don''t
         * know where that leftmost unprocessed vertex is.  In the meantime, we
         * connect vEvent to the closest vertex of either chain, and mark the region
         * as "fixUpperEdge".  This flag says to delete and reconnect this edge
         * to the next processed vertex on the boundary of the combined region.
         * Quite possibly the vertex we connected to will turn out to be the
         * closest one, in which case we won''t need to make any changes.
         */
        {
            HalfEdge eNew;
            HalfEdge eTopLeft = eBottomLeft.nextEdgeCCWAroundOrigin;
            ActiveRegion regLo = RegionBelow(regUp);
            HalfEdge eUp = regUp.upperHalfEdge;
            HalfEdge eLo = regLo.upperHalfEdge;
            bool degenerate = false;
            if (eUp.directionVertex != eLo.directionVertex)
            {
                CheckForIntersect(tess, regUp);
            }

            /* Possible new degeneracies: upper or lower edge of regUp may pass
             * through vEvent, or may coincide with new intersection vertex
             */
            if (eUp.originVertex.VertEq(tess.currentSweepVertex))
            {
                Mesh.meshSplice(eTopLeft.Oprev, eUp);
                regUp = TopLeftRegion(regUp);
                eTopLeft = RegionBelow(regUp).upperHalfEdge;
                FinishLeftRegions(tess, RegionBelow(regUp), regLo);
                degenerate = true;
            }
            if (eLo.originVertex.VertEq(tess.currentSweepVertex))
            {
                Mesh.meshSplice(eBottomLeft, eLo.Oprev);
                eBottomLeft = FinishLeftRegions(tess, regLo, null);
                degenerate = true;
            }
            if (degenerate)
            {
                AddRightEdges(tess, regUp, eBottomLeft.nextEdgeCCWAroundOrigin, eTopLeft, eTopLeft, true);
                return;
            }

            /* Non-degenerate situation -- need to add a temporary, fixable edge.
             * Connect to the closer of eLo.Org, eUp.Org.
             */
            if (eLo.originVertex.VertLeq(eUp.originVertex))
            {
                eNew = eLo.Oprev;
            }
            else
            {
                eNew = eUp;
            }
            eNew = Mesh.meshConnect(eBottomLeft.Lprev, eNew);
            /* Prevent cleanup, otherwise eNew might disappear before we've even
             * had a chance to mark it as a temporary edge.
             */
            AddRightEdges(tess, regUp, eNew, eNew.nextEdgeCCWAroundOrigin, eNew.nextEdgeCCWAroundOrigin, false);
            eNew.otherHalfOfThisEdge.regionThisIsUpperEdgeOf.fixUpperEdge = true;
            WalkDirtyRegions(tess, regUp);
        }
Esempio n. 2
0
		/* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that
		* eNew == eOrg.Lnext, and eNew.Dst is a newly created vertex.
		* eOrg and eNew will have the same left face.
		*/

		private static HalfEdge meshAddEdgeVertex(HalfEdge eOrg)
		{
			HalfEdge eNewSym;
			HalfEdge eNew = MakeEdge(eOrg);

			eNewSym = eNew.otherHalfOfThisEdge;

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

			/* Set the vertex and face information */
			eNew.originVertex = eOrg.directionVertex;
			{
				ContourVertex newVertex = new ContourVertex();

				MakeVertex(newVertex, eNewSym, eNew.originVertex);
			}
			eNew.leftFace = eNewSym.leftFace = eOrg.leftFace;

			return eNew;
		}
Esempio n. 3
0
		/* MakeEdge creates a new pair of half-edges which form their own loop.
		* No vertex or face structures are allocated, but these must be assigned
		* before the current edge operation is completed.
		*/

		private static HalfEdge MakeEdge(HalfEdge eNext)
		{
			HalfEdge ePrev;
			EdgePair pair = new EdgePair();

			/* Make sure eNext points to the first edge of the edge pair */
			if (eNext.otherHalfOfThisEdge.isFirstHalfEdge)
			{
				eNext = eNext.otherHalfOfThisEdge;
			}

			/* Insert in circular doubly-linked list before eNext.
			* Note that the prev pointer is stored in Sym.next.
			*/
			ePrev = eNext.otherHalfOfThisEdge.nextHalfEdge;
			pair.eSym.nextHalfEdge = ePrev;
			ePrev.otherHalfOfThisEdge.nextHalfEdge = pair.e;
			pair.e.nextHalfEdge = eNext;
			eNext.otherHalfOfThisEdge.nextHalfEdge = pair.eSym;

			pair.e.isFirstHalfEdge = true;
			pair.e.otherHalfOfThisEdge = pair.eSym;
			pair.e.nextEdgeCCWAroundOrigin = pair.e;
			pair.e.nextEdgeCCWAroundLeftFace = pair.eSym;
			pair.e.originVertex = null;
			pair.e.leftFace = null;
			pair.e.winding = 0;
			pair.e.regionThisIsUpperEdgeOf = null;

			pair.eSym.isFirstHalfEdge = false;
			pair.eSym.otherHalfOfThisEdge = pair.e;
			pair.eSym.nextEdgeCCWAroundOrigin = pair.eSym;
			pair.eSym.nextEdgeCCWAroundLeftFace = pair.e;
			pair.eSym.originVertex = null;
			pair.eSym.leftFace = null;
			pair.eSym.winding = 0;
			pair.eSym.regionThisIsUpperEdgeOf = null;

			return pair.e;
		}
Esempio n. 4
0
		/* Splice( a, b ) is best described by the Guibas/Stolfi paper or the
		* CS348a notes (see mesh.h).  Basically it modifies the mesh so that
		* a.Onext and b.Onext are exchanged.  This can have various effects
		* depending on whether a and b belong to different face or vertex rings.
		* For more explanation see __gl_meshSplice() below.
		*/

		private static void Splice(HalfEdge a, HalfEdge b)
		{
			HalfEdge aOnext = a.nextEdgeCCWAroundOrigin;
			HalfEdge bOnext = b.nextEdgeCCWAroundOrigin;

			aOnext.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace = b;
			bOnext.otherHalfOfThisEdge.nextEdgeCCWAroundLeftFace = a;
			a.nextEdgeCCWAroundOrigin = bOnext;
			b.nextEdgeCCWAroundOrigin = aOnext;
		}
Esempio n. 5
0
		/* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel.Sym),
		* and removes from the global edge list.
		*/

		private static void KillEdge(HalfEdge eDel)
		{
			HalfEdge ePrev, eNext;

			/* Half-edges are allocated in pairs, see EdgePair above */
			if (eDel.otherHalfOfThisEdge.isFirstHalfEdge)
			{
				eDel = eDel.otherHalfOfThisEdge;
			}

			/* delete from circular doubly-linked list */
			eNext = eDel.nextHalfEdge;
			ePrev = eDel.otherHalfOfThisEdge.nextHalfEdge;
			eNext.otherHalfOfThisEdge.nextHalfEdge = ePrev;
			ePrev.otherHalfOfThisEdge.nextHalfEdge = eNext;
		}
Esempio n. 6
0
        /*
         * Add a new active region to the sweep line, *somewhere* below "regAbove"
         * (according to where the new edge belongs in the sweep-line dictionary).
         * The upper edge of the new region will be "eNewUp".
         * Winding number and "inside" flag are not updated.
         */
        static ActiveRegion AddRegionBelow(Tesselator tess,
            ActiveRegion regAbove,
            HalfEdge eNewUp)
        {
            ActiveRegion regNew = new ActiveRegion();

            regNew.upperHalfEdge = eNewUp;
            /* __gl_dictListInsertBefore */
            regNew.upperHalfEdgeDictNode = tess.edgeDictionary.InsertBefore(regAbove.upperHalfEdgeDictNode, regNew);
            regNew.fixUpperEdge = false;
            regNew.sentinel = false;
            regNew.dirty = false;

            eNewUp.regionThisIsUpperEdgeOf = regNew;
            return regNew;
        }
Esempio n. 7
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);
		}
Esempio n. 8
0
        public void AddVertex(double[] coords3, int data)
        {
            int i;
            double x;
            double[] clamped = new double[3];

            RequireState(ProcessingState.InContour);

            if (emptyCache)
            {
                EmptyCache();
                lastHalfEdge = null;
            }

            for (i = 0; i < 3; ++i)
            {
                x = coords3[i];
                if (x < -MAX_COORD)
                {
                    throw new Exception("Your coordinate exceeded -" + MAX_COORD.ToString() + ".");
                }
                if (x > MAX_COORD)
                {
                    throw new Exception("Your coordinate exceeded " + MAX_COORD.ToString() + ".");
                }
                clamped[i] = x;
            }

            if (mesh == null)
            {
                if (cacheCount < MAX_CACHE_SIZE)
                {
                    CacheVertex(clamped, data);
                    return;
                }
                EmptyCache();
            }

            AddVertex(clamped[0], clamped[1], data);
        }
Esempio n. 9
0
        public void BeginContour()
        {
            RequireState(ProcessingState.InPolygon);

            processingState = ProcessingState.InContour;
            lastHalfEdge = null;
            if (cacheCount > 0)
            {
                // Just set a flag so we don't get confused by empty contours
                emptyCache = true;
            }
        }
Esempio n. 10
0
            public int size; /* number of triangles used */

            #endregion Fields

            #region Constructors

            public FaceCount(int _size, HalfEdge _eStart, RenderDelegate _render)
            {
                size = _size;
                eStart = _eStart;
                render = _render;
            }
Esempio n. 11
0
 // routine to render this primitive
 public void CallRender(Tesselator tess, HalfEdge edge, int data)
 {
     render(tess, edge, data);
 }
Esempio n. 12
0
 public FaceCount(int _size, HalfEdge _eStart, RenderDelegate _render)
 {
     size   = _size;
     eStart = _eStart;
     render = _render;
 }
Esempio n. 13
0
        /*
         * Purpose: connect a "right" vertex vEvent (one where all edges go left)
         * to the unprocessed portion of the mesh.  Since there are no right-going
         * edges, two regions (one above vEvent and one below) are being merged
         * into one.  "regUp" is the upper of these two regions.
         *
         * There are two reasons for doing this (adding a right-going edge):
         *  - if the two regions being merged are "inside", we must add an edge
         *    to keep them separated (the combined region would not be monotone).
         *  - in any case, we must leave some record of vEvent in the dictionary,
         *    so that we can merge vEvent with features that we have not seen yet.
         *    For example, maybe there is a vertical edge which passes just to
         *    the right of vEvent; we would like to splice vEvent into this edge.
         *
         * However, we don't want to connect vEvent to just any vertex.  We don''t
         * want the new edge to cross any other edges; otherwise we will create
         * intersection vertices even when the input data had no self-intersections.
         * (This is a bad thing; if the user's input data has no intersections,
         * we don't want to generate any false intersections ourselves.)
         *
         * Our eventual goal is to connect vEvent to the leftmost unprocessed
         * vertex of the combined region (the union of regUp and regLo).
         * But because of unseen vertices with all right-going edges, and also
         * new vertices which may be created by edge intersections, we don''t
         * know where that leftmost unprocessed vertex is.  In the meantime, we
         * connect vEvent to the closest vertex of either chain, and mark the region
         * as "fixUpperEdge".  This flag says to delete and reconnect this edge
         * to the next processed vertex on the boundary of the combined region.
         * Quite possibly the vertex we connected to will turn out to be the
         * closest one, in which case we won''t need to make any changes.
         */
        static void ConnectRightVertex(Tesselator tess, ActiveRegion regUp, HalfEdge eBottomLeft)
        {
            HalfEdge eNew;
            HalfEdge eTopLeft = eBottomLeft.nextEdgeCCWAroundOrigin;
            ActiveRegion regLo = RegionBelow(regUp);
            HalfEdge eUp = regUp.upperHalfEdge;
            HalfEdge eLo = regLo.upperHalfEdge;
            bool degenerate = false;

            if (eUp.directionVertex != eLo.directionVertex)
            {
                CheckForIntersect(tess, regUp);
            }

            /* Possible new degeneracies: upper or lower edge of regUp may pass
             * through vEvent, or may coincide with new intersection vertex
             */
            if (eUp.originVertex.VertEq(tess.currentSweepVertex))
            {
                Mesh.meshSplice(eTopLeft.Oprev, eUp);
                regUp = TopLeftRegion(regUp);
                eTopLeft = RegionBelow(regUp).upperHalfEdge;
                FinishLeftRegions(tess, RegionBelow(regUp), regLo);
                degenerate = true;
            }
            if (eLo.originVertex.VertEq(tess.currentSweepVertex))
            {
                Mesh.meshSplice(eBottomLeft, eLo.Oprev);
                eBottomLeft = FinishLeftRegions(tess, regLo, null);
                degenerate = true;
            }
            if (degenerate)
            {
                AddRightEdges(tess, regUp, eBottomLeft.nextEdgeCCWAroundOrigin, eTopLeft, eTopLeft, true);
                return;
            }

            /* Non-degenerate situation -- need to add a temporary, fixable edge.
             * Connect to the closer of eLo.Org, eUp.Org.
             */
            if (eLo.originVertex.VertLeq(eUp.originVertex))
            {
                eNew = eLo.Oprev;
            }
            else
            {
                eNew = eUp;
            }
            eNew = Mesh.meshConnect(eBottomLeft.Lprev, eNew);

            /* Prevent cleanup, otherwise eNew might disappear before we've even
             * had a chance to mark it as a temporary edge.
             */
            AddRightEdges(tess, regUp, eNew, eNew.nextEdgeCCWAroundOrigin, eNew.nextEdgeCCWAroundOrigin, false);
            eNew.otherHalfOfThisEdge.regionThisIsUpperEdgeOf.fixUpperEdge = true;
            WalkDirtyRegions(tess, regUp);
        }
Esempio n. 14
0
        /*
         * Purpose: insert right-going edges into the edge dictionary, and update
         * winding numbers and mesh connectivity appropriately.  All right-going
         * edges share a common origin vOrg.  Edges are inserted CCW starting at
         * eFirst; the last edge inserted is eLast.Oprev.  If vOrg has any
         * left-going edges already processed, then eTopLeft must be the edge
         * such that an imaginary upward vertical segment from vOrg would be
         * contained between eTopLeft.Oprev and eTopLeft; otherwise eTopLeft
         * should be null.
         */
        static void AddRightEdges(Tesselator tess, ActiveRegion regUp,
            HalfEdge eFirst, HalfEdge eLast, HalfEdge eTopLeft,
            bool cleanUp)
        {
            ActiveRegion reg, regPrev;
            HalfEdge e, ePrev;
            bool firstTime = true;

            /* Insert the new right-going edges in the dictionary */
            e = eFirst;
            do
            {
                if (!e.originVertex.VertLeq(e.directionVertex))
                {
                    throw new Exception();
                }
                AddRegionBelow(tess, regUp, e.otherHalfOfThisEdge);
                e = e.nextEdgeCCWAroundOrigin;
            } while (e != eLast);

            /* Walk *all* right-going edges from e.Org, in the dictionary order,
             * updating the winding numbers of each region, and re-linking the mesh
             * edges to match the dictionary ordering (if necessary).
             */
            if (eTopLeft == null)
            {
                eTopLeft = RegionBelow(regUp).upperHalfEdge.Rprev;
            }
            regPrev = regUp;
            ePrev = eTopLeft;
            for (; ; )
            {
                reg = RegionBelow(regPrev);
                e = reg.upperHalfEdge.otherHalfOfThisEdge;
                if (e.originVertex != ePrev.originVertex) break;

                if (e.nextEdgeCCWAroundOrigin != ePrev)
                {
                    /* Unlink e from its current position, and relink below ePrev */
                    Mesh.meshSplice(e.Oprev, e);
                    Mesh.meshSplice(ePrev.Oprev, e);
                }
                /* Compute the winding number and "inside" flag for the new regions */
                reg.windingNumber = regPrev.windingNumber - e.winding;
                reg.inside = tess.IsWindingInside(reg.windingNumber);

                /* Check for two outgoing edges with same slope -- process these
                 * before any intersection tests (see example in __gl_computeInterior).
                 */
                regPrev.dirty = true;
                if (!firstTime && CheckForRightSplice(tess, regPrev))
                {
                    AddWinding(e, ePrev);
                    DeleteRegion(regPrev);
                    Mesh.DeleteHalfEdge(ePrev);
                }
                firstTime = false;
                regPrev = reg;
                ePrev = e;
            }
            regPrev.dirty = true;
            if (regPrev.windingNumber - e.winding != reg.windingNumber)
            {
                throw new Exception();
            }

            if (cleanUp)
            {
                /* Check for intersections between newly adjacent edges. */
                WalkDirtyRegions(tess, regPrev);
            }
        }
Esempio n. 15
0
 static bool FixUpperEdge(ActiveRegion reg, HalfEdge newEdge)
 /*
  * Replace an upper edge which needs fixing (see ConnectRightVertex).
  */
 {
     if (!reg.fixUpperEdge)
     {
         throw new Exception();
     }
     Mesh.DeleteHalfEdge(reg.upperHalfEdge);
     reg.fixUpperEdge = false;
     reg.upperHalfEdge = newEdge;
     newEdge.regionThisIsUpperEdgeOf = reg;
     return true;
 }
Esempio n. 16
0
        static void RenderFan(Tesselator tess, HalfEdge e, int size)
        {
            /* Render as many CCW triangles as possible in a fan starting from
            * edge "e".  The fan *should* contain exactly "size" triangles
            * (otherwise we've goofed up somewhere).
            */
            tess.CallBegin(Tesselator.TriangleListType.TriangleFan);
            tess.CallVertex(e.originVertex.clientIndex);
            tess.CallVertex(e.directionVertex.clientIndex);

            while (!e.leftFace.Marked())
            {
                e.leftFace.marked = true;
                --size;
                e = e.nextEdgeCCWAroundOrigin;
                tess.CallVertex(e.directionVertex.clientIndex);
            }

            if (size != 0)
            {
                throw new Exception();
            }
            tess.CallEnd();
        }
Esempio n. 17
0
        static void AddRightEdges(Tesselator tess, ActiveRegion regUp,
               HalfEdge eFirst, HalfEdge eLast, HalfEdge eTopLeft,
               bool cleanUp)
        /*
         * Purpose: insert right-going edges into the edge dictionary, and update
         * winding numbers and mesh connectivity appropriately.  All right-going
         * edges share a common origin vOrg.  Edges are inserted CCW starting at
         * eFirst; the last edge inserted is eLast.Oprev.  If vOrg has any
         * left-going edges already processed, then eTopLeft must be the edge
         * such that an imaginary upward vertical segment from vOrg would be
         * contained between eTopLeft.Oprev and eTopLeft; otherwise eTopLeft
         * should be null.
         */
        {
            ActiveRegion reg, regPrev;
            HalfEdge e, ePrev;
            bool firstTime = true;
            /* Insert the new right-going edges in the dictionary */
            e = eFirst;
            do
            {
                if (!e.originVertex.VertLeq(e.directionVertex))
                {
                    throw new Exception();
                }
                AddRegionBelow(tess, regUp, e.otherHalfOfThisEdge);
                e = e.nextEdgeCCWAroundOrigin;
            } while (e != eLast);
            /* Walk *all* right-going edges from e.Org, in the dictionary order,
             * updating the winding numbers of each region, and re-linking the mesh
             * edges to match the dictionary ordering (if necessary).
             */
            if (eTopLeft == null)
            {
                eTopLeft = RegionBelow(regUp).upperHalfEdge.Rprev;
            }
            regPrev = regUp;
            ePrev = eTopLeft;
            for (;;)
            {
                reg = RegionBelow(regPrev);
                e = reg.upperHalfEdge.otherHalfOfThisEdge;
                if (e.originVertex != ePrev.originVertex) break;
                if (e.nextEdgeCCWAroundOrigin != ePrev)
                {
                    /* Unlink e from its current position, and relink below ePrev */
                    Mesh.meshSplice(e.Oprev, e);
                    Mesh.meshSplice(ePrev.Oprev, e);
                }
                /* Compute the winding number and "inside" flag for the new regions */
                reg.windingNumber = regPrev.windingNumber - e.winding;
                reg.inside = tess.IsWindingInside(reg.windingNumber);
                /* Check for two outgoing edges with same slope -- process these
                 * before any intersection tests (see example in __gl_computeInterior).
                 */
                regPrev.dirty = true;
                if (!firstTime && CheckForRightSplice(tess, regPrev))
                {
                    AddWinding(e, ePrev);
                    DeleteRegion(regPrev);
                    Mesh.DeleteHalfEdge(ePrev);
                }
                firstTime = false;
                regPrev = reg;
                ePrev = e;
            }
            regPrev.dirty = true;
            if (regPrev.windingNumber - e.winding != reg.windingNumber)
            {
                throw new Exception();
            }

            if (cleanUp)
            {
                /* Check for intersections between newly adjacent edges. */
                WalkDirtyRegions(tess, regPrev);
            }
        }
Esempio n. 18
0
        static void RenderStrip(Tesselator tess, HalfEdge halfEdge, int size)
        {
            /* Render as many CCW triangles as possible in a strip starting from
            * edge "e".  The strip *should* contain exactly "size" triangles
            * (otherwise we've goofed up somewhere).
            */
            tess.CallBegin(Tesselator.TriangleListType.TriangleStrip);
            tess.CallVertex(halfEdge.originVertex.clientIndex);
            tess.CallVertex(halfEdge.directionVertex.clientIndex);

            while (!halfEdge.leftFace.Marked())
            {
                halfEdge.leftFace.marked = true;
                --size;
                halfEdge = halfEdge.Dprev;
                tess.CallVertex(halfEdge.originVertex.clientIndex);
                if (halfEdge.leftFace.Marked()) break;

                halfEdge.leftFace.marked = true;
                --size;
                halfEdge = halfEdge.nextEdgeCCWAroundOrigin;
                tess.CallVertex(halfEdge.directionVertex.clientIndex);
            }

            if (size != 0)
            {
                throw new Exception();
            }
            tess.CallEnd();
        }
Esempio n. 19
0
        /* face.TessellateMonoRegion() 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).
         */
        internal bool TessellateMonoRegion()
        {
            /* 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.
             */
            HalfEdge up = this.halfEdgeThisIsLeftFaceOf;

            if (up.nextEdgeCCWAroundLeftFace == up || up.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace == up)
            {
                throw new Exception();
            }

            for (; up.directionVertex.VertLeq(up.originVertex); up = up.Lprev)
            {
                ;
            }
            for (; up.originVertex.VertLeq(up.directionVertex); up = up.nextEdgeCCWAroundLeftFace)
            {
                ;
            }
            HalfEdge lo = up.Lprev;

            while (up.nextEdgeCCWAroundLeftFace != lo)
            {
                if (up.directionVertex.VertLeq(lo.originVertex))
                {
                    /* 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.nextEdgeCCWAroundLeftFace != up && (lo.nextEdgeCCWAroundLeftFace.EdgeGoesLeft() ||
                                                                  ContourVertex.EdgeSign(lo.originVertex, lo.directionVertex, lo.nextEdgeCCWAroundLeftFace.directionVertex) <= 0))
                    {
                        HalfEdge tempHalfEdge = Mesh.meshConnect(lo.nextEdgeCCWAroundLeftFace, lo);
                        lo = tempHalfEdge.otherHalfOfThisEdge;
                    }
                    lo = lo.Lprev;
                }
                else
                {
                    /* lo.Org is on the left.  We can make CCW triangles from up.Dst. */
                    while (lo.nextEdgeCCWAroundLeftFace != up && (up.Lprev.EdgeGoesRight() ||
                                                                  ContourVertex.EdgeSign(up.directionVertex, up.originVertex, up.Lprev.originVertex) >= 0))
                    {
                        HalfEdge tempHalfEdge = Mesh.meshConnect(up, up.Lprev);
                        up = tempHalfEdge.otherHalfOfThisEdge;
                    }
                    up = up.nextEdgeCCWAroundLeftFace;
                }
            }

            // Now lo.Org == up.Dst == the leftmost vertex.  The remaining region
            // can be tessellated in a fan from this leftmost vertex.
            if (lo.nextEdgeCCWAroundLeftFace == up)
            {
                throw new Exception();
            }
            while (lo.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace != up)
            {
                HalfEdge tempHalfEdge = Mesh.meshConnect(lo.nextEdgeCCWAroundLeftFace, lo);
                lo = tempHalfEdge.otherHalfOfThisEdge;
            }

            return(true);
        }
Esempio n. 20
0
        bool AddVertex(double x, double y, int data)
        {
            HalfEdge e;

            e = this.lastHalfEdge;
            if (e == null)
            {
                /* Make a self-loop (one vertex, one edge). */
                e = this.mesh.MakeEdge();
                Mesh.meshSplice(e, e.otherHalfOfThisEdge);
            }
            else
            {
                /* Create a new vertex and edge which immediately follow e
                * in the ordering around the left face.
                */
                if (Mesh.meshSplitEdge(e) == null)
                {
                    return false;
                }
                e = e.nextEdgeCCWAroundLeftFace;
            }

            /* The new vertex is now e.Org. */
            e.originVertex.clientIndex = data;
            e.originVertex.coords[0] = x;
            e.originVertex.coords[1] = y;

            /* The winding of an edge says how the winding number changes as we
            * cross from the edge''s right face to its left face.  We add the
            * vertices in such an order that a CCW contour will add +1 to
            * the winding number of the region inside the contour.
            */
            e.winding = 1;
            e.otherHalfOfThisEdge.winding = -1;

            this.lastHalfEdge = e;

            return true;
        }
Esempio n. 21
0
		/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the
		* origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives
		* a place to insert the new vertex in the global vertex list.  We insert
		* the new vertex *before* vNext so that algorithms which walk the vertex
		* list will not see the newly created vertices.
		*/

		private static void MakeVertex(ContourVertex newVertex, HalfEdge eOrig, ContourVertex vNext)
		{
			HalfEdge e;
			ContourVertex vPrev;
			ContourVertex vNew = newVertex;

			/* insert in circular doubly-linked list before vNext */
			vPrev = vNext.prevVertex;
			vNew.prevVertex = vPrev;
			vPrev.nextVertex = vNew;
			vNew.nextVertex = vNext;
			vNext.prevVertex = vNew;

			vNew.edgeThisIsOriginOf = eOrig;
			vNew.clientIndex = 0;
			/* leave coords, s, t undefined */

			/* fix other edges on this vertex loop */
			e = eOrig;
			do
			{
				e.originVertex = vNew;
				e = e.nextEdgeCCWAroundOrigin;
			} while (e != eOrig);
		}
Esempio n. 22
0
        FaceCount MaximumFan(HalfEdge eOrig)
        {
            /* eOrig.Lface is the face we want to render.  We want to find the size
            * of a maximal fan around eOrig.Org.  To do this we just walk around
            * the origin vertex as far as possible in both directions.
            */
            FaceCount newFace = new FaceCount(0, null, new FaceCount.RenderDelegate(RenderFan));
            Face trail = null;
            HalfEdge e;

            for (e = eOrig; !e.leftFace.Marked(); e = e.nextEdgeCCWAroundOrigin)
            {
                Face.AddToTrail(ref e.leftFace, ref trail);
                ++newFace.size;
            }
            for (e = eOrig; !e.rightFace.Marked(); e = e.Oprev)
            {
                Face f = e.rightFace;
                Face.AddToTrail(ref f, ref trail);
                e.rightFace = f;
                ++newFace.size;
            }
            newFace.eStart = e;

            Face.FreeTrail(ref trail);
            return newFace;
        }
Esempio n. 23
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;
			}
		}
Esempio n. 24
0
        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;
        }
Esempio n. 25
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);
		}
Esempio n. 26
0
 void RenderTriangle(Tesselator tess, HalfEdge e, int size)
 {
     /* Just add the triangle to a triangle list, so we can render all
     * the separate triangles at once.
     */
     if (size != 1)
     {
         throw new Exception();
     }
     Face.AddToTrail(ref e.leftFace, ref this.lonelyTriList);
 }
Esempio n. 27
0
		/* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
		* such that eNew == eOrg.Lnext.  The new vertex is eOrg.Dst == eNew.Org.
		* eOrg and eNew will have the same left face.
		*/

		public static HalfEdge meshSplitEdge(HalfEdge eOrg)
		{
			HalfEdge eNew;
			HalfEdge tempHalfEdge = meshAddEdgeVertex(eOrg);

			eNew = tempHalfEdge.otherHalfOfThisEdge;

			/* Disconnect eOrg from eOrg.Dst and connect it to eNew.Org */
			Splice(eOrg.otherHalfOfThisEdge, eOrg.otherHalfOfThisEdge.Oprev);
			Splice(eOrg.otherHalfOfThisEdge, eNew);

			/* Set the vertex and face information */
			eOrg.directionVertex = eNew.originVertex;
			eNew.directionVertex.edgeThisIsOriginOf = eNew.otherHalfOfThisEdge;	/* may have pointed to eOrg.Sym */
			eNew.rightFace = eOrg.rightFace;
			eNew.winding = eOrg.winding;	/* copy old winding information */
			eNew.otherHalfOfThisEdge.winding = eOrg.otherHalfOfThisEdge.winding;

			return eNew;
		}
Esempio n. 28
0
 public FaceCount(int _size, HalfEdge _eStart, Action <Tesselator, HalfEdge, int> _render)
 {
     size   = _size;
     eStart = _eStart;
     render = _render;
 }
Esempio n. 29
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;
		}
Esempio n. 30
0
            // routine to render this primitive

            public void CallRender(Tesselator tess, HalfEdge edge, int data)
            {
                render(tess, edge, data);
            }
Esempio n. 31
0
        /*
         * Invariants for the Edge Dictionary.
         * - each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2)
         *   at any valid location of the sweep event
         * - if EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2
         *   share a common endpoint
         * - for each e, e.Dst has been processed, but not e.Org
         * - each edge e satisfies VertLeq(e.Dst,currentSweepVertex) && VertLeq(currentSweepVertex,e.Org)
         *   where "currentSweepVertex" is the current sweep line event.
         * - no edge e has zero length
         *
         * Invariants for the Mesh (the processed portion).
         * - the portion of the mesh left of the sweep line is a planar graph,
         *   ie. there is *some* way to embed it in the plane
         * - no processed edge has zero length
         * - no two processed vertices have identical coordinates
         * - each "inside" region is monotone, ie. can be broken into two chains
         *   of monotonically increasing vertices according to VertLeq(v1,v2)
         *   - a non-invariant: these chains may intersect (very slightly)
         *
         * Invariants for the Sweep.
         * - if none of the edges incident to the currentSweepVertex vertex have an activeRegion
         *   (ie. none of these edges are in the edge dictionary), then the vertex
         *   has only right-going edges.
         * - if an edge is marked "fixUpperEdge" (it is a temporary edge introduced
         *   by ConnectRightVertex), then it is the only right-going edge from
         *   its associated vertex.  (This says that these edges exist only
         *   when it is necessary.)
         */

        //#undef	MAX
        //#undef	MIN
        //#define MAX(x,y)	((x) >= (y) ? (x) : (y))
        //#define MIN(x,y)	((x) <= (y) ? (x) : (y))

        /* When we merge two edges into one, we need to compute the combined
         * winding of the new edge.
         */

        static void AddWinding(HalfEdge eDst, HalfEdge eSrc)
        {
            eDst.winding += eSrc.winding;
            eDst.otherHalfOfThisEdge.winding += eSrc.otherHalfOfThisEdge.winding;
        }
Esempio n. 32
0
        private 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);
        }
Esempio n. 33
0
 static ActiveRegion AddRegionBelow(Tesselator tess,
                      ActiveRegion regAbove,
                      HalfEdge eNewUp)
 /*
  * Add a new active region to the sweep line, *somewhere* below "regAbove"
  * (according to where the new edge belongs in the sweep-line dictionary).
  * The upper edge of the new region will be "eNewUp".
  * Winding number and "inside" flag are not updated.
  */
 {
     ActiveRegion regNew = new ActiveRegion();
     regNew.upperHalfEdge = eNewUp;
     /* __gl_dictListInsertBefore */
     regNew.upperHalfEdgeDictNode = tess.edgeDictionary.InsertBefore(regAbove.upperHalfEdgeDictNode, regNew);
     regNew.fixUpperEdge = false;
     regNew.sentinel = false;
     regNew.dirty = false;
     eNewUp.regionThisIsUpperEdgeOf = regNew;
     return regNew;
 }
Esempio n. 34
0
        /* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency.
         */
        public void CheckMesh()
        {
            Face          fHead = this.faceHead;
            ContourVertex vHead = this.vertexHead;
            HalfEdge      eHead = this.halfEdgeHead;
            Face          f, fPrev;
            ContourVertex v, vPrev;
            HalfEdge      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();
            }
        }
Esempio n. 35
0
 static void SpliceMergeVertices(Tesselator tess, HalfEdge e1, HalfEdge e2)
 /*
  * Two vertices with idential coordinates are combined into one.
  * e1.Org is kept, while e2.Org is discarded.
  */
 {
     int[] data4 = new int[4];
     double[] weights4 = new double[] { 0.5f, 0.5f, 0, 0 };
     data4[0] = e1.originVertex.clientIndex;
     data4[1] = e2.originVertex.clientIndex;
     CallCombine(tess, e1.originVertex, data4, weights4, false);
     Mesh.meshSplice(e1, e2);
 }
Esempio n. 36
0
        /*
         * Two vertices with idential coordinates are combined into one.
         * e1.Org is kept, while e2.Org is discarded.
         */
        static void SpliceMergeVertices(Tesselator tess, HalfEdge e1, HalfEdge e2)
        {
            int[] data4 = new int[4];
            double[] weights4 = new double[] { 0.5f, 0.5f, 0, 0 };

            data4[0] = e1.originVertex.clientIndex;
            data4[1] = e2.originVertex.clientIndex;
            CallCombine(tess, e1.originVertex, data4, weights4, false);
            Mesh.meshSplice(e1, e2);
        }