示例#1
0
        public static string RunSequential(List <IPolygon> polygons)
        {
            var pool       = new TrianglePool();
            var predicates = new RobustPredicates();

            var config = new Configuration();

            config.Predicates   = () => predicates;
            config.TrianglePool = () => pool.Restart();

            var mesher = new GenericMesher(config);
            var result = new MeshResult();

            foreach (var poly in polygons)
            {
                var mesh = mesher.Triangulate(poly);

                ProcessMesh(mesh, result);
            }

            pool.Clear();

            //Console.WriteLine("Total number of triangles processed: {0}", result.NumberOfTriangles);
            var sequential = $"Total number of triangles processed: {result.NumberOfTriangles}";

            if (result.Invalid > 0)
            {
                //Console.WriteLine("   Number of invalid triangulations: {0}", result.Invalid);
                sequential += $"Number of invalid triangulations: {result.Invalid}";
            }
            return(sequential);
        }
示例#2
0
        /// <summary>
        ///     Find the holes and infect them. Find the area constraints and infect
        ///     them. Infect the convex hull. Spread the infection and kill triangles.
        ///     Spread the area constraints.
        /// </summary>
        public void CarveHoles()
        {
            Otri         searchtri = default(Otri);
            Vertex       searchorg, searchdest;
            LocateResult intersect;

            var dummytri = mesh.dummytri;

            if (!mesh.behavior.Convex)
            {
                // Mark as infected any unprotected triangles on the boundary.
                // This is one way by which concavities are created.
                InfectHull();
            }

            if (!mesh.behavior.NoHoles)
            {
                // Infect each triangle in which a hole lies.
                foreach (var hole in mesh.holes)
                {
                    // Ignore holes that aren't within the bounds of the mesh.
                    if (mesh.bounds.Contains(hole))
                    {
                        // Start searching from some triangle on the outer boundary.
                        searchtri.tri    = dummytri;
                        searchtri.orient = 0;
                        searchtri.Sym();
                        // Ensure that the hole is to the left of this boundary edge;
                        // otherwise, locate() will falsely report that the hole
                        // falls within the starting triangle.
                        searchorg  = searchtri.Org();
                        searchdest = searchtri.Dest();
                        if (RobustPredicates.CounterClockwise(searchorg, searchdest, hole) > 0.0)
                        {
                            // Find a triangle that contains the hole.
                            intersect = mesh.locator.Locate(hole, ref searchtri);
                            if ((intersect != LocateResult.Outside) && (!searchtri.IsInfected()))
                            {
                                // Infect the triangle. This is done by marking the triangle
                                // as infected and including the triangle in the virus pool.
                                searchtri.Infect();
                                viri.Add(searchtri.tri);
                            }
                        }
                    }
                }
            }

            if (viri.Count > 0)
            {
                // Carve the holes and concavities.
                Plague();
            }

            // Free up memory (virus pool should be empty anyway).
            viri.Clear();
        }
示例#3
0
        private SplayNode CircleTopInsert(SplayNode splayroot, Otri newkey,
                                          Vertex pa, Vertex pb, Vertex pc, double topy)
        {
            double ccwabc;
            double xac, yac, xbc, ybc;
            double aclen2, bclen2;
            Point  searchpoint = new Point(); // TODO: mesh.nextras
            Otri   dummytri    = default(Otri);

            ccwabc        = RobustPredicates.CounterClockwise(pa, pb, pc);
            xac           = pa.X - pc.X;
            yac           = pa.Y - pc.Y;
            xbc           = pb.X - pc.X;
            ybc           = pb.Y - pc.Y;
            aclen2        = xac * xac + yac * yac;
            bclen2        = xbc * xbc + ybc * ybc;
            searchpoint.X = pc.X - (yac * bclen2 - ybc * aclen2) / (2.0 * ccwabc);
            searchpoint.Y = topy;
            return(SplayInsert(Splay(splayroot, searchpoint, ref dummytri), newkey, searchpoint));
        }
示例#4
0
        /// <summary>
        ///     Enforce the Delaunay condition at an edge, fanning out recursively from
        ///     an existing vertex. Pay special attention to stacking inverted triangles.
        /// </summary>
        /// <param name="fixuptri"></param>
        /// <param name="leftside">
        ///     Indicates whether or not fixuptri is to the left of
        ///     the segment being inserted. (Imagine that the segment is pointing up from
        ///     endpoint1 to endpoint2.)
        /// </param>
        /// <remarks>
        ///     This is a support routine for inserting segments into a constrained
        ///     Delaunay triangulation.
        ///     The origin of fixuptri is treated as if it has just been inserted, and
        ///     the local Delaunay condition needs to be enforced. It is only enforced
        ///     in one sector, however, that being the angular range defined by
        ///     fixuptri.
        ///     This routine also needs to make decisions regarding the "stacking" of
        ///     triangles. (Read the description of ConstrainedEdge() below before
        ///     reading on here, so you understand the algorithm.) If the position of
        ///     the new vertex (the origin of fixuptri) indicates that the vertex before
        ///     it on the polygon is a reflex vertex, then "stack" the triangle by
        ///     doing nothing.  (fixuptri is an inverted triangle, which is how stacked
        ///     triangles are identified.)
        ///     Otherwise, check whether the vertex before that was a reflex vertex.
        ///     If so, perform an edge flip, thereby eliminating an inverted triangle
        ///     (popping it off the stack). The edge flip may result in the creation
        ///     of a new inverted triangle, depending on whether or not the new vertex
        ///     is visible to the vertex three edges behind on the polygon.
        ///     If neither of the two vertices behind the new vertex are reflex
        ///     vertices, fixuptri and fartri, the triangle opposite it, are not
        ///     inverted; hence, ensure that the edge between them is locally Delaunay.
        /// </remarks>
        private void DelaunayFixup(ref Otri fixuptri, bool leftside)
        {
            Otri   neartri = default(Otri);
            Otri   fartri = default(Otri);
            Osub   faredge = default(Osub);
            Vertex nearvertex, leftvertex, rightvertex, farvertex;

            fixuptri.Lnext(ref neartri);
            neartri.Sym(ref fartri);
            // Check if the edge opposite the origin of fixuptri can be flipped.
            if (fartri.tri.Id == Mesh.DUMMY)
            {
                return;
            }
            neartri.Pivot(ref faredge);
            if (faredge.seg.hash != Mesh.DUMMY)
            {
                return;
            }
            // Find all the relevant vertices.
            nearvertex  = neartri.Apex();
            leftvertex  = neartri.Org();
            rightvertex = neartri.Dest();
            farvertex   = fartri.Apex();
            // Check whether the previous polygon vertex is a reflex vertex.
            if (leftside)
            {
                if (RobustPredicates.CounterClockwise(nearvertex, leftvertex, farvertex) <= 0.0)
                {
                    // leftvertex is a reflex vertex too. Nothing can
                    // be done until a convex section is found.
                    return;
                }
            }
            else
            {
                if (RobustPredicates.CounterClockwise(farvertex, rightvertex, nearvertex) <= 0.0)
                {
                    // rightvertex is a reflex vertex too.  Nothing can
                    // be done until a convex section is found.
                    return;
                }
            }
            if (RobustPredicates.CounterClockwise(rightvertex, leftvertex, farvertex) > 0.0)
            {
                // fartri is not an inverted triangle, and farvertex is not a reflex
                // vertex.  As there are no reflex vertices, fixuptri isn't an
                // inverted triangle, either.  Hence, test the edge between the
                // triangles to ensure it is locally Delaunay.
                if (RobustPredicates.InCircle(leftvertex, farvertex, rightvertex, nearvertex) <= 0.0)
                {
                    return;
                }
                // Not locally Delaunay; go on to an edge flip.
            }
            // else fartri is inverted; remove it from the stack by flipping.
            mesh.Flip(ref neartri);
            fixuptri.Lprev(); // Restore the origin of fixuptri after the flip.
            // Recursively process the two triangles that result from the flip.
            DelaunayFixup(ref fixuptri, leftside);
            DelaunayFixup(ref fartri, leftside);
        }
