예제 #1
0
        /// <summary>
        ///     Form a Delaunay triangulation by incrementally inserting vertices.
        /// </summary>
        /// <returns>
        ///     Returns the number of edges on the convex hull of the
        ///     triangulation.
        /// </returns>
        public Mesh Triangulate(List <Vertex> points)
        {
            mesh = TrianglePool.AllocMesh();
            mesh.TransferNodes(points);

            Otri starttri = new Otri();

            // Create a triangular bounding box.
            GetBoundingBox();

            foreach (var v in mesh.vertices.Values)
            {
                starttri.tri = mesh.dummytri;
                Osub tmp = default(Osub);
                if (mesh.InsertVertex(v, ref starttri, ref tmp, false, false) == InsertVertexResult.Duplicate)
                {
                    v.type = VertexType.UndeadVertex;
                    mesh.undeads++;
                }
            }

            // Remove the bounding box.
            mesh.hullsize = RemoveBox();

            return(mesh);
        }
예제 #2
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);
        }
예제 #3
0
        public void TestToArray()
        {
            var pool = new TrianglePool();

            // Create 4 triangles.
            pool.Get();
            pool.Get();
            pool.Get();
            pool.Get();

            var a = pool.ToArray();

            Assert.AreEqual(4, a.Length);
            Assert.AreEqual(0, a[0].ID);
            Assert.AreEqual(1, a[1].ID);
            Assert.AreEqual(2, a[2].ID);
            Assert.AreEqual(3, a[3].ID);

            pool.Release(a[1]);

            a = pool.ToArray();

            Assert.AreEqual(3, a.Length);
            Assert.AreEqual(0, a[0].ID);
            Assert.AreEqual(2, a[1].ID);
            Assert.AreEqual(3, a[2].ID);

            pool.Release(a[1]);

            a = pool.ToArray();

            Assert.AreEqual(2, a.Length);
            Assert.AreEqual(0, a[0].ID);
            Assert.AreEqual(3, a[1].ID);

            var t2 = pool.Get();

            a = pool.ToArray();

            Assert.AreEqual(3, a.Length);
            Assert.AreEqual(0, a[0].ID);
            Assert.AreEqual(2, a[1].ID);
            Assert.AreEqual(2, t2.ID);
            Assert.AreEqual(3, a[2].ID);

            var t1 = pool.Get();

            a = pool.ToArray();

            Assert.AreEqual(4, a.Length);
            Assert.AreEqual(0, a[0].ID);
            Assert.AreEqual(1, a[1].ID);
            Assert.AreEqual(1, t1.ID);
            Assert.AreEqual(2, a[2].ID);
            Assert.AreEqual(3, a[3].ID);
        }
예제 #4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SimpleSmoother" /> class.
        /// </summary>
        public SimpleSmoother(IVoronoiFactory factory)
        {
            this.factory = factory;
            this.pool = new TrianglePool();

            this.config = new Configuration(
                () => RobustPredicates.Default,
                () => pool.Restart());

            this.options = new ConstraintOptions() { ConformingDelaunay = true };
        }
예제 #5
0
        /// <summary> Add a bad triangle to the end of a queue. </summary>
        /// <param name="enqtri"></param>
        /// <param name="minedge"></param>
        /// <param name="apex"></param>
        /// <param name="org"></param>
        /// <param name="dest"></param>
        public void Enqueue(ref Otri enqtri, double minedge, Vertex apex, Vertex org, Vertex dest)
        {
            // Allocate space for the bad triangle.
            BadTriangle newbad = TrianglePool.AllocBadTri();

            newbad.poortri = enqtri;
            newbad.key     = minedge;
            newbad.apex    = apex;
            newbad.org     = org;
            newbad.dest    = dest;

            Enqueue(newbad);
        }
예제 #6
0
        /// <summary>
        /// Form a Delaunay triangulation by the divide-and-conquer method.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// Sorts the vertices, calls a recursive procedure to triangulate them, and
        /// removes the bounding box, setting boundary markers as appropriate.
        /// </remarks>
        public Mesh Triangulate(List <Vertex> points)
        {
            this.mesh = TrianglePool.AllocMesh();
            this.mesh.TransferNodes(points);

            Otri hullleft = default(Otri), hullright = default(Otri);
            int  divider;
            int  i, j, n = points.Count;

            sortarray = points;
            VertexSort(0, n - 1);
            // Discard duplicate vertices, which can really mess up the algorithm.
            i = 0;
            for (j = 1; j < n; j++)
            {
                if ((sortarray[i].X == sortarray[j].X) && (sortarray[i].Y == sortarray[j].Y))
                {
                    sortarray[j].type = VertexType.UndeadVertex;
                    mesh.undeads++;
                }
                else
                {
                    i++;
                    sortarray[i] = sortarray[j];
                }
            }
            i++;
            if (UseDwyer)
            {
                // Re-sort the array of vertices to accommodate alternating cuts.
                divider = i >> 1;
                if (i - divider >= 2)
                {
                    if (divider >= 2)
                    {
                        AlternateAxes(0, divider - 1, 1);
                    }
                    AlternateAxes(divider, i - 1, 1);
                }
            }

            // Form the Delaunay triangulation.
            DivconqRecurse(0, i - 1, 0, ref hullleft, ref hullright);

            //DebugWriter.Session.Write(mesh);
            //DebugWriter.Session.Finish();

            this.mesh.hullsize = RemoveGhosts(ref hullleft);

            return(this.mesh);
        }
