private static List <Point> GetResult(Partition partition)
        {
            var      result   = new List <Point>();
            QuadEdge baseEdge = QuadEdge.ConnectLeft(partition.Left.Inverse, partition.Right);
            QuadEdge e        = baseEdge.DestinationNext.Inverse;
            Point    u        = baseEdge.Destination;

            while (true)
            {
                while (e != baseEdge && !CanMoveForward(e.DestinationNext, baseEdge))
                {
                    u = e.Destination;
                    e = e.DestinationNext.Inverse;
                }

                if (e != baseEdge)
                {
                    result.Add(e.Origin);
                    result.Add(e.Destination);
                }

                while (!CanMoveForward(e.OriginNext, baseEdge))
                {
                    result.Add(u);
                    if (u == baseEdge.Destination)
                    {
                        baseEdge.Delete();
                        return(result);
                    }
                    e = e.OriginNext.Inverse;
                    while (CanMoveForward(e.DestinationNext, baseEdge))
                    {
                        e = e.DestinationNext;
                    }
                    u = e.Origin;

                    result.Add(e.Origin);
                    result.Add(e.Destination);
                }
                e = e.OriginNext;
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Triangulate the set of points using a divide and conquer approach.
        /// </summary>
        private QuadEdge <T>[] Triangulate(Vec3[] pts)
        {
            QuadEdge <T> a, b, c, nextCand;

            // Only two points -> One edge
            if (pts.Length == 2)
            {
                a = QuadEdge <T> .MakeEdge(pts[0], pts[1]);

                return(new QuadEdge <T>[] { a, a.Sym });
            }
            // Only tree points
            else if (pts.Length == 3)
            {
                a = QuadEdge <T> .MakeEdge(pts[0], pts[1]);

                b = QuadEdge <T> .MakeEdge(pts[1], pts[2]);

                QuadEdge <T> .Splice(a.Sym, b);

                // Closing triangle
                if (Geometry.Ccw(pts[0], pts[1], pts[2]))
                {
                    c = QuadEdge <T> .Connect(b, a);

                    return(new QuadEdge <T>[] { a, b.Sym });
                }
                else if (Geometry.Ccw(pts[0], pts[2], pts[1]))
                {
                    c = QuadEdge <T> .Connect(b, a);

                    return(new QuadEdge <T>[] { c.Sym, c });
                }
                else
                {
                    // Points are collinear
                    return(new QuadEdge <T>[] { a, b.Sym });
                }
            }

            // SPLITTING
            // Divide them halfsize recursively
            int halfLength = (pts.Length + 1) / 2;

            QuadEdge <T>[] left  = Triangulate(pts.Take(halfLength).ToArray());
            QuadEdge <T>[] right = Triangulate(pts.Skip(halfLength).ToArray());


            // MERGING
            // From left to right
            QuadEdge <T> ldo = left[0];
            QuadEdge <T> ldi = left[1];
            QuadEdge <T> rdi = right[0];
            QuadEdge <T> rdo = right[1];

            // Compute lower common tangent to be able to merge both triangulations
            bool crossEdgeNotFound = true;

            while (crossEdgeNotFound)
            {
                if (Geometry.LeftOf(rdi.Origin, ldi))
                {
                    ldi = ldi.Lnext;
                }
                else if (Geometry.RightOf(ldi.Origin, rdi))
                {
                    rdi = rdi.Rprev;
                }
                else
                {
                    crossEdgeNotFound = false;
                }
            }

            // Start merging
            // 1) Creation of the baseEdge quad edge (See Fig.21)
            QuadEdge <T> baseEdge = QuadEdge <T> .Connect(rdi.Sym, ldi);

            if (ldi.Origin == ldo.Origin)
            {
                ldo = baseEdge.Sym;
            }
            if (rdi.Origin == rdo.Origin)
            {
                rdo = baseEdge;
            }

            // 2) Rising bubble (See Fig. 22)
            bool upperCommonTangentNotFound = true;

            while (upperCommonTangentNotFound)
            {
                // Locate the first L site (lCand.Destination) to be encountered
                // by the rising bubble, and delete L edges out of baseEdge.Destination
                // that fail the circle test.
                QuadEdge <T> lCand = baseEdge.Sym.Onext;
                if (IsValid(lCand, baseEdge))
                {
                    while (Geometry.InCircumCercle2D(lCand.Onext.Destination,
                                                     baseEdge.Destination, baseEdge.Origin, lCand.Destination))
                    {
                        nextCand = lCand.Onext;
                        QuadEdge <T> .Delete(lCand);

                        lCand = nextCand;
                    }
                }
                // Same for the right part (Symetrically)
                QuadEdge <T> rCand = baseEdge.Oprev;
                if (IsValid(rCand, baseEdge))
                {
                    while (Geometry.InCircumCercle2D(rCand.Oprev.Destination,
                                                     baseEdge.Destination, baseEdge.Origin, rCand.Destination))
                    {
                        nextCand = rCand.Oprev;
                        QuadEdge <T> .Delete(rCand);

                        rCand = nextCand;
                    }
                }
                // Upper common tangent is baseEdge
                if (!IsValid(lCand, baseEdge) && !IsValid(rCand, baseEdge))
                {
                    upperCommonTangentNotFound = false;
                }
                // Construct new cross edge between left and right
                // The next cross edge is to be connected to either lcand.Dest or rCand.Dest
                // If both are valid, then choose the appropriate one using the
                // Geometry.InCircumCercle2D test
                else if (!IsValid(lCand, baseEdge) ||
                         (
                             IsValid(rCand, baseEdge) &&
                             Geometry.InCircumCercle2D(rCand.Destination,
                                                       lCand.Destination,
                                                       lCand.Origin,
                                                       rCand.Origin)
                         )
                         )
                {
                    // Cross edge baseEdge added from rCand.Destination to basel.Destination
                    baseEdge = QuadEdge <T> .Connect(rCand, baseEdge.Sym);
                }
                else
                {
                    // Cross edge baseEdge added from baseEdge.Origin to lCand.Destination
                    baseEdge = QuadEdge <T> .Connect(baseEdge.Sym, lCand.Sym);
                }
            }
            return(new QuadEdge <T>[] { ldo, rdo });
        }
Exemplo n.º 3
0
        /// <summary>
        /// Insert a new site inside an existing delaunay triangulation. New site
        /// must be inside the convex hull of previoulsy added sites.
        /// Set <paramref name="safe"/> to true to first test if new site is correct.
        /// </summary>
        /// <param name="newPos">The position to of new site</param>
        /// <param name="edge">Edge used to start locate process. Can be used to speed up search.</param>
        /// <param name="safe">If true, check if <paramref name="newPos"/> inside the convex hull.</param>
        public bool Insert(Vec3 newPos, QuadEdge <T> edge = null, bool safe = false)
        {
            if (safe)
            {
                bool result = InsideConvexHull(newPos);
                if (!result)
                {
                    // Cannot add site not already inside the convex hull
                    return(false);
                }
            }

            // Start somewhere if no hint
            if (edge == null)
            {
                edge = _leftRightEdges[1];
            }

            // Locate edge (must be inside the boundary)
            QuadEdge <T> foundE = Locate(newPos, edge, safe: false);

            // Site already triangulated
            if (Geometry.AlmostEquals(foundE.Origin, newPos) ||
                Geometry.AlmostEquals(foundE.Destination, newPos))
            {
                return(false);
            }

            // On an edge ?
            if (Geometry.AlmostColinear(foundE.Origin, foundE.Destination, newPos))
            {
                var temp = foundE.Oprev;
                QuadEdge <T> .Delete(foundE);

                foundE = temp;
            }

            // Create new edge to connect new site to neighbors
            QuadEdge <T> baseE = QuadEdge <T> .MakeEdge(foundE.Origin, newPos);

            Vec3 first = baseE.Origin;

            QuadEdge <T> .Splice(baseE, foundE);

            // Up to 4 vertices if new site on an edge
            do
            {
                baseE = QuadEdge <T> .Connect(foundE, baseE.Sym);

                foundE = baseE.Oprev;
            } while (foundE.Destination != first);


            // Fill star shaped polygon and swap suspect edges
            // Adding a new point can break old condition about InCircle test
            foundE = baseE.Oprev;
            bool shouldExit = false;

            do
            {
                var tempE = foundE.Oprev;
                if (Geometry.RightOf(tempE.Destination, foundE) &&
                    Geometry.InCircumCercle2D(newPos, foundE.Origin, tempE.Destination, foundE.Destination))
                {
                    QuadEdge <T> .Swap(foundE);

                    // tempE != foundE.Oprev after swap
                    foundE = foundE.Oprev;
                }
                else if (foundE.Origin == first)
                {
                    // No more suspect edge ... exit
                    shouldExit = true;
                }
                else
                {
                    // Get next suspect edge from top to bottom
                    foundE = foundE.Onext.Lprev;
                }
            } while (!shouldExit);

            return(true);
        }
        private static Partition Merge(Partition leftPartition, Partition rightPartition)
        {
            QuadEdge left  = leftPartition.Left;                                                             //ldo
            QuadEdge right = rightPartition.Right;                                                           //rdo

            LowestCommonTangent(leftPartition, rightPartition, out QuadEdge lowLeft, out QuadEdge lowRight); //ldi, rdi

            QuadEdge edgeBase = QuadEdge.ConnectLeft(lowRight.Inverse, lowLeft);
            QuadEdge lcand    = edgeBase.RightPrevious;
            QuadEdge rcand    = edgeBase.OriginPrevious;

            if (edgeBase.Origin == right.Origin)
            {
                right = edgeBase;
            }
            if (edgeBase.Destination == left.Origin)
            {
                left = edgeBase.Inverse;
            }

            while (true)
            {
                QuadEdge temp = lcand.OriginNext;
                if (Point.IsCounterClockWise(edgeBase.Origin, temp.Destination, edgeBase.Destination))
                {
                    while (edgeBase.Origin.InCircle(lcand.Destination, temp.Destination, lcand.Origin))
                    {
                        lcand.Delete();
                        lcand = temp;
                        temp  = lcand.OriginNext;
                    }
                }

                temp = rcand.OriginPrevious;
                if (Point.IsCounterClockWise(edgeBase.Origin, temp.Destination, edgeBase.Destination))
                {
                    while (edgeBase.Destination.InCircle(temp.Destination, rcand.Destination, rcand.Origin))
                    {
                        rcand.Delete();
                        rcand = temp;
                        temp  = rcand.OriginPrevious;
                    }
                }

                bool leftValid  = Point.IsCounterClockWise(edgeBase.Origin, lcand.Destination, edgeBase.Destination);
                bool rightValid = Point.IsCounterClockWise(edgeBase.Origin, rcand.Destination, edgeBase.Destination);
                if (!leftValid && !rightValid)
                {
                    break;
                }

                if (!leftValid ||
                    rightValid && rcand.Destination.InCircle(lcand.Destination, lcand.Origin, rcand.Origin))
                {
                    edgeBase = QuadEdge.ConnectLeft(rcand, edgeBase.Inverse);
                    rcand    = edgeBase.Inverse.LeftNext;
                }
                else
                {
                    edgeBase = QuadEdge.ConnectRight(lcand, edgeBase).Inverse;
                    lcand    = edgeBase.RightPrevious;
                }
            }

            return(new Partition
            {
                Left = left,
                Right = right
            });
        }