示例#5
0
        /// <summary>
        /// Merge two adjacent Delaunay triangulations into a single Delaunay triangulation.
        /// </summary>
        /// <param name="farleft">Bounding triangles of the left triangulation.</param>
        /// <param name="innerleft">Bounding triangles of the left triangulation.</param>
        /// <param name="innerright">Bounding triangles of the right triangulation.</param>
        /// <param name="farright">Bounding triangles of the right triangulation.</param>
        /// <param name="axis"></param>
        /// <remarks>
        /// This is similar to the algorithm given by Guibas and Stolfi, but uses
        /// a triangle-based, rather than edge-based, data structure.
        ///
        /// The algorithm walks up the gap between the two triangulations, knitting
        /// them together.  As they are merged, some of their bounding triangles
        /// are converted into real triangles of the triangulation.  The procedure
        /// pulls each hull's bounding triangles apart, then knits them together
        /// like the teeth of two gears.  The Delaunay property determines, at each
        /// step, whether the next "tooth" is a bounding triangle of the left hull
        /// or the right.  When a bounding triangle becomes real, its apex is
        /// changed from NULL to a real vertex.
        ///
        /// Only two new triangles need to be allocated.  These become new bounding
        /// triangles at the top and bottom of the seam.  They are used to connect
        /// the remaining bounding triangles (those that have not been converted
        /// into real triangles) into a single fan.
        ///
        /// On entry, 'farleft' and 'innerleft' are bounding triangles of the left
        /// triangulation.  The origin of 'farleft' is the leftmost vertex, and
        /// the destination of 'innerleft' is the rightmost vertex of the
        /// triangulation.  Similarly, 'innerright' and 'farright' are bounding
        /// triangles of the right triangulation.  The origin of 'innerright' and
        /// destination of 'farright' are the leftmost and rightmost vertices.
        ///
        /// On completion, the origin of 'farleft' is the leftmost vertex of the
        /// merged triangulation, and the destination of 'farright' is the rightmost
        /// vertex.
        /// </remarks>
        void MergeHulls(ref Otri farleft, ref Otri innerleft, ref Otri innerright,
                        ref Otri farright, int axis)
        {
            Otri   leftcand = default(Otri), rightcand = default(Otri);
            Otri   nextedge = default(Otri);
            Otri   sidecasing = default(Otri), topcasing = default(Otri), outercasing = default(Otri);
            Otri   checkedge = default(Otri);
            Otri   baseedge  = default(Otri);
            Vertex innerleftdest;
            Vertex innerrightorg;
            Vertex innerleftapex, innerrightapex;
            Vertex farleftpt, farrightpt;
            Vertex farleftapex, farrightapex;
            Vertex lowerleft, lowerright;
            Vertex upperleft, upperright;
            Vertex nextapex;
            Vertex checkvertex;
            bool   changemade;
            bool   badedge;
            bool   leftfinished, rightfinished;

            innerleftdest  = innerleft.Dest();
            innerleftapex  = innerleft.Apex();
            innerrightorg  = innerright.Org();
            innerrightapex = innerright.Apex();
            // Special treatment for horizontal cuts.
            if (UseDwyer && (axis == 1))
            {
                farleftpt    = farleft.Org();
                farleftapex  = farleft.Apex();
                farrightpt   = farright.Dest();
                farrightapex = farright.Apex();
                // The pointers to the extremal vertices are shifted to point to the
                // topmost and bottommost vertex of each hull, rather than the
                // leftmost and rightmost vertices.
                while (farleftapex.Y < farleftpt.Y)
                {
                    farleft.Lnext();
                    farleft.Sym();
                    farleftpt   = farleftapex;
                    farleftapex = farleft.Apex();
                }
                innerleft.Sym(ref checkedge);
                checkvertex = checkedge.Apex();
                while (checkvertex.Y > innerleftdest.Y)
                {
                    checkedge.Lnext(ref innerleft);
                    innerleftapex = innerleftdest;
                    innerleftdest = checkvertex;
                    innerleft.Sym(ref checkedge);
                    checkvertex = checkedge.Apex();
                }
                while (innerrightapex.Y < innerrightorg.Y)
                {
                    innerright.Lnext();
                    innerright.Sym();
                    innerrightorg  = innerrightapex;
                    innerrightapex = innerright.Apex();
                }
                farright.Sym(ref checkedge);
                checkvertex = checkedge.Apex();
                while (checkvertex.Y > farrightpt.Y)
                {
                    checkedge.Lnext(ref farright);
                    farrightapex = farrightpt;
                    farrightpt   = checkvertex;
                    farright.Sym(ref checkedge);
                    checkvertex = checkedge.Apex();
                }
            }
            // Find a line tangent to and below both hulls.
            do
            {
                changemade = false;
                // Make innerleftdest the "bottommost" vertex of the left hull.
                if (RobustPredicates.CounterClockwise(innerleftdest, innerleftapex, innerrightorg) > 0.0)
                {
                    innerleft.Lprev();
                    innerleft.Sym();
                    innerleftdest = innerleftapex;
                    innerleftapex = innerleft.Apex();
                    changemade    = true;
                }
                // Make innerrightorg the "bottommost" vertex of the right hull.
                if (RobustPredicates.CounterClockwise(innerrightapex, innerrightorg, innerleftdest) > 0.0)
                {
                    innerright.Lnext();
                    innerright.Sym();
                    innerrightorg  = innerrightapex;
                    innerrightapex = innerright.Apex();
                    changemade     = true;
                }
            } while (changemade);

            // Find the two candidates to be the next "gear tooth."
            innerleft.Sym(ref leftcand);
            innerright.Sym(ref rightcand);
            // Create the bottom new bounding triangle.
            mesh.MakeTriangle(ref baseedge);
            // Connect it to the bounding boxes of the left and right triangulations.
            baseedge.Bond(ref innerleft);
            baseedge.Lnext();
            baseedge.Bond(ref innerright);
            baseedge.Lnext();
            baseedge.SetOrg(innerrightorg);
            baseedge.SetDest(innerleftdest);
            // Apex is intentionally left NULL.

            // Fix the extreme triangles if necessary.
            farleftpt = farleft.Org();
            if (innerleftdest == farleftpt)
            {
                baseedge.Lnext(ref farleft);
            }
            farrightpt = farright.Dest();
            if (innerrightorg == farrightpt)
            {
                baseedge.Lprev(ref farright);
            }
            // The vertices of the current knitting edge.
            lowerleft  = innerleftdest;
            lowerright = innerrightorg;
            // The candidate vertices for knitting.
            upperleft  = leftcand.Apex();
            upperright = rightcand.Apex();
            // Walk up the gap between the two triangulations, knitting them together.
            while (true)
            {
                // Have we reached the top? (This isn't quite the right question,
                // because even though the left triangulation might seem finished now,
                // moving up on the right triangulation might reveal a new vertex of
                // the left triangulation. And vice-versa.)
                leftfinished  = RobustPredicates.CounterClockwise(upperleft, lowerleft, lowerright) <= 0.0;
                rightfinished = RobustPredicates.CounterClockwise(upperright, lowerleft, lowerright) <= 0.0;
                if (leftfinished && rightfinished)
                {
                    // Create the top new bounding triangle.
                    mesh.MakeTriangle(ref nextedge);
                    nextedge.SetOrg(lowerleft);
                    nextedge.SetDest(lowerright);
                    // Apex is intentionally left NULL.
                    // Connect it to the bounding boxes of the two triangulations.
                    nextedge.Bond(ref baseedge);
                    nextedge.Lnext();
                    nextedge.Bond(ref rightcand);
                    nextedge.Lnext();
                    nextedge.Bond(ref leftcand);

                    // Special treatment for horizontal cuts.
                    if (UseDwyer && (axis == 1))
                    {
                        farleftpt    = farleft.Org();
                        farleftapex  = farleft.Apex();
                        farrightpt   = farright.Dest();
                        farrightapex = farright.Apex();
                        farleft.Sym(ref checkedge);
                        checkvertex = checkedge.Apex();
                        // The pointers to the extremal vertices are restored to the
                        // leftmost and rightmost vertices (rather than topmost and
                        // bottommost).
                        while (checkvertex.X < farleftpt.X)
                        {
                            checkedge.Lprev(ref farleft);
                            farleftapex = farleftpt;
                            farleftpt   = checkvertex;
                            farleft.Sym(ref checkedge);
                            checkvertex = checkedge.Apex();
                        }
                        while (farrightapex.X > farrightpt.X)
                        {
                            farright.Lprev();
                            farright.Sym();
                            farrightpt   = farrightapex;
                            farrightapex = farright.Apex();
                        }
                    }
                    return;
                }
                // Consider eliminating edges from the left triangulation.
                if (!leftfinished)
                {
                    // What vertex would be exposed if an edge were deleted?
                    leftcand.Lprev(ref nextedge);
                    nextedge.Sym();
                    nextapex = nextedge.Apex();
                    // If nextapex is NULL, then no vertex would be exposed; the
                    // triangulation would have been eaten right through.
                    if (nextapex != null)
                    {
                        // Check whether the edge is Delaunay.
                        badedge = RobustPredicates.InCircle(lowerleft, lowerright, upperleft, nextapex) > 0.0;
                        while (badedge)
                        {
                            // Eliminate the edge with an edge flip.  As a result, the
                            // left triangulation will have one more boundary triangle.
                            nextedge.Lnext();
                            nextedge.Sym(ref topcasing);
                            nextedge.Lnext();
                            nextedge.Sym(ref sidecasing);
                            nextedge.Bond(ref topcasing);
                            leftcand.Bond(ref sidecasing);
                            leftcand.Lnext();
                            leftcand.Sym(ref outercasing);
                            nextedge.Lprev();
                            nextedge.Bond(ref outercasing);
                            // Correct the vertices to reflect the edge flip.
                            leftcand.SetOrg(lowerleft);
                            leftcand.SetDest(null);
                            leftcand.SetApex(nextapex);
                            nextedge.SetOrg(null);
                            nextedge.SetDest(upperleft);
                            nextedge.SetApex(nextapex);
                            // Consider the newly exposed vertex.
                            upperleft = nextapex;
                            // What vertex would be exposed if another edge were deleted?
                            sidecasing.Copy(ref nextedge);
                            nextapex = nextedge.Apex();
                            if (nextapex != null)
                            {
                                // Check whether the edge is Delaunay.
                                badedge = RobustPredicates.InCircle(lowerleft, lowerright, upperleft, nextapex) > 0.0;
                            }
                            else
                            {
                                // Avoid eating right through the triangulation.
                                badedge = false;
                            }
                        }
                    }
                }
                // Consider eliminating edges from the right triangulation.
                if (!rightfinished)
                {
                    // What vertex would be exposed if an edge were deleted?
                    rightcand.Lnext(ref nextedge);
                    nextedge.Sym();
                    nextapex = nextedge.Apex();
                    // If nextapex is NULL, then no vertex would be exposed; the
                    // triangulation would have been eaten right through.
                    if (nextapex != null)
                    {
                        // Check whether the edge is Delaunay.
                        badedge = RobustPredicates.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0;
                        while (badedge)
                        {
                            // Eliminate the edge with an edge flip.  As a result, the
                            // right triangulation will have one more boundary triangle.
                            nextedge.Lprev();
                            nextedge.Sym(ref topcasing);
                            nextedge.Lprev();
                            nextedge.Sym(ref sidecasing);
                            nextedge.Bond(ref topcasing);
                            rightcand.Bond(ref sidecasing);
                            rightcand.Lprev();
                            rightcand.Sym(ref outercasing);
                            nextedge.Lnext();
                            nextedge.Bond(ref outercasing);
                            // Correct the vertices to reflect the edge flip.
                            rightcand.SetOrg(null);
                            rightcand.SetDest(lowerright);
                            rightcand.SetApex(nextapex);
                            nextedge.SetOrg(upperright);
                            nextedge.SetDest(null);
                            nextedge.SetApex(nextapex);
                            // Consider the newly exposed vertex.
                            upperright = nextapex;
                            // What vertex would be exposed if another edge were deleted?
                            sidecasing.Copy(ref nextedge);
                            nextapex = nextedge.Apex();
                            if (nextapex != null)
                            {
                                // Check whether the edge is Delaunay.
                                badedge = RobustPredicates.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0;
                            }
                            else
                            {
                                // Avoid eating right through the triangulation.
                                badedge = false;
                            }
                        }
                    }
                }
                if (leftfinished || (!rightfinished &&
                                     (RobustPredicates.InCircle(upperleft, lowerleft, lowerright, upperright) > 0.0)))
                {
                    // Knit the triangulations, adding an edge from 'lowerleft'
                    // to 'upperright'.
                    baseedge.Bond(ref rightcand);
                    rightcand.Lprev(ref baseedge);
                    baseedge.SetDest(lowerleft);
                    lowerright = upperright;
                    baseedge.Sym(ref rightcand);
                    upperright = rightcand.Apex();
                }
                else
                {
                    // Knit the triangulations, adding an edge from 'upperleft'
                    // to 'lowerright'.
                    baseedge.Bond(ref leftcand);
                    leftcand.Lnext(ref baseedge);
                    baseedge.SetOrg(lowerright);
                    lowerleft = upperleft;
                    baseedge.Sym(ref leftcand);
                    upperleft = leftcand.Apex();
                }
            }
        }
