/// <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); }
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); }
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); }
/// <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 }; }
/// <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); }
/// <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); }
/// <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)) { } }
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; }
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); }
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); }
/// <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); }
/// <inheritdoc /> public void Dispose() { TrianglePool.FreeMesh(Mesh); }
/// <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); }
/// <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); }
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); }
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); }