예제 #1
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.
  */
 private static void AddWinding(HalfEdge eDst, HalfEdge eSrc)
 {
     eDst.winding += eSrc.winding;
     eDst.otherHalfOfThisEdge.winding += eSrc.otherHalfOfThisEdge.winding;
 }
예제 #2
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.
         */
        private 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);
        }
예제 #3
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;
            {
                var newVertex = new ContourVertex();

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

            return eNew;
        }
예제 #4
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;
            }
예제 #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;
        }
예제 #6
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);
        }
예제 #7
0
        private 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.
            */
            var newFace = new FaceCount(0, null, 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;
        }
예제 #8
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;
            }
        }
예제 #9
0
        private 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(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();
        }
예제 #10
0
        private bool AddVertex(double x, double y, int data)
        {
            HalfEdge e;

            e = lastHalfEdge;
            if (e == null)
            {
                /* Make a self-loop (one vertex, one edge). */
                e = 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;

            lastHalfEdge = e;

            return true;
        }
예제 #11
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;
            }
        }
예제 #12
0
        public void AddVertex(double[] coords3, int data)
        {
            int i;
            double x;
            var 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 + ".");
                }
                if (x > MAX_COORD)
                {
                    throw new Exception("Your coordinate exceeded " + MAX_COORD + ".");
                }
                clamped[i] = x;
            }

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

            AddVertex(clamped[0], clamped[1], data);
        }
예제 #13
0
 // routine to render this primitive
 public void CallRender(Tesselator tess, HalfEdge edge, int data)
 {
     render(tess, edge, data);
 }
예제 #14
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);
        }
예제 #15
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.
            */
            var 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;
        }
예제 #16
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;
        }
예제 #17
0
        /*
         * Replace an upper edge which needs fixing (see ConnectRightVertex).
         */
        private static bool FixUpperEdge(ActiveRegion reg, HalfEdge newEdge)
        {
            if (!reg.fixUpperEdge)
            {
                throw new Exception();
            }
            Mesh.DeleteHalfEdge(reg.upperHalfEdge);
            reg.fixUpperEdge = false;
            reg.upperHalfEdge = newEdge;
            newEdge.regionThisIsUpperEdgeOf = reg;

            return true;
        }
예제 #18
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;
        }
예제 #19
0
        /*
         * Two vertices with idential coordinates are combined into one.
         * e1.Org is kept, while e2.Org is discarded.
         */
        private static void SpliceMergeVertices(Tesselator tess, HalfEdge e1, HalfEdge e2)
        {
            var data4 = new int[4];
            var 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);
        }
예제 #20
0
        /* Allocate and free half-edges in pairs for efficiency.
        * The *only* place that should use this fact is allocation/free.
        */
        /* 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;
            var 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;
        }
예제 #21
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.
         */
        private static ActiveRegion AddRegionBelow(Tesselator tess,
                                                   ActiveRegion regAbove,
                                                   HalfEdge eNewUp)
        {
            var 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;
        }
예제 #22
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);
        }
예제 #23
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.
         */
        private 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);
            }
        }
예제 #24
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;
        }
예제 #25
0
 private 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 lonelyTriList);
 }