示例#6
0
        /// <summary>
        ///     Find the first triangle on the path from one point to another.
        /// </summary>
        /// <param name="searchtri"></param>
        /// <param name="searchpoint"></param>
        /// <returns>
        ///     The return value notes whether the destination or apex of the found
        ///     triangle is collinear with the two points in question.
        /// </returns>
        /// <remarks>
        ///     Finds the triangle that intersects a line segment drawn from the
        ///     origin of 'searchtri' to the point 'searchpoint', and returns the result
        ///     in 'searchtri'. The origin of 'searchtri' does not change, even though
        ///     the triangle returned may differ from the one passed in. This routine
        ///     is used to find the direction to move in to get from one point to
        ///     another.
        /// </remarks>
        private FindDirectionResult FindDirection(ref Otri searchtri, Vertex searchpoint)
        {
            Otri   checktri = default(Otri);
            Vertex startvertex;
            Vertex leftvertex, rightvertex;
            double leftccw, rightccw;
            bool   leftflag, rightflag;

            startvertex = searchtri.Org();
            rightvertex = searchtri.Dest();
            leftvertex  = searchtri.Apex();
            // Is 'searchpoint' to the left?
            leftccw  = RobustPredicates.CounterClockwise(searchpoint, startvertex, leftvertex);
            leftflag = leftccw > 0.0;
            // Is 'searchpoint' to the right?
            rightccw  = RobustPredicates.CounterClockwise(startvertex, searchpoint, rightvertex);
            rightflag = rightccw > 0.0;
            if (leftflag && rightflag)
            {
                // 'searchtri' faces directly away from 'searchpoint'. We could go left
                // or right. Ask whether it's a triangle or a boundary on the left.
                searchtri.Onext(ref checktri);
                if (checktri.tri.Id == Mesh.DUMMY)
                {
                    leftflag = false;
                }
                else
                {
                    rightflag = false;
                }
            }
            while (leftflag)
            {
                // Turn left until satisfied.
                searchtri.Onext();
                if (searchtri.tri.Id == Mesh.DUMMY)
                {
                    throw new Exception("Unable to find a triangle on path.");
                }
                leftvertex = searchtri.Apex();
                rightccw   = leftccw;
                leftccw    = RobustPredicates.CounterClockwise(searchpoint, startvertex, leftvertex);
                leftflag   = leftccw > 0.0;
            }
            while (rightflag)
            {
                // Turn right until satisfied.
                searchtri.Oprev();
                if (searchtri.tri.Id == Mesh.DUMMY)
                {
                    throw new Exception("Unable to find a triangle on path.");
                }
                rightvertex = searchtri.Dest();
                leftccw     = rightccw;
                rightccw    = RobustPredicates.CounterClockwise(startvertex, searchpoint, rightvertex);
                rightflag   = rightccw > 0.0;
            }
            if (leftccw == 0.0)
            {
                return(FindDirectionResult.Leftcollinear);
            }
            if (rightccw == 0.0)
            {
                return(FindDirectionResult.Rightcollinear);
            }
            return(FindDirectionResult.Within);
        }
示例#7
0
        /// <summary>
        /// Triangulate a given number of random point sets in parallel.
        /// </summary>
        public static bool Run(int n = 1000)
        {
            // Use thread-safe random source.
            var random = Random.Shared;

            // Generate a random set of sizes.
            var sizes = Enumerable.Range(0, n).Select(_ => random.Next(500, 5000));

            var queue = new ConcurrentQueue <int>(sizes);

            int concurrencyLevel = Environment.ProcessorCount / 2;

            var tasks = new Task <MeshResult> [concurrencyLevel];

            for (int i = 0; i < concurrencyLevel; i++)
            {
                tasks[i] = Task.Run(() =>
                {
                    // Each task has it's own triangle pool and predicates instance.
                    var pool       = new TrianglePool();
                    var predicates = new RobustPredicates();

                    // The factory methods return the above instances.
                    var config = new Configuration()
                    {
                        Predicates   = () => predicates,
                        TrianglePool = () => pool.Restart(),
                        RandomSource = () => Random.Shared
                    };

                    var triangulator = new Dwyer();
                    var result       = new MeshResult();

                    var bounds = new Rectangle(0d, 0d, 1000d, 1000d);

                    while (queue.Count > 0)
                    {
                        if (queue.TryDequeue(out int size))
                        {
                            var points = Generate.RandomPoints(size, bounds);

                            var mesh = triangulator.Triangulate(points, config);

                            ProcessMesh(mesh, result);
                        }
                    }

                    pool.Clear();

                    return(result);
                });
            }

            Task.WaitAll(tasks);

            int numberOfTriangles = tasks.Sum(t => t.Result.NumberOfTriangles);
            int invalid           = tasks.Sum(t => t.Result.Invalid);

            Console.WriteLine("Total number of triangles processed: {0}", numberOfTriangles);

            if (invalid > 0)
            {
                Console.WriteLine("   Number of invalid triangulations: {0}", invalid);
            }

            return(invalid == 0);
        }
示例#8
0
        /// <summary>
        /// Reads all .poly files from given directory and processes them in parallel.
        /// </summary>
        public static bool Run(string dir)
        {
            var files = Directory.EnumerateFiles(dir, "*.poly", SearchOption.AllDirectories);

            var queue = new ConcurrentQueue <string>(files);

            int concurrencyLevel = Environment.ProcessorCount / 2;

            var tasks = new Task <MeshResult> [concurrencyLevel];

            for (int i = 0; i < concurrencyLevel; i++)
            {
                tasks[i] = Task.Run(() =>
                {
                    // Each task has it's own triangle pool and predicates instance.
                    var pool       = new TrianglePool();
                    var predicates = new RobustPredicates();

                    // The factory methods return the above instances.
                    var config = new Configuration()
                    {
                        Predicates   = () => predicates,
                        TrianglePool = () => pool.Restart(),
                        RandomSource = () => Random.Shared
                    };

                    var mesher = new GenericMesher(config);
                    var result = new MeshResult();

                    while (queue.Count > 0)
                    {
                        if (queue.TryDequeue(out var file))
                        {
                            var poly = FileProcessor.Read(file);

                            var mesh = mesher.Triangulate(poly);

                            ProcessMesh(mesh, result);
                        }
                    }

                    pool.Clear();

                    return(result);
                });
            }

            Task.WaitAll(tasks);

            int numberOfTriangles = tasks.Sum(t => t.Result.NumberOfTriangles);
            int invalid           = tasks.Sum(t => t.Result.Invalid);

            Console.WriteLine("Total number of triangles processed: {0}", numberOfTriangles);

            if (invalid > 0)
            {
                Console.WriteLine("   Number of invalid triangulations: {0}", invalid);
            }

            return(invalid == 0);
        }
示例#9
0
        public static string RunParallel(List <IPolygon> polygons)
        {
            var queue = new ConcurrentQueue <IPolygon>(polygons);

            int concurrencyLevel = Environment.ProcessorCount;

            var tasks = new Task <MeshResult> [concurrencyLevel];

            for (int i = 0; i < concurrencyLevel; i++)
            {
                tasks[i] = Task.Run(() =>
                {
                    // Each task has it's own triangle pool and predicates instance.
                    var pool       = new TrianglePool();
                    var predicates = new RobustPredicates();

                    var config = new Configuration();

                    // The factory methods return the above instances.
                    config.Predicates   = () => predicates;
                    config.TrianglePool = () => pool.Restart();

                    IPolygon poly;

                    var mesher = new GenericMesher(config);
                    var result = new MeshResult();

                    while (queue.Count > 0)
                    {
                        if (queue.TryDequeue(out poly))
                        {
                            var mesh = mesher.Triangulate(poly);

                            ProcessMesh(mesh, result);
                        }
                    }

                    pool.Clear();

                    return(result);
                });
            }

            Task.WaitAll(tasks);

            int numberOfTriangles = 0;
            int invalid           = 0;

            for (int i = 0; i < concurrencyLevel; i++)
            {
                var result = tasks[i].Result;

                numberOfTriangles += result.NumberOfTriangles;
                invalid           += result.Invalid;
            }
            string parallel = $"Total number of triangles processed: {numberOfTriangles}";

            //Console.WriteLine("Total number of triangles processed: {0}", numberOfTriangles);

            if (invalid > 0)
            {
                //Console.WriteLine("   Number of invalid triangulations: {0}", invalid);
                parallel += $"\tNumber of invalid triangulations: {invalid}";
            }
            return(parallel);
        }