예제 #7
0
        /// <summary>
        ///     Remove all the encroached subsegments and bad triangles from the triangulation.
        /// </summary>
        public void EnforceQuality()
        {
            BadTriangle badtri;

            // Test all segments to see if they're encroached.
            TallyEncs();

            // Fix encroached subsegments without noting bad triangles.
            SplitEncSegs(false);
            // At this point, if we haven't run out of Steiner points, the
            // triangulation should be (conforming) Delaunay.

            // Next, we worry about enforcing triangle quality.
            if ((behavior.MinAngle > 0.0) || behavior.VarArea || behavior.fixedArea || behavior.UserTest != null)
            {
                // TODO: Reset queue? (Or is it always empty at this point)

                // Test all triangles to see if they're bad.
                TallyFaces();

                mesh.checkquality = true;
                while ((queue.Count > 0) && (mesh.steinerleft != 0))
                {
                    // Fix one bad triangle by inserting a vertex at its circumcenter.
                    badtri = queue.Dequeue();
                    SplitTriangle(badtri);

                    if (badsubsegs.Count > 0)
                    {
                        // Put bad triangle back in queue for another try later.
                        queue.Enqueue(badtri);
                        // Fix any encroached subsegments that resulted.
                        // Record any new bad triangles that result.
                        SplitEncSegs(true);
                    }
                    else
                    {
                        TrianglePool.FreeBadTri(badtri);
                    }
                }
            }

            // At this point, if the "-D" switch was selected and we haven't run out
            // of Steiner points, the triangulation should be (conforming) Delaunay
            // and have no low-quality triangles.

            // Might we have run out of Steiner points too soon?
            if (behavior.ConformingDelaunay && (badsubsegs.Count > 0) && (mesh.steinerleft == 0))
            {
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="SimpleSmoother" /> class.
        /// </summary>
        public SimpleSmoother(IVoronoiFactory factory)
        {
            this.factory = factory;
            this.pool    = new TrianglePool();

            this.config = new Configuration(
                () => RobustPredicates.Default,
                () => pool.Restart());

            this.options = new ConstraintOptions()
            {
                ConformingDelaunay = true
            };
        }
    private void Start()
    {
        halfHeight    = Camera.main.orthographicSize;
        halfWidth     = Camera.main.orthographicSize * Camera.main.aspect;
        points        = new List <Point>();
        trianglePool  = new TrianglePool();
        triangulation = new HashSet <Triangle>();
        GeneratePoints();
        Triangulate();

        // Unity sets a maximum delta time to avoid abnormal behaviour in games
        // this triangulation can go slow with high number of points, so we set it
        // to virtually unlimited
        Time.maximumDeltaTime = 10.0f;
    }
예제 #10
0
        public void TestRestart()
        {
            var pool = new TrianglePool();

            Assert.AreEqual(0, pool.Count);
            Assert.AreEqual(0, pool.Capacity);

            int n = 10;

            for (int i = 0; i < n; i++)
            {
                Assert.AreEqual(i, pool.Get().ID);
            }

            Assert.AreEqual(n, pool.Count);

            pool.Restart();

            Assert.AreEqual(0, pool.Count);
            Assert.AreEqual(10, pool.Capacity);
        }
예제 #11
0
        public void TestGetRelease()
        {
            var pool = new TrianglePool();

            var t0 = pool.Get();
            var t1 = pool.Get();
            var t2 = pool.Get();

            Assert.AreEqual(0, t0.ID);
            Assert.AreEqual(1, t1.ID);
            Assert.AreEqual(2, t2.ID);

            Assert.AreEqual(3, pool.Count);

            pool.Release(t0);

            Assert.AreEqual(2, pool.Count);
            Assert.Less(t0.GetHashCode(), 0);

            pool.Release(t1);

            Assert.AreEqual(1, pool.Count);
            Assert.Less(t1.GetHashCode(), 0);

            var t4 = pool.Get();

            Assert.AreEqual(2, pool.Count);
            Assert.AreEqual(t1.ID, t4.ID);

            var t5 = pool.Get();

            Assert.AreEqual(3, pool.Count);
            Assert.AreEqual(t0.ID, t5.ID);

            var t6 = pool.Get();

            Assert.AreEqual(4, pool.Count);
            Assert.AreEqual(3, t6.ID);
        }
예제 #12
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Mesh" /> class.
        /// </summary>
        public Mesh(Configuration config)
        {
            Initialize();

            logger = Log.Instance;

            behavior = new Behavior();

            vertices = new Dictionary<int, Vertex>();
            subsegs = new Dictionary<int, SubSegment>();

            triangles = config.TrianglePool();

            flipstack = new Stack<Otri>();

            holes = new List<Point>();
            regions = new List<RegionPointer>();

            steinerleft = -1;

            this.predicates = config.Predicates();

            this.locator = new TriangleLocator(this, predicates);
        }
예제 #13
0
 /// <inheritdoc />
 public void Dispose()
 {
     TrianglePool.FreeMesh(Mesh);
 }
예제 #14
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);
        }
예제 #15
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);
        }
예제 #16
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);
        }
예제 #17
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);
        }