Пример #1
0
        /*
         * Check the upper and lower edge of "regUp", to make sure that the
         * eUp.Org is above eLo, or eLo.Org is below eUp (depending on which
         * origin is leftmost).
         *
         * The main purpose is to splice right-going edges with the same
         * dest vertex and nearly identical slopes (ie. we can't distinguish
         * the slopes numerically).  However the splicing can also help us
         * to recover from numerical errors.  For example, suppose at one
         * point we checked eUp and eLo, and decided that eUp.Org is barely
         * above eLo.  Then later, we split eLo into two edges (eg. from
         * a splice operation like this one).  This can change the result of
         * our test so that now eUp.Org is incident to eLo, or barely below it.
         * We must correct this condition to maintain the dictionary invariants.
         *
         * One possibility is to check these edges for intersection again
         * (ie. CheckForIntersect).  This is what we do if possible.  However
         * CheckForIntersect requires that tess.currentSweepVertex lies between eUp and eLo,
         * so that it has something to fall back on when the intersection
         * calculation gives us an unusable answer.  So, for those cases where
         * we can't check for intersection, this routine fixes the problem
         * by just splicing the offending vertex into the other edge.
         * This is a guaranteed solution, no matter how degenerate things get.
         * Basically this is a combinatorial solution to a numerical problem.
         */
        private static bool CheckForRightSplice(Tesselator tess, ActiveRegion regUp)
        {
            ActiveRegion regLo = RegionBelow(regUp);
            HalfEdge eUp = regUp.upperHalfEdge;
            HalfEdge eLo = regLo.upperHalfEdge;

            if (eUp.originVertex.VertLeq(eLo.originVertex))
            {
                if (ContourVertex.EdgeSign(eLo.directionVertex, eUp.originVertex, eLo.originVertex) > 0)
                {
                    return false;
                }

                /* eUp.Org appears to be below eLo */
                if (!eUp.originVertex.VertEq(eLo.originVertex))
                {
                    /* Splice eUp.Org into eLo */
                    Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge);
                    Mesh.meshSplice(eUp, eLo.Oprev);
                    regUp.dirty = regLo.dirty = true;
                }
                else if (eUp.originVertex != eLo.originVertex)
                {
                    /* merge the two vertices, discarding eUp.Org */
                    tess.vertexPriorityQue.Delete(eUp.originVertex.priorityQueueHandle);
                    //pqDelete(tess.pq, eUp.Org.pqHandle); /* __gl_pqSortDelete */
                    SpliceMergeVertices(tess, eLo.Oprev, eUp);
                }
            }
            else
            {
                if (ContourVertex.EdgeSign(eUp.directionVertex, eLo.originVertex, eUp.originVertex) < 0)
                {
                    return false;
                }

                /* eLo.Org appears to be above eUp, so splice eLo.Org into eUp */
                regUp.RegionAbove().dirty = regUp.dirty = true;
                Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge);
                Mesh.meshSplice(eLo.Oprev, eUp);
            }
            return true;
        }
Пример #2
0
 private static void ComputeWinding(Tesselator tess, ActiveRegion reg)
 {
     reg.windingNumber = reg.RegionAbove().windingNumber + reg.upperHalfEdge.winding;
     reg.inside = tess.IsWindingInside(reg.windingNumber);
 }
Пример #3
0
        /*
         * Check the upper and lower edge of "regUp", to make sure that the
         * eUp.Dst is above eLo, or eLo.Dst is below eUp (depending on which
         * destination is rightmost).
         *
         * Theoretically, this should always be true.  However, splitting an edge
         * into two pieces can change the results of previous tests.  For example,
         * suppose at one point we checked eUp and eLo, and decided that eUp.Dst
         * is barely above eLo.  Then later, we split eLo into two edges (eg. from
         * a splice operation like this one).  This can change the result of
         * the test so that now eUp.Dst is incident to eLo, or barely below it.
         * We must correct this condition to maintain the dictionary invariants
         * (otherwise new edges might get inserted in the wrong place in the
         * dictionary, and bad stuff will happen).
         *
         * We fix the problem by just splicing the offending vertex into the
         * other edge.
         */
        private static bool CheckForLeftSplice(Tesselator tess, ActiveRegion regUp)
        {
            ActiveRegion regLo = RegionBelow(regUp);
            HalfEdge eUp = regUp.upperHalfEdge;
            HalfEdge eLo = regLo.upperHalfEdge;
            HalfEdge e;

            if (eUp.directionVertex.VertEq(eLo.directionVertex))
            {
                throw new Exception();
            }

            if (eUp.directionVertex.VertLeq(eLo.directionVertex))
            {
                if (ContourVertex.EdgeSign(eUp.directionVertex, eLo.directionVertex, eUp.originVertex) < 0)
                {
                    return false;
                }

                /* eLo.Dst is above eUp, so splice eLo.Dst into eUp */
                regUp.RegionAbove().dirty = regUp.dirty = true;
                e = Mesh.meshSplitEdge(eUp);
                Mesh.meshSplice(eLo.otherHalfOfThisEdge, e);
                e.leftFace.isInterior = regUp.inside;
            }
            else
            {
                if (ContourVertex.EdgeSign(eLo.directionVertex, eUp.directionVertex, eLo.originVertex) > 0)
                    return false;

                /* eUp.Dst is below eLo, so splice eUp.Dst into eLo */
                regUp.dirty = regLo.dirty = true;
                e = Mesh.meshSplitEdge(eLo);
                Mesh.meshSplice(eUp.nextEdgeCCWAroundLeftFace, eLo.otherHalfOfThisEdge);
                e.rightFace.isInterior = regUp.inside;
            }
            return true;
        }