示例#10
0
        private static Point FindPointInPolygon(List<Vertex> contour, int limit, double eps)
        {
            var bounds = new Rectangle();
            bounds.Expand(contour.ToPoints());

            int length = contour.Count;

            var test = new Point();

            Point a, b, c; // Current corner points.

            double bx, by;
            double dx, dy;
            double h;

            var predicates = new RobustPredicates();

            a = contour[0];
            b = contour[1];

            for (int i = 0; i < length; i++)
            {
                c = contour[(i + 2) % length];

                // Corner point.
                bx = b.x;
                by = b.y;

                // NOTE: if we knew the contour points were in counterclockwise order, we
                // could skip concave corners and search only in one direction.

                h = predicates.CounterClockwise(a, b, c);

                if (Math.Abs(h) < eps)
                {
                    // Points are nearly co-linear. Use perpendicular direction.
                    dx = (c.y - a.y) / 2;
                    dy = (a.x - c.x) / 2;
                }
                else
                {
                    // Direction [midpoint(a-c) -> corner point]
                    dx = (a.x + c.x) / 2 - bx;
                    dy = (a.y + c.y) / 2 - by;
                }

                // Move around the contour.
                a = b;
                b = c;

                h = 1.0;

                for (int j = 0; j < limit; j++)
                {
                    // Search in direction.
                    test.x = bx + dx * h;
                    test.y = by + dy * h;

                    if (bounds.Contains(test) && IsPointInPolygon(test, contour))
                    {
                        return test;
                    }

                    // Search in opposite direction (see NOTE above).
                    test.x = bx - dx * h;
                    test.y = by - dy * h;

                    if (bounds.Contains(test) && IsPointInPolygon(test, contour))
                    {
                        return test;
                    }

                    h = h / 2;
                }
            }

            throw new Exception();
        }
示例#11
0
        /// <summary>
        ///     Force a segment into a constrained Delaunay triangulation by deleting the
        ///     triangles it intersects, and triangulating the polygons that form on each
        ///     side of it.
        /// </summary>
        /// <param name="starttri"></param>
        /// <param name="endpoint2"></param>
        /// <param name="newmark"></param>
        /// <remarks>
        ///     Generates a single subsegment connecting 'endpoint1' to 'endpoint2'.
        ///     The triangle 'starttri' has 'endpoint1' as its origin.  'newmark' is the
        ///     boundary marker of the segment.
        ///     To insert a segment, every triangle whose interior intersects the
        ///     segment is deleted. The union of these deleted triangles is a polygon
        ///     (which is not necessarily monotone, but is close enough), which is
        ///     divided into two polygons by the new segment. This routine's task is
        ///     to generate the Delaunay triangulation of these two polygons.
        ///     You might think of this routine's behavior as a two-step process.  The
        ///     first step is to walk from endpoint1 to endpoint2, flipping each edge
        ///     encountered.  This step creates a fan of edges connected to endpoint1,
        ///     including the desired edge to endpoint2. The second step enforces the
        ///     Delaunay condition on each side of the segment in an incremental manner:
        ///     proceeding along the polygon from endpoint1 to endpoint2 (this is done
        ///     independently on each side of the segment), each vertex is "enforced"
        ///     as if it had just been inserted, but affecting only the previous
        ///     vertices. The result is the same as if the vertices had been inserted
        ///     in the order they appear on the polygon, so the result is Delaunay.
        ///     In truth, ConstrainedEdge() interleaves these two steps. The procedure
        ///     walks from endpoint1 to endpoint2, and each time an edge is encountered
        ///     and flipped, the newly exposed vertex (at the far end of the flipped
        ///     edge) is "enforced" upon the previously flipped edges, usually affecting
        ///     only one side of the polygon (depending upon which side of the segment
        ///     the vertex falls on).
        ///     The algorithm is complicated by the need to handle polygons that are not
        ///     convex.  Although the polygon is not necessarily monotone, it can be
        ///     triangulated in a manner similar to the stack-based algorithms for
        ///     monotone polygons. For each reflex vertex (local concavity) of the
        ///     polygon, there will be an inverted triangle formed by one of the edge
        ///     flips. (An inverted triangle is one with negative area - that is, its
        ///     vertices are arranged in clockwise order - and is best thought of as a
        ///     wrinkle in the fabric of the mesh.)  Each inverted triangle can be
        ///     thought of as a reflex vertex pushed on the stack, waiting to be fixed
        ///     later.
        ///     A reflex vertex is popped from the stack when a vertex is inserted that
        ///     is visible to the reflex vertex. (However, if the vertex behind the
        ///     reflex vertex is not visible to the reflex vertex, a new inverted
        ///     triangle will take its place on the stack.) These details are handled
        ///     by the DelaunayFixup() routine above.
        /// </remarks>
        private void ConstrainedEdge(ref Otri starttri, Vertex endpoint2, ushort newmark)
        {
            Otri   fixuptri = default(Otri), fixuptri2 = default(Otri);
            Osub   crosssubseg = default(Osub);
            Vertex endpoint1;
            Vertex farvertex;
            double area;
            bool   collision;
            bool   done;

            endpoint1 = starttri.Org();
            starttri.Lnext(ref fixuptri);
            mesh.Flip(ref fixuptri);
            // 'collision' indicates whether we have found a vertex directly
            // between endpoint1 and endpoint2.
            collision = false;
            done      = false;
            do
            {
                farvertex = fixuptri.Org();
                // 'farvertex' is the extreme point of the polygon we are "digging"
                //  to get from endpoint1 to endpoint2.
                if ((farvertex.X == endpoint2.X) && (farvertex.Y == endpoint2.Y))
                {
                    fixuptri.Oprev(ref fixuptri2);
                    // Enforce the Delaunay condition around endpoint2.
                    DelaunayFixup(ref fixuptri, false);
                    DelaunayFixup(ref fixuptri2, true);
                    done = true;
                }
                else
                {
                    // Check whether farvertex is to the left or right of the segment being
                    // inserted, to decide which edge of fixuptri to dig through next.
                    area = RobustPredicates.CounterClockwise(endpoint1, endpoint2, farvertex);
                    if (area == 0.0)
                    {
                        // We've collided with a vertex between endpoint1 and endpoint2.
                        collision = true;
                        fixuptri.Oprev(ref fixuptri2);
                        // Enforce the Delaunay condition around farvertex.
                        DelaunayFixup(ref fixuptri, false);
                        DelaunayFixup(ref fixuptri2, true);
                        done = true;
                    }
                    else
                    {
                        if (area > 0.0)
                        {
                            // farvertex is to the left of the segment.
                            fixuptri.Oprev(ref fixuptri2);
                            // Enforce the Delaunay condition around farvertex, on the
                            // left side of the segment only.
                            DelaunayFixup(ref fixuptri2, true);
                            // Flip the edge that crosses the segment. After the edge is
                            // flipped, one of its endpoints is the fan vertex, and the
                            // destination of fixuptri is the fan vertex.
                            fixuptri.Lprev();
                        }
                        else
                        {
                            // farvertex is to the right of the segment.
                            DelaunayFixup(ref fixuptri, false);
                            // Flip the edge that crosses the segment. After the edge is
                            // flipped, one of its endpoints is the fan vertex, and the
                            // destination of fixuptri is the fan vertex.
                            fixuptri.Oprev();
                        }
                        // Check for two intersecting segments.
                        fixuptri.Pivot(ref crosssubseg);
                        if (crosssubseg.seg.hash == Mesh.DUMMY)
                        {
                            mesh.Flip(ref fixuptri); // May create inverted triangle at left.
                        }
                        else
                        {
                            // We've collided with a segment between endpoint1 and endpoint2.
                            collision = true;
                            // Insert a vertex at the intersection.
                            SegmentIntersection(ref fixuptri, ref crosssubseg, endpoint2);
                            done = true;
                        }
                    }
                }
            } while (!done);
            // Insert a subsegment to make the segment permanent.
            mesh.InsertSubseg(ref fixuptri, newmark);
            // If there was a collision with an interceding vertex, install another
            // segment connecting that vertex with endpoint2.
            if (collision)
            {
                // Insert the remainder of the segment.
                if (!ScoutSegment(ref fixuptri, endpoint2, newmark))
                {
                    ConstrainedEdge(ref fixuptri, endpoint2, newmark);
                }
            }
        }
