private async Task <Partition> Subdivide(int lidx, int ridx)
        {
            Debug.Assert(lidx != ridx);
            if (ridx - lidx == 1) //2 points
            {
                QuadEdge edge = QuadEdge.MakeEdge(this._points[lidx], this._points[lidx + 1]);
                return(new Partition
                {
                    Left = edge,
                    Right = edge.Inverse
                });
            }
            if (ridx - lidx == 2) //3 points
            {
                QuadEdge a = QuadEdge.MakeEdge(this._points[lidx], this._points[lidx + 1]);
                QuadEdge b = QuadEdge.MakeEdge(this._points[lidx + 1], this._points[lidx + 2]);
                QuadEdge.Splice(a.Inverse, b);

                if (Point.IsCounterClockWise(this._points[lidx], this._points[lidx + 1], this._points[lidx + 2]))
                {
                    QuadEdge c = QuadEdge.ConnectLeft(b, a);
                    return(new Partition
                    {
                        Left = a,
                        Right = b.Inverse
                    });
                }
                if (Point.IsCounterClockWise(this._points[lidx], this._points[lidx + 2], this._points[lidx + 1]))
                {
                    QuadEdge c = QuadEdge.ConnectLeft(b, a);
                    return(new Partition
                    {
                        Left = c.Inverse,
                        Right = c
                    });
                }
                return(new Partition
                {
                    Left = a,
                    Right = b.Inverse
                });
            }
            int midx = (lidx + ridx) / 2;
            Task <Partition> leftTask  = Subdivide(lidx, midx);
            Task <Partition> rightTask = Subdivide(midx + 1, ridx);

            return(Merge(await leftTask, await rightTask));
        }
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);
        }