Пример #1
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.
         */
        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);
        }
Пример #2
0
 public static bool VertCCW(ContourVertex <T> u, ContourVertex <T> v, ContourVertex <T> w)
 {
     /* For almost-degenerate situations, the results are not reliable.
      * Unless the floating-point arithmetic can be performed without
      * rounding errors, *any* implementation will give incorrect results
      * on some degenerate inputs, so the client must have some way to
      * handle this situation.
      */
     return((u.x.Multiply(v.y.Subtract(w.y)).Add(v.x.Multiply(w.y.Subtract(u.y))).Add(w.x.Multiply(u.y.Subtract(v.y)))).GreaterThanOrEqualTo(0));
 }
Пример #3
0
        public C5.IPriorityQueueHandle <ContourVertex <T> > priorityQueueHandle; /* to allow deletion from priority queue */

        public int CompareTo(ContourVertex <T> otherVertex)
        {
            if (VertEq(otherVertex))
            {
                return(0);
            }
            if (this.VertLeq(otherVertex))
            {
                return(-1);
            }
            return(1);
        }
Пример #4
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);
        }
Пример #5
0
        private void ProjectPolygon()
        {
            ContourVertex v, vHead = this.mesh.vertexHead;

            // Project the vertices onto the sweep plane
            for (v = vHead.nextVertex; v != vHead; v = v.nextVertex)
            {
                v.x = v.coords[0];
                v.y = -v.coords[1];
            }

            CheckOrientation();
        }
Пример #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;
            }
        }
Пример #7
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.
         */
        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);
        }
Пример #8
0
        /* KillVertex( vDel ) destroys a vertex and removes it from the global
         * vertex list.  It updates the vertex loop to point to a given new vertex.
         */
        static void KillVertex(ContourVertex vDel, ContourVertex newOrg)
        {
            HalfEdge      e, eStart = vDel.edgeThisIsOriginOf;
            ContourVertex vPrev, vNext;

            /* change the origin of all affected edges */
            e = eStart;
            do
            {
                e.originVertex = newOrg;
                e = e.nextEdgeCCWAroundOrigin;
            } while (e != eStart);
            /* delete from circular doubly-linked list */
            vPrev            = vDel.prevVertex;
            vNext            = vDel.nextVertex;
            vNext.prevVertex = vPrev;
            vPrev.nextVertex = vNext;
        }
Пример #9
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 originalEdge)
        {
            HalfEdge newEdgeOtherHalf;
            HalfEdge newEdge = MakeEdge(originalEdge);

            newEdgeOtherHalf = newEdge.otherHalfOfThisEdge;

            /* Connect the new edge appropriately */
            Splice(newEdge, originalEdge.nextEdgeCCWAroundLeftFace);

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

                MakeVertex(newVertex, newEdgeOtherHalf, newEdge.originVertex);
            }
            newEdge.leftFace = newEdgeOtherHalf.leftFace = originalEdge.leftFace;

            return(newEdge);
        }
Пример #10
0
        public static T EdgeEval(ContourVertex <T> u, ContourVertex <T> v, ContourVertex <T> w)
        {
            /* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w),
             * evaluates the t-coord of the edge uw at the s-coord of the vertex v.
             * Returns v.t - (uw)(v.s), ie. the signed distance from uw to v.
             * If uw is vertical (and thus passes thru v), the result is zero.
             *
             * The calculation is extremely accurate and stable, even when v
             * is very close to u or w.  In particular if we set v.t = 0 and
             * let r be the negated result (this evaluates (uw)(v.s)), then
             * r is guaranteed to satisfy MIN(u.t,w.t) <= r <= MAX(u.t,w.t).
             */
            T gapL, gapR;

            if (!((u.VertLeq(u) && v.VertLeq(v))))
            {
                throw new Exception();
            }

            gapL = v.x.Subtract(u.x);
            gapR = w.x.Subtract(v.x);

            if (gapL.Add(gapR).GreaterThan(0))
            {
                if (gapL.LessThan(gapR))
                {
                    return(v.y.Subtract(u.y).Add(
                               u.y.Subtract(w.y).Multiply(
                                   gapL.Divide(gapL.Add(gapR)))));
                }
                else
                {
                    return(v.y.Subtract(w.y).Add(w.y.Subtract(u.y).Multiply(gapR.Divide(gapL.Add(gapR)))));
                }
            }

            // vertical line
            return(M.Zero <T>());
        }
Пример #11
0
        static public T EdgeSign(ContourVertex <T> u, ContourVertex <T> v, ContourVertex <T> w)
        {
            /* Returns a number whose sign matches EdgeEval(u,w) but which
             * is cheaper to evaluate.  Returns > 0, == 0 , or < 0
             * as v is above, on, or below the edge uw.
             */
            T gapL, gapR;

            if (!u.VertLeq(v) || !v.VertLeq(w))
            {
                throw new System.Exception();
            }

            gapL = v.x.Subtract(u.x);
            gapR = w.x.Subtract(v.x);

            if (gapL.Add(gapR).GreaterThan(0))
            {
                return(v.y.Subtract(w.y).Multiply(gapL).Add(v.y.Subtract(u.y).Multiply(gapR)));
            }
            /* vertical line */
            return(M.Zero <T>());
        }