示例#12
0
        /// <summary> Find a triangle or edge containing a given point. </summary>
        /// <param name="searchpoint">The point to locate.</param>
        /// <param name="searchtri">The triangle to start the search at.</param>
        /// <param name="stopatsubsegment">
        ///     If 'stopatsubsegment' is set, the search
        ///     will stop if it tries to walk through a subsegment, and will return OUTSIDE.
        /// </param>
        /// <returns>Location information.</returns>
        /// <remarks>
        ///     Begins its search from 'searchtri'. It is important that 'searchtri'
        ///     be a handle with the property that 'searchpoint' is strictly to the left
        ///     of the edge denoted by 'searchtri', or is collinear with that edge and
        ///     does not intersect that edge. (In particular, 'searchpoint' should not
        ///     be the origin or destination of that edge.)
        ///     These conditions are imposed because preciselocate() is normally used in
        ///     one of two situations:
        ///     (1)  To try to find the location to insert a new point.  Normally, we
        ///     know an edge that the point is strictly to the left of. In the
        ///     incremental Delaunay algorithm, that edge is a bounding box edge.
        ///     In Ruppert's Delaunay refinement algorithm for quality meshing,
        ///     that edge is the shortest edge of the triangle whose circumcenter
        ///     is being inserted.
        ///     (2)  To try to find an existing point.  In this case, any edge on the
        ///     convex hull is a good starting edge. You must screen out the
        ///     possibility that the vertex sought is an endpoint of the starting
        ///     edge before you call preciselocate().
        ///     On completion, 'searchtri' is a triangle that contains 'searchpoint'.
        ///     This implementation differs from that given by Guibas and Stolfi.  It
        ///     walks from triangle to triangle, crossing an edge only if 'searchpoint'
        ///     is on the other side of the line containing that edge. After entering
        ///     a triangle, there are two edges by which one can leave that triangle.
        ///     If both edges are valid ('searchpoint' is on the other side of both
        ///     edges), one of the two is chosen by drawing a line perpendicular to
        ///     the entry edge (whose endpoints are 'forg' and 'fdest') passing through
        ///     'fapex'. Depending on which side of this perpendicular 'searchpoint'
        ///     falls on, an exit edge is chosen.
        ///     This implementation is empirically faster than the Guibas and Stolfi
        ///     point location routine (which I originally used), which tends to spiral
        ///     in toward its target.
        ///     Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
        ///     is a handle whose origin is the existing vertex.
        ///     Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
        ///     handle whose primary edge is the edge on which the point lies.
        ///     Returns INTRIANGLE if the point lies strictly within a triangle.
        ///     'searchtri' is a handle on the triangle that contains the point.
        ///     Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
        ///     handle whose primary edge the point is to the right of.  This might
        ///     occur when the circumcenter of a triangle falls just slightly outside
        ///     the mesh due to floating-point roundoff error. It also occurs when
        ///     seeking a hole or region point that a foolish user has placed outside
        ///     the mesh.
        ///     WARNING:  This routine is designed for convex triangulations, and will
        ///     not generally work after the holes and concavities have been carved.
        ///     However, it can still be used to find the circumcenter of a triangle, as
        ///     long as the search is begun from the triangle in question.
        /// </remarks>
        public LocateResult PreciseLocate(Point searchpoint, ref Otri searchtri,
                                          bool stopatsubsegment)
        {
            Otri   backtracktri = default(Otri);
            Osub   checkedge = default(Osub);
            Point  forg, fdest, fapex;
            double orgorient, destorient;
            bool   moveleft;

            // Where are we?
            forg  = searchtri.Org();
            fdest = searchtri.Dest();
            fapex = searchtri.Apex();
            while (true)
            {
                // Check whether the apex is the point we seek.
                if ((fapex.X == searchpoint.X) && (fapex.Y == searchpoint.Y))
                {
                    searchtri.Lprev();
                    return(LocateResult.OnVertex);
                }
                // Does the point lie on the other side of the line defined by the
                // triangle edge opposite the triangle's destination?
                destorient = RobustPredicates.CounterClockwise(forg, fapex, searchpoint);
                // Does the point lie on the other side of the line defined by the
                // triangle edge opposite the triangle's origin?
                orgorient = RobustPredicates.CounterClockwise(fapex, fdest, searchpoint);
                if (destorient > 0.0)
                {
                    if (orgorient > 0.0)
                    {
                        // Move left if the inner product of (fapex - searchpoint) and
                        // (fdest - forg) is positive.  This is equivalent to drawing
                        // a line perpendicular to the line (forg, fdest) and passing
                        // through 'fapex', and determining which side of this line
                        // 'searchpoint' falls on.
                        moveleft = (fapex.X - searchpoint.X) * (fdest.X - forg.X) +
                                   (fapex.Y - searchpoint.Y) * (fdest.Y - forg.Y) > 0.0;
                    }
                    else
                    {
                        moveleft = true;
                    }
                }
                else
                {
                    if (orgorient > 0.0)
                    {
                        moveleft = false;
                    }
                    else
                    {
                        // The point we seek must be on the boundary of or inside this
                        // triangle.
                        if (destorient == 0.0)
                        {
                            searchtri.Lprev();
                            return(LocateResult.OnEdge);
                        }
                        if (orgorient == 0.0)
                        {
                            searchtri.Lnext();
                            return(LocateResult.OnEdge);
                        }
                        return(LocateResult.InTriangle);
                    }
                }

                // Move to another triangle. Leave a trace 'backtracktri' in case
                // floating-point roundoff or some such bogey causes us to walk
                // off a boundary of the triangulation.
                if (moveleft)
                {
                    searchtri.Lprev(ref backtracktri);
                    fdest = fapex;
                }
                else
                {
                    searchtri.Lnext(ref backtracktri);
                    forg = fapex;
                }
                backtracktri.Sym(ref searchtri);

                if (mesh.checksegments && stopatsubsegment)
                {
                    // Check for walking through a subsegment.
                    backtracktri.Pivot(ref checkedge);
                    if (checkedge.seg.hash != Mesh.DUMMY)
                    {
                        // Go back to the last triangle.
                        backtracktri.Copy(ref searchtri);
                        return(LocateResult.Outside);
                    }
                }
                // Check for walking right out of the triangulation.
                if (searchtri.tri.Id == Mesh.DUMMY)
                {
                    // Go back to the last triangle.
                    backtracktri.Copy(ref searchtri);
                    return(LocateResult.Outside);
                }

                fapex = searchtri.Apex();
            }
        }
示例#13
0
        public Mesh Triangulate(List <Vertex> points)
        {
            mesh = TrianglePool.AllocMesh();
            mesh.TransferNodes(points);

            // Nonexistent x value used as a flag to mark circle events in sweepline
            // Delaunay algorithm.
            xminextreme = 10 * mesh.bounds.Left - 9 * mesh.bounds.Right;

            SweepEvent[] eventheap;

            SweepEvent nextevent;
            SweepEvent newevent;
            SplayNode  splayroot;
            Otri       bottommost = default(Otri);
            Otri       searchtri  = default(Otri);
            Otri       fliptri;
            Otri       lefttri = default(Otri);
            Otri       righttri = default(Otri);
            Otri       farlefttri = default(Otri);
            Otri       farrighttri = default(Otri);
            Otri       inserttri = default(Otri);
            Vertex     firstvertex, secondvertex;
            Vertex     nextvertex, lastvertex;
            Vertex     connectvertex;
            Vertex     leftvertex, midvertex, rightvertex;
            double     lefttest, righttest;
            int        heapsize;
            bool       check4events, farrightflag = false;

            splaynodes = new List <SplayNode>();
            splayroot  = null;

            CreateHeap(out eventheap); //, out events, out freeevents);
            heapsize = mesh.invertices;

            mesh.MakeTriangle(ref lefttri);
            mesh.MakeTriangle(ref righttri);
            lefttri.Bond(ref righttri);
            lefttri.Lnext();
            righttri.Lprev();
            lefttri.Bond(ref righttri);
            lefttri.Lnext();
            righttri.Lprev();
            lefttri.Bond(ref righttri);
            firstvertex = eventheap[0].vertexEvent;

            HeapDelete(eventheap, heapsize, 0);
            heapsize--;
            do
            {
                if (heapsize == 0)
                {
                    throw new Exception("Input vertices are all identical.");
                }

                secondvertex = eventheap[0].vertexEvent;
                HeapDelete(eventheap, heapsize, 0);
                heapsize--;
                if ((firstvertex.X == secondvertex.X) &&
                    (firstvertex.Y == secondvertex.Y))
                {
                    secondvertex.type = VertexType.UndeadVertex;
                    mesh.undeads++;
                }
            } while ((firstvertex.X == secondvertex.X) &&
                     (firstvertex.Y == secondvertex.Y));
            lefttri.SetOrg(firstvertex);
            lefttri.SetDest(secondvertex);
            righttri.SetOrg(secondvertex);
            righttri.SetDest(firstvertex);
            lefttri.Lprev(ref bottommost);
            lastvertex = secondvertex;

            while (heapsize > 0)
            {
                nextevent = eventheap[0];
                HeapDelete(eventheap, heapsize, 0);
                heapsize--;
                check4events = true;
                if (nextevent.xkey < mesh.bounds.Left)
                {
                    fliptri = nextevent.otriEvent;
                    fliptri.Oprev(ref farlefttri);
                    Check4DeadEvent(ref farlefttri, eventheap, ref heapsize);
                    fliptri.Onext(ref farrighttri);
                    Check4DeadEvent(ref farrighttri, eventheap, ref heapsize);

                    if (farlefttri.Equal(bottommost))
                    {
                        fliptri.Lprev(ref bottommost);
                    }
                    mesh.Flip(ref fliptri);
                    fliptri.SetApex(null);
                    fliptri.Lprev(ref lefttri);
                    fliptri.Lnext(ref righttri);
                    lefttri.Sym(ref farlefttri);

                    if (randomnation(SAMPLERATE) == 0)
                    {
                        fliptri.Sym();
                        leftvertex  = fliptri.Dest();
                        midvertex   = fliptri.Apex();
                        rightvertex = fliptri.Org();
                        splayroot   = CircleTopInsert(splayroot, lefttri, leftvertex, midvertex, rightvertex,
                                                      nextevent.ykey);
                    }
                }
                else
                {
                    nextvertex = nextevent.vertexEvent;
                    if ((nextvertex.X == lastvertex.X) &&
                        (nextvertex.Y == lastvertex.Y))
                    {
                        nextvertex.type = VertexType.UndeadVertex;
                        mesh.undeads++;
                        check4events = false;
                    }
                    else
                    {
                        lastvertex = nextvertex;

                        splayroot = FrontLocate(splayroot, bottommost, nextvertex, ref searchtri, ref farrightflag);

                        //bottommost.Copy(ref searchtri);
                        //farrightflag = false;
                        //while (!farrightflag && RightOfHyperbola(ref searchtri, nextvertex))
                        //{
                        //    searchtri.OnextSelf();
                        //    farrightflag = searchtri.Equal(bottommost);
                        //}

                        Check4DeadEvent(ref searchtri, eventheap, ref heapsize);

                        searchtri.Copy(ref farrighttri);
                        searchtri.Sym(ref farlefttri);
                        mesh.MakeTriangle(ref lefttri);
                        mesh.MakeTriangle(ref righttri);
                        connectvertex = farrighttri.Dest();
                        lefttri.SetOrg(connectvertex);
                        lefttri.SetDest(nextvertex);
                        righttri.SetOrg(nextvertex);
                        righttri.SetDest(connectvertex);
                        lefttri.Bond(ref righttri);
                        lefttri.Lnext();
                        righttri.Lprev();
                        lefttri.Bond(ref righttri);
                        lefttri.Lnext();
                        righttri.Lprev();
                        lefttri.Bond(ref farlefttri);
                        righttri.Bond(ref farrighttri);
                        if (!farrightflag && farrighttri.Equal(bottommost))
                        {
                            lefttri.Copy(ref bottommost);
                        }

                        if (randomnation(SAMPLERATE) == 0)
                        {
                            splayroot = SplayInsert(splayroot, lefttri, nextvertex);
                        }
                        else if (randomnation(SAMPLERATE) == 0)
                        {
                            righttri.Lnext(ref inserttri);
                            splayroot = SplayInsert(splayroot, inserttri, nextvertex);
                        }
                    }
                }

                if (check4events)
                {
                    leftvertex  = farlefttri.Apex();
                    midvertex   = lefttri.Dest();
                    rightvertex = lefttri.Apex();
                    lefttest    = RobustPredicates.CounterClockwise(leftvertex, midvertex, rightvertex);
                    if (lefttest > 0.0)
                    {
                        newevent = new SweepEvent();

                        newevent.xkey      = xminextreme;
                        newevent.ykey      = CircleTop(leftvertex, midvertex, rightvertex, lefttest);
                        newevent.otriEvent = lefttri;
                        HeapInsert(eventheap, heapsize, newevent);
                        heapsize++;
                        lefttri.SetOrg(new SweepEventVertex(newevent));
                    }
                    leftvertex  = righttri.Apex();
                    midvertex   = righttri.Org();
                    rightvertex = farrighttri.Apex();
                    righttest   = RobustPredicates.CounterClockwise(leftvertex, midvertex, rightvertex);
                    if (righttest > 0.0)
                    {
                        newevent = new SweepEvent();

                        newevent.xkey      = xminextreme;
                        newevent.ykey      = CircleTop(leftvertex, midvertex, rightvertex, righttest);
                        newevent.otriEvent = farrighttri;
                        HeapInsert(eventheap, heapsize, newevent);
                        heapsize++;
                        farrighttri.SetOrg(new SweepEventVertex(newevent));
                    }
                }
            }

            splaynodes.Clear();
            bottommost.Lprev();

            mesh.hullsize = RemoveGhosts(ref bottommost);

            return(mesh);
        }