Пример #4
0
        /*
         * Check the upper and lower edges of the given region to see if
         * they intersect.  If so, create the intersection and add it
         * to the data structures.
         *
         * Returns true if adding the new intersection resulted in a recursive
         * call to AddRightEdges(); in this case all "dirty" regions have been
         * checked for intersections, and possibly regUp has been deleted.
         */
        private static bool CheckForIntersect(Tesselator tess, ActiveRegion regUp)
        {
            ActiveRegion regLo = RegionBelow(regUp);
            HalfEdge eUp = regUp.upperHalfEdge;
            HalfEdge eLo = regLo.upperHalfEdge;
            ContourVertex orgUp = eUp.originVertex;
            ContourVertex orgLo = eLo.originVertex;
            ContourVertex dstUp = eUp.directionVertex;
            ContourVertex dstLo = eLo.directionVertex;
            double tMinUp, tMaxLo;
            var isect = new ContourVertex();
            ContourVertex orgMin;
            HalfEdge e;

            if (dstLo.VertEq(dstUp))
            {
                throw new Exception();
            }
            if (ContourVertex.EdgeSign(dstUp, tess.currentSweepVertex, orgUp) > 0)
            {
                throw new Exception();
            }
            if (ContourVertex.EdgeSign(dstLo, tess.currentSweepVertex, orgLo) < 0)
            {
                throw new Exception();
            }
            if (orgUp == tess.currentSweepVertex || orgLo == tess.currentSweepVertex)
            {
                throw new Exception();
            }
            if (regUp.fixUpperEdge || regLo.fixUpperEdge)
            {
                throw new Exception();
            }

            if (orgUp == orgLo)
            {
                return false; /* right endpoints are the same */
            }

            tMinUp = Math.Min(orgUp.y, dstUp.y);
            tMaxLo = Math.Max(orgLo.y, dstLo.y);
            if (tMinUp > tMaxLo)
            {
                return false; /* t ranges do not overlap */
            }

            if (orgUp.VertLeq(orgLo))
            {
                if (ContourVertex.EdgeSign(dstLo, orgUp, orgLo) > 0)
                {
                    return false;
                }
            }
            else
            {
                if (ContourVertex.EdgeSign(dstUp, orgLo, orgUp) < 0)
                {
                    return false;
                }
            }

            EdgeIntersect(dstUp, orgUp, dstLo, orgLo, ref isect);
            // The following properties are guaranteed:
            if (!(Math.Min(orgUp.y, dstUp.y) <= isect.y))
            {
                throw new Exception();
            }
            if (!(isect.y <= Math.Max(orgLo.y, dstLo.y)))
            {
                throw new Exception();
            }
            if (!(Math.Min(dstLo.x, dstUp.x) <= isect.x))
            {
                throw new Exception();
            }
            if (!(isect.x <= Math.Max(orgLo.x, orgUp.x)))
            {
                throw new Exception();
            }

            if (isect.VertLeq(tess.currentSweepVertex))
            {
                /* The intersection point lies slightly to the left of the sweep line,
                 * so move it until it''s slightly to the right of the sweep line.
                 * (If we had perfect numerical precision, this would never happen
                 * in the first place).  The easiest and safest thing to do is
                 * replace the intersection by tess.currentSweepVertex.
                 */
                isect.x = tess.currentSweepVertex.x;
                isect.y = tess.currentSweepVertex.y;
            }
            /* Similarly, if the computed intersection lies to the right of the
             * rightmost origin (which should rarely happen), it can cause
             * unbelievable inefficiency on sufficiently degenerate inputs.
             * (If you have the test program, try running test54.d with the
             * "X zoom" option turned on).
             */
            orgMin = orgUp.VertLeq(orgLo) ? orgUp : orgLo;
            if (orgMin.VertLeq(isect))
            {
                isect.x = orgMin.x;
                isect.y = orgMin.y;
            }

            if (isect.VertEq(orgUp) || isect.VertEq(orgLo))
            {
                /* Easy case -- intersection at one of the right endpoints */
                CheckForRightSplice(tess, regUp);
                return false;
            }

            if ((!dstUp.VertEq(tess.currentSweepVertex)
                 && ContourVertex.EdgeSign(dstUp, tess.currentSweepVertex, isect) >= 0)
                || (!dstLo.VertEq(tess.currentSweepVertex)
                    && ContourVertex.EdgeSign(dstLo, tess.currentSweepVertex, isect) <= 0))
            {
                /* Very unusual -- the new upper or lower edge would pass on the
                 * wrong side of the sweep currentSweepVertex, or through it.  This can happen
                 * due to very small numerical errors in the intersection calculation.
                 */
                if (dstLo == tess.currentSweepVertex)
                {
                    /* Splice dstLo into eUp, and process the new region(s) */
                    Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge);
                    Mesh.meshSplice(eLo.otherHalfOfThisEdge, eUp);
                    regUp = TopLeftRegion(regUp);
                    eUp = RegionBelow(regUp).upperHalfEdge;
                    FinishLeftRegions(tess, RegionBelow(regUp), regLo);
                    AddRightEdges(tess, regUp, eUp.Oprev, eUp, eUp, true);
                    return true;
                }
                if (dstUp == tess.currentSweepVertex)
                {
                    /* Splice dstUp into eLo, and process the new region(s) */
                    Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge);
                    Mesh.meshSplice(eUp.nextEdgeCCWAroundLeftFace, eLo.Oprev);
                    regLo = regUp;
                    regUp = TopRightRegion(regUp);
                    e = RegionBelow(regUp).upperHalfEdge.Rprev;
                    regLo.upperHalfEdge = eLo.Oprev;
                    eLo = FinishLeftRegions(tess, regLo, null);
                    AddRightEdges(tess, regUp, eLo.nextEdgeCCWAroundOrigin, eUp.Rprev, e, true);
                    return true;
                }

                /* Special case: called from ConnectRightVertex.  If either
                 * edge passes on the wrong side of tess.currentSweepVertex, split it
                 * (and wait for ConnectRightVertex to splice it appropriately).
                 */
                if (ContourVertex.EdgeSign(dstUp, tess.currentSweepVertex, isect) >= 0)
                {
                    regUp.RegionAbove().dirty = regUp.dirty = true;
                    Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge);
                    eUp.originVertex.x = tess.currentSweepVertex.x;
                    eUp.originVertex.y = tess.currentSweepVertex.y;
                }
                if (ContourVertex.EdgeSign(dstLo, tess.currentSweepVertex, isect) <= 0)
                {
                    regUp.dirty = regLo.dirty = true;
                    Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge);
                    eLo.originVertex.x = tess.currentSweepVertex.x;
                    eLo.originVertex.y = tess.currentSweepVertex.y;
                }
                /* leave the rest for ConnectRightVertex */
                return false;
            }

            /* General case -- split both edges, splice into new vertex.
             * When we do the splice operation, the order of the arguments is
             * arbitrary as far as correctness goes.  However, when the operation
             * creates a new face, the work done is proportional to the size of
             * the new face.  We expect the faces in the processed part of
             * the mesh (ie. eUp.Lface) to be smaller than the faces in the
             * unprocessed original contours (which will be eLo.Oprev.Lface).
             */
            Mesh.meshSplitEdge(eUp.otherHalfOfThisEdge);
            Mesh.meshSplitEdge(eLo.otherHalfOfThisEdge);
            Mesh.meshSplice(eLo.Oprev, eUp);
            eUp.originVertex.x = isect.x;
            eUp.originVertex.y = isect.y;
            tess.vertexPriorityQue.Add(ref eUp.originVertex.priorityQueueHandle, eUp.originVertex);
                /* __gl_pqSortInsert */
            GetIntersectData(tess, eUp.originVertex, orgUp, dstUp, orgLo, dstLo);
            regUp.RegionAbove().dirty = regUp.dirty = regLo.dirty = true;
            return false;
        }