Пример #12
0
        /* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in
         * both meshes, and returns the new mesh (the old meshes are destroyed).
         */
        Mesh meshUnion(Mesh mesh1, Mesh mesh2)
        {
            Face          f1 = mesh1.faceHead;
            ContourVertex v1 = mesh1.vertexHead;
            HalfEdge      e1 = mesh1.halfEdgeHead;
            Face          f2 = mesh2.faceHead;
            ContourVertex v2 = mesh2.vertexHead;
            HalfEdge      e2 = mesh2.halfEdgeHead;

            /* Add the faces, vertices, and edges of mesh2 to those of mesh1 */
            if (f2.nextFace != f2)
            {
                f1.prevFace.nextFace = f2.nextFace;
                f2.nextFace.prevFace = f1.prevFace;
                f2.prevFace.nextFace = f1;
                f1.prevFace          = f2.prevFace;
            }

            if (v2.nextVertex != v2)
            {
                v1.prevVertex.nextVertex = v2.nextVertex;
                v2.nextVertex.prevVertex = v1.prevVertex;
                v2.prevVertex.nextVertex = v1;
                v1.prevVertex            = v2.prevVertex;
            }

            if (e2.nextHalfEdge != e2)
            {
                e1.otherHalfOfThisEdge.nextHalfEdge.otherHalfOfThisEdge.nextHalfEdge = e2.nextHalfEdge;
                e2.nextHalfEdge.otherHalfOfThisEdge.nextHalfEdge = e1.otherHalfOfThisEdge.nextHalfEdge;
                e2.otherHalfOfThisEdge.nextHalfEdge.otherHalfOfThisEdge.nextHalfEdge = e1;
                e1.otherHalfOfThisEdge.nextHalfEdge = e2.otherHalfOfThisEdge.nextHalfEdge;
            }

            mesh2 = null;
            return(mesh1);
        }
Пример #13
0
        private void CheckOrientation()
        {
            double        area;
            Face          curFace, faceHead = this.mesh.faceHead;
            ContourVertex vHead = this.mesh.vertexHead;
            HalfEdge      curHalfEdge;

            /* When we compute the normal automatically, we choose the orientation
             * so that the sum of the signed areas of all contours is non-negative.
             */
            area = 0;
            for (curFace = faceHead.nextFace; curFace != faceHead; curFace = curFace.nextFace)
            {
                curHalfEdge = curFace.halfEdgeThisIsLeftFaceOf;
                if (curHalfEdge.winding <= 0)
                {
                    continue;
                }

                do
                {
                    area += (curHalfEdge.originVertex.x - curHalfEdge.directionVertex.x)
                            * (curHalfEdge.originVertex.y + curHalfEdge.directionVertex.y);
                    curHalfEdge = curHalfEdge.nextEdgeCCWAroundLeftFace;
                } while (curHalfEdge != curFace.halfEdgeThisIsLeftFaceOf);
            }

            if (area < 0)
            {
                /* Reverse the orientation by flipping all the t-coordinates */
                for (ContourVertex curVertex = vHead.nextVertex; curVertex != vHead; curVertex = curVertex.nextVertex)
                {
                    curVertex.y = -curVertex.y;
                }
            }
        }
Пример #14
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();
            }
        }
Пример #15
0
 public bool TransLeq(ContourVertex <T> v)
 {
     return((this.y.LessThan(v.y)) || (this.y.Equals(v.y) && this.x.LessThanOrEqualTo(v.x)));
 }
Пример #16
0
 public bool VertLeq(ContourVertex <T> v)
 {
     return((this.x.LessThan(v.x)) || (this.x.Equals(v.x) && this.y.LessThan(v.y)));
 }
Пример #17
0
 public bool VertEq(ContourVertex <T> v)
 {
     return((this.x.Equals(v.x)) && this.y.Equals(v.y));
 }
Пример #18
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 <T> 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 <T> 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 <T> .EdgeSign(lo.originVertex, lo.directionVertex, lo.nextEdgeCCWAroundLeftFace.directionVertex).LessThanOrEqualTo(0)))
                    {
                        HalfEdge <T> tempHalfEdge = Mesh <T> .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 <T> .EdgeSign(up.directionVertex, up.originVertex, up.Lprev.originVertex).GreaterThanOrEqualTo(0)))
                    {
                        HalfEdge <T> tempHalfEdge = Mesh <T> .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 <T> tempHalfEdge = Mesh <T> .meshConnect(lo.nextEdgeCCWAroundLeftFace, lo);

                lo = tempHalfEdge.otherHalfOfThisEdge;
            }

            return(true);
        }
Пример #19
0
 public bool Equal2D(ContourVertex <T> OtherVertex)
 {
     return(this.x.Equals(OtherVertex.x) && this.y.Equals(OtherVertex.y));
 }