示例#14
0
        /// <summary>
        ///     Inserts a vertex at the circumcenter of a triangle. Deletes
        ///     the newly inserted vertex if it encroaches upon a segment.
        /// </summary>
        /// <param name="badtri"></param>
        private void SplitTriangle(BadTriangle badtri)
        {
            Otri               badotri = default(Otri);
            Vertex             borg, bdest, bapex;
            Point              newloc; // Location of the new vertex
            double             xi = 0, eta = 0;
            InsertVertexResult success;
            bool               errorflag;

            badotri = badtri.poortri;
            borg    = badotri.Org();
            bdest   = badotri.Dest();
            bapex   = badotri.Apex();

            // Make sure that this triangle is still the same triangle it was
            // when it was tested and determined to be of bad quality.
            // Subsequent transformations may have made it a different triangle.
            if (!Otri.IsDead(badotri.tri) && (borg == badtri.org) &&
                (bdest == badtri.dest) && (bapex == badtri.apex))
            {
                errorflag = false;
                // Create a new vertex at the triangle's circumcenter.

                // Using the original (simpler) Steiner point location method
                // for mesh refinement.
                // TODO: NewLocation doesn't work for refinement. Why? Maybe
                // reset VertexType?
                newloc = RobustPredicates.FindCircumcenter(borg, bdest, bapex, ref xi, ref eta, behavior.offconstant);

                // Check whether the new vertex lies on a triangle vertex.
                if (((newloc.X == borg.X) && (newloc.Y == borg.Y)) ||
                    ((newloc.X == bdest.X) && (newloc.Y == bdest.Y)) ||
                    ((newloc.X == bapex.X) && (newloc.Y == bapex.Y)))
                {
                    //errorflag = true;
                }
                else
                {
                    // The new vertex must be in the interior, and therefore is a
                    // free vertex with a marker of zero.
                    Vertex newvertex = new Vertex(newloc.X, newloc.Y, 0);
                    newvertex.type = VertexType.FreeVertex;

                    // Ensure that the handle 'badotri' does not represent the longest
                    // edge of the triangle.  This ensures that the circumcenter must
                    // fall to the left of this edge, so point location will work.
                    // (If the angle org-apex-dest exceeds 90 degrees, then the
                    // circumcenter lies outside the org-dest edge, and eta is
                    // negative.  Roundoff error might prevent eta from being
                    // negative when it should be, so I test eta against xi.)
                    if (eta < xi)
                    {
                        badotri.Lprev();
                    }

                    // Insert the circumcenter, searching from the edge of the triangle,
                    // and maintain the Delaunay property of the triangulation.
                    Osub tmp = default(Osub);
                    success = mesh.InsertVertex(newvertex, ref badotri, ref tmp, true, true);

                    if (success == InsertVertexResult.Successful)
                    {
                        newvertex.Id = mesh.hash_vtx++;

                        mesh.vertices.Add(newvertex.Id, newvertex);

                        if (mesh.steinerleft > 0)
                        {
                            mesh.steinerleft--;
                        }
                    }
                    else if (success == InsertVertexResult.Encroaching)
                    {
                        // If the newly inserted vertex encroaches upon a subsegment,
                        // delete the new vertex.
                        mesh.UndoVertex();
                    }
                    else if (success == InsertVertexResult.Violating)
                    {
                        // Failed to insert the new vertex, but some subsegment was
                        // marked as being encroached.
                    }
                    else
                    {
                        //errorflag = true;
                    }
                }
                if (errorflag)
                {
                    throw new Exception("The new vertex is at the circumcenter of triangle.");
                }
            }
        }