Пример #5
0
        /*
         * When the upper or lower edge of any region changes, the region is
         * marked "dirty".  This routine walks through all the dirty regions
         * and makes sure that the dictionary invariants are satisfied
         * (see the comments at the beginning of this file).  Of course
         * new dirty regions can be created as we make changes to restore
         * the invariants.
         */
        private static void WalkDirtyRegions(Tesselator tess, ActiveRegion regUp)
        {
            ActiveRegion regLo = RegionBelow(regUp);
            HalfEdge eUp, eLo;

            for (;;)
            {
                /* Find the lowest dirty region (we walk from the bottom up). */
                while (regLo.dirty)
                {
                    regUp = regLo;
                    regLo = RegionBelow(regLo);
                }
                if (!regUp.dirty)
                {
                    regLo = regUp;
                    regUp = regUp.RegionAbove();
                    if (regUp == null || !regUp.dirty)
                    {
                        /* We've walked all the dirty regions */
                        return;
                    }
                }
                regUp.dirty = false;
                eUp = regUp.upperHalfEdge;
                eLo = regLo.upperHalfEdge;

                if (eUp.directionVertex != eLo.directionVertex)
                {
                    /* Check that the edge ordering is obeyed at the Dst vertices. */
                    if (CheckForLeftSplice(tess, regUp))
                    {
                        /* If the upper or lower edge was marked fixUpperEdge, then
                         * we no longer need it (since these edges are needed only for
                         * vertices which otherwise have no right-going edges).
                         */
                        if (regLo.fixUpperEdge)
                        {
                            DeleteRegion(regLo);
                            Mesh.DeleteHalfEdge(eLo);
                            regLo = RegionBelow(regUp);
                            eLo = regLo.upperHalfEdge;
                        }
                        else if (regUp.fixUpperEdge)
                        {
                            DeleteRegion(regUp);
                            Mesh.DeleteHalfEdge(eUp);
                            regUp = regLo.RegionAbove();
                            eUp = regUp.upperHalfEdge;
                        }
                    }
                }
                if (eUp.originVertex != eLo.originVertex)
                {
                    if (eUp.directionVertex != eLo.directionVertex
                        && !regUp.fixUpperEdge && !regLo.fixUpperEdge
                        &&
                        (eUp.directionVertex == tess.currentSweepVertex ||
                         eLo.directionVertex == tess.currentSweepVertex))
                    {
                        /* When all else fails in CheckForIntersect(), it uses tess.currentSweepVertex
                         * as the intersection location.  To make this possible, it requires
                         * that tess.currentSweepVertex lie between the upper and lower edges, and also
                         * that neither of these is marked fixUpperEdge (since in the worst
                         * case it might splice one of these edges into tess.currentSweepVertex, and
                         * violate the invariant that fixable edges are the only right-going
                         * edge from their associated vertex).
                         */
                        if (CheckForIntersect(tess, regUp))
                        {
                            /* WalkDirtyRegions() was called recursively; we're done */
                            return;
                        }
                    }
                    else
                    {
                        /* Even though we can't use CheckForIntersect(), the Org vertices
                         * may violate the dictionary edge ordering.  Check and correct this.
                         */
                        CheckForRightSplice(tess, regUp);
                    }
                }
                if (eUp.originVertex == eLo.originVertex && eUp.directionVertex == eLo.directionVertex)
                {
                    /* A degenerate loop consisting of only two edges -- delete it. */
                    AddWinding(eLo, eUp);
                    DeleteRegion(regUp);
                    Mesh.DeleteHalfEdge(eUp);
                    regUp = regLo.RegionAbove();
                }
            }
        }