示例#15
0
        /// <summary>
        ///     Split all the encroached subsegments.
        /// </summary>
        /// <param name="triflaws">
        ///     A flag that specifies whether one should take
        ///     note of new bad triangles that result from inserting vertices to repair
        ///     encroached subsegments.
        /// </param>
        /// <remarks>
        ///     Each encroached subsegment is repaired by splitting it - inserting a
        ///     vertex at or near its midpoint.  Newly inserted vertices may encroach
        ///     upon other subsegments; these are also repaired.
        /// </remarks>
        private void SplitEncSegs(bool triflaws)
        {
            Otri               enctri     = default(Otri);
            Otri               testtri    = default(Otri);
            Osub               testsh     = default(Osub);
            Osub               currentenc = default(Osub);
            BadSubseg          seg;
            Vertex             eorg, edest, eapex;
            Vertex             newvertex;
            InsertVertexResult success;
            double             segmentlength, nearestpoweroftwo;
            double             split;
            double             multiplier, divisor;
            bool               acuteorg, acuteorg2, acutedest, acutedest2;

            // Note that steinerleft == -1 if an unlimited number
            // of Steiner points is allowed.
            while (badsubsegs.Count > 0)
            {
                if (mesh.steinerleft == 0)
                {
                    break;
                }

                seg = badsubsegs.Dequeue();

                currentenc = seg.subseg;
                eorg       = currentenc.Org();
                edest      = currentenc.Dest();
                // Make sure that this segment is still the same segment it was
                // when it was determined to be encroached.  If the segment was
                // enqueued multiple times (because several newly inserted
                // vertices encroached it), it may have already been split.
                if (!Osub.IsDead(currentenc.seg) && (eorg == seg.org) && (edest == seg.dest))
                {
                    // To decide where to split a segment, we need to know if the
                    // segment shares an endpoint with an adjacent segment.
                    // The concern is that, if we simply split every encroached
                    // segment in its center, two adjacent segments with a small
                    // angle between them might lead to an infinite loop; each
                    // vertex added to split one segment will encroach upon the
                    // other segment, which must then be split with a vertex that
                    // will encroach upon the first segment, and so on forever.
                    // To avoid this, imagine a set of concentric circles, whose
                    // radii are powers of two, about each segment endpoint.
                    // These concentric circles determine where the segment is
                    // split. (If both endpoints are shared with adjacent
                    // segments, split the segment in the middle, and apply the
                    // concentric circles for later splittings.)

                    // Is the origin shared with another segment?
                    currentenc.Pivot(ref enctri);
                    enctri.Lnext(ref testtri);
                    testtri.Pivot(ref testsh);
                    acuteorg = testsh.seg.hash != Mesh.DUMMY;
                    // Is the destination shared with another segment?
                    testtri.Lnext();
                    testtri.Pivot(ref testsh);
                    acutedest = testsh.seg.hash != Mesh.DUMMY;

                    // If we're using Chew's algorithm (rather than Ruppert's)
                    // to define encroachment, delete free vertices from the
                    // subsegment's diametral circle.
                    if (!behavior.ConformingDelaunay && !acuteorg && !acutedest)
                    {
                        eapex = enctri.Apex();
                        while ((eapex.type == VertexType.FreeVertex) &&
                               ((eorg.X - eapex.X) * (edest.X - eapex.X) +
                                (eorg.Y - eapex.Y) * (edest.Y - eapex.Y) < 0.0))
                        {
                            mesh.DeleteVertex(ref testtri);
                            currentenc.Pivot(ref enctri);
                            eapex = enctri.Apex();
                            enctri.Lprev(ref testtri);
                        }
                    }

                    // Now, check the other side of the segment, if there's a triangle there.
                    enctri.Sym(ref testtri);
                    if (testtri.tri.Id != Mesh.DUMMY)
                    {
                        // Is the destination shared with another segment?
                        testtri.Lnext();
                        testtri.Pivot(ref testsh);
                        acutedest2 = testsh.seg.hash != Mesh.DUMMY;
                        acutedest  = acutedest || acutedest2;
                        // Is the origin shared with another segment?
                        testtri.Lnext();
                        testtri.Pivot(ref testsh);
                        acuteorg2 = testsh.seg.hash != Mesh.DUMMY;
                        acuteorg  = acuteorg || acuteorg2;

                        // Delete free vertices from the subsegment's diametral circle.
                        if (!behavior.ConformingDelaunay && !acuteorg2 && !acutedest2)
                        {
                            eapex = testtri.Org();
                            while ((eapex.type == VertexType.FreeVertex) &&
                                   ((eorg.X - eapex.X) * (edest.X - eapex.X) +
                                    (eorg.Y - eapex.Y) * (edest.Y - eapex.Y) < 0.0))
                            {
                                mesh.DeleteVertex(ref testtri);
                                enctri.Sym(ref testtri);
                                eapex = testtri.Apex();
                                testtri.Lprev();
                            }
                        }
                    }

                    // Use the concentric circles if exactly one endpoint is shared
                    // with another adjacent segment.
                    if (acuteorg || acutedest)
                    {
                        segmentlength = Math.Sqrt((edest.X - eorg.X) * (edest.X - eorg.X) +
                                                  (edest.Y - eorg.Y) * (edest.Y - eorg.Y));
                        // Find the power of two that most evenly splits the segment.
                        // The worst case is a 2:1 ratio between subsegment lengths.
                        nearestpoweroftwo = 1.0;
                        while (segmentlength > 3.0 * nearestpoweroftwo)
                        {
                            nearestpoweroftwo *= 2.0;
                        }
                        while (segmentlength < 1.5 * nearestpoweroftwo)
                        {
                            nearestpoweroftwo *= 0.5;
                        }
                        // Where do we split the segment?
                        split = nearestpoweroftwo / segmentlength;
                        if (acutedest)
                        {
                            split = 1.0 - split;
                        }
                    }
                    else
                    {
                        // If we're not worried about adjacent segments, split
                        // this segment in the middle.
                        split = 0.5;
                    }

                    // Create the new vertex (interpolate coordinates).
                    newvertex = new Vertex(
                        eorg.X + split * (edest.X - eorg.X),
                        eorg.Y + split * (edest.Y - eorg.Y),
                        currentenc.seg.boundary);

                    newvertex.type = VertexType.SegmentVertex;

                    newvertex.Id = mesh.hash_vtx++;

                    mesh.vertices.Add(newvertex.Id, newvertex);

                    if (!Behavior.NoExact)
                    {
                        // Roundoff in the above calculation may yield a 'newvertex'
                        // that is not precisely collinear with 'eorg' and 'edest'.
                        // Improve collinearity by one step of iterative refinement.
                        multiplier = RobustPredicates.CounterClockwise(eorg, edest, newvertex);
                        divisor    = ((eorg.X - edest.X) * (eorg.X - edest.X) +
                                      (eorg.Y - edest.Y) * (eorg.Y - edest.Y));
                        if ((multiplier != 0.0) && (divisor != 0.0))
                        {
                            multiplier = multiplier / divisor;
                            // Watch out for NANs.
                            if (!double.IsNaN(multiplier))
                            {
                                newvertex.X += multiplier * (edest.Y - eorg.Y);
                                newvertex.Y += multiplier * (eorg.X - edest.X);
                            }
                        }
                    }

                    // Check whether the new vertex lies on an endpoint.
                    if (((newvertex.X == eorg.X) && (newvertex.Y == eorg.Y)) ||
                        ((newvertex.X == edest.X) && (newvertex.Y == edest.Y)))
                    {
                        throw new Exception("Ran out of precision");
                    }
                    // Insert the splitting vertex.  This should always succeed.
                    success = mesh.InsertVertex(newvertex, ref enctri, ref currentenc, true, triflaws);
                    if ((success != InsertVertexResult.Successful) && (success != InsertVertexResult.Encroaching))
                    {
                        throw new Exception("Failure to split a segment.");
                    }
                    if (mesh.steinerleft > 0)
                    {
                        mesh.steinerleft--;
                    }
                    // Check the two new subsegments to see if they're encroached.
                    CheckSeg4Encroach(ref currentenc);
                    currentenc.Next();
                    CheckSeg4Encroach(ref currentenc);
                }

                // Set subsegment's origin to NULL. This makes it possible to detect dead
                // badsubsegs when traversing the list of all badsubsegs.
                seg.org = null;
            }
        }
示例#16
0
        public static TriangleNet.Geometry.Point FindPointInPolygon(SectionContour contour, List <SectionContour> otherContours,
                                                                    int limit, double eps = 2e-5)
        {
            List <Vertex> poly   = contour.Points.Select(p => new Vertex(p.X, p.Y)).ToList();
            var           bounds = new TriangleNet.Geometry.Rectangle();

            bounds.Expand(poly);

            int length = poly.Count;

            var test = new TriangleNet.Geometry.Point();

            TriangleNet.Geometry.Point a, b, c; // Current corner points.

            double bx, by;
            double dx, dy;
            double h;

            var predicates = new RobustPredicates();

            a = poly[0];
            b = poly[1];

            for (int i = 0; i < length; i++)
            {
                c = poly[(i + 2) % length];

                // Corner point.
                bx = b.X;
                by = b.Y;

                // NOTE: if we knew the contour points were in counterclockwise order, we
                // could skip concave corners and search only in one direction.

                h = predicates.CounterClockwise(a, b, c);

                if (Math.Abs(h) < eps)
                {
                    // Points are nearly co-linear. Use perpendicular direction.
                    dx = (c.Y - a.Y) / 2;
                    dy = (a.X - c.X) / 2;
                }
                else
                {
                    // Direction [midpoint(a-c) -> corner point]
                    dx = (a.X + c.X) / 2 - bx;
                    dy = (a.Y + c.Y) / 2 - by;
                }

                // Move around the contour.
                a = b;
                b = c;

                h = 1.0;

                for (int j = 0; j < limit; j++)
                {
                    // Search in direction.
                    test.X = bx + dx * h;
                    test.Y = by + dy * h;

                    if (bounds.Contains(test) && IsPointInPolygon(test, contour, otherContours))
                    {
                        return(test);
                    }

                    // Search in opposite direction (see NOTE above).
                    test.X = bx - dx * h;
                    test.Y = by - dy * h;

                    if (bounds.Contains(test) && IsPointInPolygon(test, contour, otherContours))
                    {
                        return(test);
                    }

                    h = h / 2;
                }
            }

            throw new Exception();
        }
示例#17
0
        /// <summary>
        /// Recursively form a Delaunay triangulation by the divide-and-conquer method.
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <param name="axis"></param>
        /// <param name="farleft"></param>
        /// <param name="farright"></param>
        /// <remarks>
        /// Recursively breaks down the problem into smaller pieces, which are
        /// knitted together by mergehulls(). The base cases (problems of two or
        /// three vertices) are handled specially here.
        ///
        /// On completion, 'farleft' and 'farright' are bounding triangles such that
        /// the origin of 'farleft' is the leftmost vertex (breaking ties by
        /// choosing the highest leftmost vertex), and the destination of
        /// 'farright' is the rightmost vertex (breaking ties by choosing the
        /// lowest rightmost vertex).
        /// </remarks>
        void DivconqRecurse(int left, int right, int axis,
                            ref Otri farleft, ref Otri farright)
        {
            Otri   midtri = default(Otri);
            Otri   tri1 = default(Otri);
            Otri   tri2 = default(Otri);
            Otri   tri3 = default(Otri);
            Otri   innerleft = default(Otri), innerright = default(Otri);
            double area;
            int    vertices = right - left + 1;
            int    divider;

            if (vertices == 2)
            {
                // The triangulation of two vertices is an edge.  An edge is
                // represented by two bounding triangles.
                mesh.MakeTriangle(ref farleft);
                farleft.SetOrg(sortarray[left]);
                farleft.SetDest(sortarray[left + 1]);
                // The apex is intentionally left NULL.
                mesh.MakeTriangle(ref farright);
                farright.SetOrg(sortarray[left + 1]);
                farright.SetDest(sortarray[left]);
                // The apex is intentionally left NULL.
                farleft.Bond(ref farright);
                farleft.Lprev();
                farright.Lnext();
                farleft.Bond(ref farright);
                farleft.Lprev();
                farright.Lnext();
                farleft.Bond(ref farright);

                // Ensure that the origin of 'farleft' is sortarray[0].
                farright.Lprev(ref farleft);
                return;
            }
            else if (vertices == 3)
            {
                // The triangulation of three vertices is either a triangle (with
                // three bounding triangles) or two edges (with four bounding
                // triangles).  In either case, four triangles are created.
                mesh.MakeTriangle(ref midtri);
                mesh.MakeTriangle(ref tri1);
                mesh.MakeTriangle(ref tri2);
                mesh.MakeTriangle(ref tri3);
                area = RobustPredicates.CounterClockwise(sortarray[left], sortarray[left + 1], sortarray[left + 2]);
                if (area == 0.0)
                {
                    // Three collinear vertices; the triangulation is two edges.
                    midtri.SetOrg(sortarray[left]);
                    midtri.SetDest(sortarray[left + 1]);
                    tri1.SetOrg(sortarray[left + 1]);
                    tri1.SetDest(sortarray[left]);
                    tri2.SetOrg(sortarray[left + 2]);
                    tri2.SetDest(sortarray[left + 1]);
                    tri3.SetOrg(sortarray[left + 1]);
                    tri3.SetDest(sortarray[left + 2]);
                    // All apices are intentionally left NULL.
                    midtri.Bond(ref tri1);
                    tri2.Bond(ref tri3);
                    midtri.Lnext();
                    tri1.Lprev();
                    tri2.Lnext();
                    tri3.Lprev();
                    midtri.Bond(ref tri3);
                    tri1.Bond(ref tri2);
                    midtri.Lnext();
                    tri1.Lprev();
                    tri2.Lnext();
                    tri3.Lprev();
                    midtri.Bond(ref tri1);
                    tri2.Bond(ref tri3);
                    // Ensure that the origin of 'farleft' is sortarray[0].
                    tri1.Copy(ref farleft);
                    // Ensure that the destination of 'farright' is sortarray[2].
                    tri2.Copy(ref farright);
                }
                else
                {
                    // The three vertices are not collinear; the triangulation is one
                    // triangle, namely 'midtri'.
                    midtri.SetOrg(sortarray[left]);
                    tri1.SetDest(sortarray[left]);
                    tri3.SetOrg(sortarray[left]);
                    // Apices of tri1, tri2, and tri3 are left NULL.
                    if (area > 0.0)
                    {
                        // The vertices are in counterclockwise order.
                        midtri.SetDest(sortarray[left + 1]);
                        tri1.SetOrg(sortarray[left + 1]);
                        tri2.SetDest(sortarray[left + 1]);
                        midtri.SetApex(sortarray[left + 2]);
                        tri2.SetOrg(sortarray[left + 2]);
                        tri3.SetDest(sortarray[left + 2]);
                    }
                    else
                    {
                        // The vertices are in clockwise order.
                        midtri.SetDest(sortarray[left + 2]);
                        tri1.SetOrg(sortarray[left + 2]);
                        tri2.SetDest(sortarray[left + 2]);
                        midtri.SetApex(sortarray[left + 1]);
                        tri2.SetOrg(sortarray[left + 1]);
                        tri3.SetDest(sortarray[left + 1]);
                    }
                    // The topology does not depend on how the vertices are ordered.
                    midtri.Bond(ref tri1);
                    midtri.Lnext();
                    midtri.Bond(ref tri2);
                    midtri.Lnext();
                    midtri.Bond(ref tri3);
                    tri1.Lprev();
                    tri2.Lnext();
                    tri1.Bond(ref tri2);
                    tri1.Lprev();
                    tri3.Lprev();
                    tri1.Bond(ref tri3);
                    tri2.Lnext();
                    tri3.Lprev();
                    tri2.Bond(ref tri3);
                    // Ensure that the origin of 'farleft' is sortarray[0].
                    tri1.Copy(ref farleft);
                    // Ensure that the destination of 'farright' is sortarray[2].
                    if (area > 0.0)
                    {
                        tri2.Copy(ref farright);
                    }
                    else
                    {
                        farleft.Lnext(ref farright);
                    }
                }

                return;
            }
            else
            {
                // Split the vertices in half.
                divider = vertices >> 1;
                // Recursively triangulate each half.
                DivconqRecurse(left, left + divider - 1, 1 - axis, ref farleft, ref innerleft);
                //DebugWriter.Session.Write(mesh, true);
                DivconqRecurse(left + divider, right, 1 - axis, ref innerright, ref farright);
                //DebugWriter.Session.Write(mesh, true);

                // Merge the two triangulations into one.
                MergeHulls(ref farleft, ref innerleft, ref innerright, ref farright, axis);
                //DebugWriter.Session.Write(mesh, true);
            }
        }
示例#18
0
        private static Point FindPointInPolygon(List <Vertex> contour, int limit, double eps)
        {
            var bounds = new Rectangle();

            bounds.Expand(contour.ConvertAll(v => new Point(v.x, v.y, v.label)));

            int length = contour.Count;

            var test = new Point();

            Point a, b, c; // Current corner points.

            double bx, by;
            double dx, dy;
            double h;

            var predicates = new RobustPredicates();

            a = contour[0];
            b = contour[1];

            for (int i = 0; i < length; i++)
            {
                c = contour[(i + 2) % length];

                // Corner point.
                bx = b.x;
                by = b.y;

                // NOTE: if we knew the contour points were in counterclockwise order, we
                // could skip concave corners and search only in one direction.

                h = predicates.CounterClockwise(a, b, c);

                if (Math.Abs(h) < eps)
                {
                    // Points are nearly co-linear. Use perpendicular direction.
                    dx = (c.y - a.y) / 2;
                    dy = (a.x - c.x) / 2;
                }
                else
                {
                    // Direction [midpoint(a-c) -> corner point]
                    dx = (a.x + c.x) / 2 - bx;
                    dy = (a.y + c.y) / 2 - by;
                }

                // Move around the contour.
                a = b;
                b = c;

                h = 1.0;

                for (int j = 0; j < limit; j++)
                {
                    // Search in direction.
                    test.x = bx + dx * h;
                    test.y = by + dy * h;

                    if (bounds.Contains(test) && IsPointInPolygon(test, contour))
                    {
                        return(test);
                    }

                    // Search in opposite direction (see NOTE above).
                    test.x = bx - dx * h;
                    test.y = by - dy * h;

                    if (bounds.Contains(test) && IsPointInPolygon(test, contour))
                    {
                        return(test);
                    }

                    h = h / 2;
                }
            }

            throw new Exception();
        }
示例#19
0
        /// <summary> Find a triangle or edge containing a given point. </summary>
        /// <param name="searchpoint">The point to locate.</param>
        /// <param name="searchtri">The triangle to start the search at.</param>
        /// <returns>Location information.</returns>
        /// <remarks>
        ///     Searching begins from one of:  the input 'searchtri', a recently
        ///     encountered triangle 'recenttri', or from a triangle chosen from a
        ///     random sample. The choice is made by determining which triangle's
        ///     origin is closest to the point we are searching for. Normally,
        ///     'searchtri' should be a handle on the convex hull of the triangulation.
        ///     Details on the random sampling method can be found in the Mucke, Saias,
        ///     and Zhu paper cited in the header of this code.
        ///     On completion, 'searchtri' is a triangle that contains 'searchpoint'.
        ///     Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
        ///     is a handle whose origin is the existing vertex.
        ///     Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
        ///     handle whose primary edge is the edge on which the point lies.
        ///     Returns INTRIANGLE if the point lies strictly within a triangle.
        ///     'searchtri' is a handle on the triangle that contains the point.
        ///     Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
        ///     handle whose primary edge the point is to the right of.  This might
        ///     occur when the circumcenter of a triangle falls just slightly outside
        ///     the mesh due to floating-point roundoff error. It also occurs when
        ///     seeking a hole or region point that a foolish user has placed outside
        ///     the mesh.
        ///     WARNING:  This routine is designed for convex triangulations, and will
        ///     not generally work after the holes and concavities have been carved.
        /// </remarks>
        public LocateResult Locate(Point searchpoint, ref Otri searchtri)
        {
            Otri   sampletri = default(Otri);
            Vertex torg, tdest;
            double searchdist, dist;
            double ahead;

            // Record the distance from the suggested starting triangle to the
            // point we seek.
            torg       = searchtri.Org();
            searchdist = (searchpoint.X - torg.X) * (searchpoint.X - torg.X) +
                         (searchpoint.Y - torg.Y) * (searchpoint.Y - torg.Y);

            // If a recently encountered triangle has been recorded and has not been
            // deallocated, test it as a good starting point.
            if (recenttri.tri != null)
            {
                if (!Otri.IsDead(recenttri.tri))
                {
                    torg = recenttri.Org();
                    if ((torg.X == searchpoint.X) && (torg.Y == searchpoint.Y))
                    {
                        recenttri.Copy(ref searchtri);
                        return(LocateResult.OnVertex);
                    }
                    dist = (searchpoint.X - torg.X) * (searchpoint.X - torg.X) +
                           (searchpoint.Y - torg.Y) * (searchpoint.Y - torg.Y);
                    if (dist < searchdist)
                    {
                        recenttri.Copy(ref searchtri);
                        searchdist = dist;
                    }
                }
            }

            // TODO: Improve sampling.
            sampler.Update(mesh);
            var samples = sampler.GetSamples(mesh);

            foreach (var key in samples)
            {
                sampletri.tri = mesh.triangles[key];
                if (!Otri.IsDead(sampletri.tri))
                {
                    torg = sampletri.Org();
                    dist = (searchpoint.X - torg.X) * (searchpoint.X - torg.X) +
                           (searchpoint.Y - torg.Y) * (searchpoint.Y - torg.Y);
                    if (dist < searchdist)
                    {
                        sampletri.Copy(ref searchtri);
                        searchdist = dist;
                    }
                }
            }

            // Where are we?
            torg  = searchtri.Org();
            tdest = searchtri.Dest();
            // Check the starting triangle's vertices.
            if ((torg.X == searchpoint.X) && (torg.Y == searchpoint.Y))
            {
                return(LocateResult.OnVertex);
            }
            if ((tdest.X == searchpoint.X) && (tdest.Y == searchpoint.Y))
            {
                searchtri.Lnext();
                return(LocateResult.OnVertex);
            }
            // Orient 'searchtri' to fit the preconditions of calling preciselocate().
            ahead = RobustPredicates.CounterClockwise(torg, tdest, searchpoint);
            if (ahead < 0.0)
            {
                // Turn around so that 'searchpoint' is to the left of the
                // edge specified by 'searchtri'.
                searchtri.Sym();
            }
            else if (ahead == 0.0)
            {
                // Check if 'searchpoint' is between 'torg' and 'tdest'.
                if (((torg.X < searchpoint.X) == (searchpoint.X < tdest.X)) &&
                    ((torg.Y < searchpoint.Y) == (searchpoint.Y < tdest.Y)))
                {
                    return(LocateResult.OnEdge);
                }
            }
            return(PreciseLocate(searchpoint, ref searchtri, false));
        }