Пример #6
0
        private static ActiveRegion TopRightRegion(ActiveRegion reg)
        {
            ContourVertex dst = reg.upperHalfEdge.directionVertex;

            /* Find the region above the uppermost edge with the same destination */
            do
            {
                reg = reg.RegionAbove();
            } while (reg.upperHalfEdge.directionVertex == dst);
            return reg;
        }
Пример #7
0
        private static ActiveRegion TopLeftRegion(ActiveRegion reg)
        {
            ContourVertex org = reg.upperHalfEdge.originVertex;
            HalfEdge e;

            /* Find the region above the uppermost edge with the same origin */
            do
            {
                reg = reg.RegionAbove();
            } while (reg.upperHalfEdge.originVertex == org);

            /* If the edge above was a temporary edge introduced by ConnectRightVertex,
             * now is the time to fix it.
             */
            if (reg.fixUpperEdge)
            {
                e = Mesh.meshConnect(RegionBelow(reg).upperHalfEdge.otherHalfOfThisEdge,
                                     reg.upperHalfEdge.nextEdgeCCWAroundLeftFace);
                if (e == null)
                {
                    return null;
                }
                if (!FixUpperEdge(reg, e))
                {
                    return null;
                }
                reg = reg.RegionAbove();
            }
            return reg;
        }