From c19681b2dd76b018af42e80f4dd98551a4d3cf4e Mon Sep 17 00:00:00 2001 From: "SND\\wo80_cp" Date: Mon, 21 Jan 2013 14:59:13 +0000 Subject: [PATCH] Removed support for triangle attributes, Added triangle region marker git-svn-id: https://triangle.svn.codeplex.com/svn@71645 0e2699bc-83d4-4a8f-98e7-55e24ab8c7a5 --- Triangle.NET/TestApp/FormGenerator.cs | 14 +- .../TestApp/Generators/CircleWithHole.cs | 139 +++++++++++++++++ Triangle.NET/TestApp/Mesh Explorer.csproj | 1 + .../TestApp/Views/AboutView.Designer.cs | 2 +- Triangle.NET/Triangle/Algorithm/Dwyer.cs | 1 - Triangle.NET/Triangle/Behavior.cs | 6 +- Triangle.NET/Triangle/Carver.cs | 146 +++--------------- Triangle.NET/Triangle/Data/Triangle.cs | 16 +- Triangle.NET/Triangle/Geometry/ITriangle.cs | 4 +- .../Triangle/Geometry/InputGeometry.cs | 7 +- .../Triangle/Geometry/RegionPointer.cs | 35 +---- Triangle.NET/Triangle/IO/DataReader.cs | 5 +- Triangle.NET/Triangle/IO/FileReader.cs | 37 +++-- Triangle.NET/Triangle/IO/FileWriter.cs | 12 +- Triangle.NET/Triangle/IO/InputTriangle.cs | 9 +- Triangle.NET/Triangle/Mesh.cs | 63 ++++---- Triangle.NET/Triangle/Primitives.cs | 4 + Triangle.NET/Triangle/Quality.cs | 4 +- Triangle.NET/Triangle/Tools/RegionIterator.cs | 139 +++++++++++++++++ Triangle.NET/Triangle/Tools/Statistic.cs | 2 + Triangle.NET/Triangle/Triangle.csproj | 1 + 21 files changed, 396 insertions(+), 251 deletions(-) create mode 100644 Triangle.NET/TestApp/Generators/CircleWithHole.cs create mode 100644 Triangle.NET/Triangle/Tools/RegionIterator.cs diff --git a/Triangle.NET/TestApp/FormGenerator.cs b/Triangle.NET/TestApp/FormGenerator.cs index 6bd8356..eaee467 100644 --- a/Triangle.NET/TestApp/FormGenerator.cs +++ b/Triangle.NET/TestApp/FormGenerator.cs @@ -63,10 +63,17 @@ namespace MeshExplorer { if (currentGenerator != null && InputGenerated != null) { - InputGeometry input = currentGenerator.Generate(sliderParam1.Value, - sliderParam2.Value, sliderParam3.Value); + try + { + InputGeometry input = currentGenerator.Generate(sliderParam1.Value, + sliderParam2.Value, sliderParam3.Value); - InputGenerated(input, EventArgs.Empty); + InputGenerated(input, EventArgs.Empty); + } + catch (Exception ex) + { + DarkMessageBox.Show("Exception", ex.Message); + } } } @@ -135,6 +142,7 @@ namespace MeshExplorer darkListBox1.Items.Add(new StarInBox()); darkListBox1.Items.Add(new RingPolygon()); darkListBox1.Items.Add(new BoxWithHole()); + darkListBox1.Items.Add(new CircleWithHole()); darkListBox1.SelectedIndex = 0; } diff --git a/Triangle.NET/TestApp/Generators/CircleWithHole.cs b/Triangle.NET/TestApp/Generators/CircleWithHole.cs new file mode 100644 index 0000000..ccac84f --- /dev/null +++ b/Triangle.NET/TestApp/Generators/CircleWithHole.cs @@ -0,0 +1,139 @@ +// ----------------------------------------------------------------------- +// +// Christian Woltering, Triangle.NET, http://triangle.codeplex.com/ +// +// ----------------------------------------------------------------------- + +namespace MeshExplorer.Generators +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using TriangleNet.Geometry; + + /// + /// Generates a ring polygon. + /// + public class CircleWithHole : IGenerator + { + // Parameters range + double[] range1 = { 100, 250 }; + double[] range2 = { 2, 15 }; + + public string Name + { + get { return "Circle with Hole"; } + } + + public string Description + { + get { return ""; } + } + + public int ParameterCount + { + get { return 2; } + } + + public string ParameterDescription(int paramIndex) + { + if (paramIndex == 1) + { + return "Number of points:"; + } + + if (paramIndex == 2) + { + return "Outer radius:"; + } + + return ""; + } + + public string ParameterDescription(int paramIndex, double paramValue) + { + if (paramIndex == 1) + { + int num = (int)((range1[1] - range1[0]) / 100.0 * paramValue + range1[0]); + + return num.ToString(); + } + + if (paramIndex == 2) + { + int radius = (int)((range2[1] - range2[0]) / 100.0 * paramValue + range2[0]); + + return radius.ToString(); + } + + return ""; + } + + public InputGeometry Generate(double param1, double param2, double param3) + { + // Number of points on the outer circle + int n = (int)((range1[1] - range1[0]) / 100.0 * param1 + range1[0]); + int count, npoints; + + double radius = (int)((range2[1] - range2[0]) / 100.0 * param2 + range2[0]); + + // Step size on the outer circle + double h = 2 * Math.PI * radius / n; + + // Current radius and step size + double r, dphi; + + InputGeometry input = new InputGeometry(n + 1); + + // Inner cirlce (radius = 1) + r = 1; + npoints = (int)(2 * Math.PI * r / h); + dphi = 2 * Math.PI / npoints; + for (int i = 0; i < npoints; i++) + { + input.AddPoint(r * Math.Cos(i * dphi), r * Math.Sin(i * dphi), 1); + input.AddSegment(i, (i + 1) % npoints, 1); + } + + count = input.Count; + + // Center cirlce + r = (radius + 1) / 2.0; + npoints = (int)(2 * Math.PI * r / h); + dphi = 2 * Math.PI / npoints; + for (int i = 0; i < npoints; i++) + { + input.AddPoint(r * Math.Cos(i * dphi), r * Math.Sin(i * dphi), 2); + input.AddSegment(count + i, count + (i + 1) % npoints, 2); + } + + count = input.Count; + + // Outer cirlce + r = radius; + npoints = (int)(2 * Math.PI * r / h); + dphi = 2 * Math.PI / npoints; + for (int i = 0; i < npoints; i++) + { + input.AddPoint(r * Math.Cos(i * dphi), r * Math.Sin(i * dphi), 3); + input.AddSegment(count + i, count + (i + 1) % npoints, 3); + } + + input.AddHole(0, 0); + + // Regions: |------|------|---| + // r 1 0 + + input.AddRegion((r + 3.0) / 4.0, 0, 1); + input.AddRegion((3 * r + 1.0) / 4.0, 0, 2); + + return input; + } + + public override string ToString() + { + return this.Name; + } + } +} diff --git a/Triangle.NET/TestApp/Mesh Explorer.csproj b/Triangle.NET/TestApp/Mesh Explorer.csproj index a291fc1..d174071 100644 --- a/Triangle.NET/TestApp/Mesh Explorer.csproj +++ b/Triangle.NET/TestApp/Mesh Explorer.csproj @@ -97,6 +97,7 @@ FormMain.cs + diff --git a/Triangle.NET/TestApp/Views/AboutView.Designer.cs b/Triangle.NET/TestApp/Views/AboutView.Designer.cs index b892f81..2aa417f 100644 --- a/Triangle.NET/TestApp/Views/AboutView.Designer.cs +++ b/Triangle.NET/TestApp/Views/AboutView.Designer.cs @@ -79,7 +79,7 @@ this.label19.Name = "label19"; this.label19.Size = new System.Drawing.Size(134, 40); this.label19.TabIndex = 6; - this.label19.Text = "Beta 3 (2012-10-30)\r\nChristian Woltering\r\nMIT"; + this.label19.Text = "Beta 3 (2013-01-20)\r\nChristian Woltering\r\nMIT"; // // label18 // diff --git a/Triangle.NET/Triangle/Algorithm/Dwyer.cs b/Triangle.NET/Triangle/Algorithm/Dwyer.cs index edf0b79..ebf83df 100644 --- a/Triangle.NET/Triangle/Algorithm/Dwyer.cs +++ b/Triangle.NET/Triangle/Algorithm/Dwyer.cs @@ -599,7 +599,6 @@ namespace TriangleNet.Algorithm /// /// Recursively form a Delaunay triangulation by the divide-and-conquer method. /// - /// /// /// /// diff --git a/Triangle.NET/Triangle/Behavior.cs b/Triangle.NET/Triangle/Behavior.cs index 38b8404..979f60f 100644 --- a/Triangle.NET/Triangle/Behavior.cs +++ b/Triangle.NET/Triangle/Behavior.cs @@ -24,7 +24,7 @@ namespace TriangleNet VarArea = false; FixedArea = false; Usertest = false; - RegionAttrib = false; + UseRegions = false; Convex = false; Jettison = false; UseBoundaryMarkers = true; @@ -84,9 +84,9 @@ namespace TriangleNet /// public bool Usertest { get; set; } /// - /// Apply attributes to identify triangles in certain regions. + /// Identify triangles in certain regions. /// - public bool RegionAttrib { get; set; } + public bool UseRegions { get; set; } /// /// Enclose the convex hull with segments. /// diff --git a/Triangle.NET/Triangle/Carver.cs b/Triangle.NET/Triangle/Carver.cs index 3fdbfbb..1e51d3c 100644 --- a/Triangle.NET/Triangle/Carver.cs +++ b/Triangle.NET/Triangle/Carver.cs @@ -11,6 +11,7 @@ namespace TriangleNet using System; using TriangleNet.Geometry; using System.Collections.Generic; + using TriangleNet.Tools; /// /// Carves holes into the triangulation. @@ -18,10 +19,12 @@ namespace TriangleNet class Carver { Mesh mesh; + List viri; public Carver(Mesh mesh) { this.mesh = mesh; + this.viri = new List(); } /// @@ -57,7 +60,7 @@ namespace TriangleNet if (!hulltri.IsInfected()) { hulltri.Infect(); - mesh.viri.Add(hulltri.triangle); + viri.Add(hulltri.triangle); } } else @@ -118,11 +121,11 @@ namespace TriangleNet // Loop through all the infected triangles, spreading the virus to // their neighbors, then to their neighbors' neighbors. - for (int i = 0; i < mesh.viri.Count; i++) + for (int i = 0; i < viri.Count; i++) { // WARNING: Don't use foreach, mesh.viri list may get modified. - testtri.triangle = mesh.viri[i]; + testtri.triangle = viri[i]; // A triangle is marked as infected by messing with one of its pointers // to subsegments, setting it to an illegal value. Hence, we have to // temporarily uninfect this triangle so that we can examine its @@ -164,7 +167,7 @@ namespace TriangleNet // the neighbor becomes infected. neighbor.Infect(); // Ensure that the neighbor's neighbors will be infected. - mesh.viri.Add(neighbor.triangle); + viri.Add(neighbor.triangle); } else { @@ -194,7 +197,7 @@ namespace TriangleNet testtri.Infect(); } - foreach (var virus in mesh.viri) + foreach (var virus in viri) { testtri.triangle = virus; @@ -286,90 +289,9 @@ namespace TriangleNet } // Empty the virus pool. - mesh.viri.Clear(); + viri.Clear(); } - /// - /// Spread regional attributes and/or area constraints (from a .poly file) - /// throughout the mesh. - /// - /// - /// - /// - /// This procedure operates in two phases. The first phase spreads an - /// attribute and/or an area constraint through a (segment-bounded) region. - /// The triangles are marked to ensure that each triangle is added to the - /// virus pool only once, so the procedure will terminate. - /// - /// The second phase uninfects all infected triangles, returning them to - /// normal. - /// - void RegionPlague(double attribute, double area) - { - Otri testtri = default(Otri); - Otri neighbor = default(Otri); - Osub neighborsubseg = default(Osub); - - Behavior behavior = mesh.behavior; - - // Loop through all the infected triangles, spreading the attribute - // and/or area constraint to their neighbors, then to their neighbors' - // neighbors. - for (int i = 0; i < mesh.viri.Count; i++) - { - // WARNING: Don't use foreach, mesh.viri list may get modified. - - testtri.triangle = mesh.viri[i]; - // A triangle is marked as infected by messing with one of its pointers - // to subsegments, setting it to an illegal value. Hence, we have to - // temporarily uninfect this triangle so that we can examine its - // adjacent subsegments. - // TODO: Not true in the C# version (so we could skip this). - testtri.Uninfect(); - if (behavior.RegionAttrib) - { - // Set an attribute (Note: the attributes array was resized before). - testtri.triangle.attributes[mesh.eextras] = attribute; - } - if (behavior.VarArea) - { - // Set an area constraint. - testtri.triangle.area = area; - } - - // Check each of the triangle's three neighbors. - for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) - { - // Find the neighbor. - testtri.Sym(ref neighbor); - // Check for a subsegment between the triangle and its neighbor. - testtri.SegPivot(ref neighborsubseg); - // Make sure the neighbor exists, is not already infected, and - // isn't protected by a subsegment. - if ((neighbor.triangle != Mesh.dummytri) && !neighbor.IsInfected() - && (neighborsubseg.seg == Mesh.dummysub)) - { - // Infect the neighbor. - neighbor.Infect(); - // Ensure that the neighbor's neighbors will be infected. - mesh.viri.Add(neighbor.triangle); - } - } - // Remark the triangle as infected, so it doesn't get added to the - // virus pool again. - testtri.Infect(); - } - - // Uninfect all triangles. - foreach (var virus in mesh.viri) - { - testtri.triangle = virus; - testtri.Uninfect(); - } - - // Empty the virus pool. - mesh.viri.Clear(); - } /// /// Find the holes and infect them. Find the area constraints and infect /// them. Infect the convex hull. Spread the infection and kill triangles. @@ -378,11 +300,10 @@ namespace TriangleNet public void CarveHoles() { Otri searchtri = default(Otri); - Otri tri = default(Otri); Vertex searchorg, searchdest; LocateResult intersect; - Otri[] regionTris = null; + Triangle[] regionTris = null; if (!mesh.behavior.Convex) { @@ -417,7 +338,7 @@ namespace TriangleNet // Infect the triangle. This is done by marking the triangle // as infected and including the triangle in the virus pool. searchtri.Infect(); - mesh.viri.Add(searchtri.triangle); + viri.Add(searchtri.triangle); } } } @@ -430,14 +351,14 @@ namespace TriangleNet // which might not be convex; they can only be used with a freshly triangulated PSLG.) if (mesh.regions.Count > 0) { - regionTris = new Otri[mesh.regions.Count]; - int i = 0; + regionTris = new Triangle[mesh.regions.Count]; + // Find the starting triangle for each region. foreach (var region in mesh.regions) { - regionTris[i].triangle = Mesh.dummytri; + regionTris[i] = Mesh.dummytri; // Ignore region points that aren't within the bounds of the mesh. if (mesh.bounds.Contains(region.point)) { @@ -458,7 +379,8 @@ namespace TriangleNet { // Record the triangle for processing after the // holes have been carved. - searchtri.Copy(ref regionTris[i]); + regionTris[i] = searchtri.triangle; + regionTris[i].region = region.id; } } } @@ -467,57 +389,33 @@ namespace TriangleNet } } - if (mesh.viri.Count > 0) + if (viri.Count > 0) { // Carve the holes and concavities. Plague(); } - // The virus pool should be empty now. if (regionTris != null) { - if (mesh.behavior.RegionAttrib) - { - // Make the triangle's attributes larger. - double[] attributes = new double[mesh.eextras + 1]; - - // Assign every triangle a regional attribute of zero. - tri.orient = 0; - foreach (var t in mesh.triangles.Values) - { - Array.Copy(tri.triangle.attributes, attributes, mesh.eextras); - tri.triangle = t; - tri.triangle.attributes = attributes; - } - } + var iterator = new RegionIterator(mesh); for (int i = 0; i < regionTris.Length; i++) { - if (regionTris[i].triangle != Mesh.dummytri) + if (regionTris[i] != Mesh.dummytri) { // Make sure the triangle under consideration still exists. // It may have been eaten by the virus. - if (!Otri.IsDead(regionTris[i].triangle)) + if (!Otri.IsDead(regionTris[i])) { - // Put one triangle in the virus pool. - regionTris[i].Infect(); - mesh.viri.Add(regionTris[i].triangle); // Apply one region's attribute and/or area constraint. - RegionPlague(mesh.regions[i].Attribute, mesh.regions[i].Area); - // The virus pool should be empty now. + iterator.Process(regionTris[i]); } } } - - if (mesh.behavior.RegionAttrib) - { - // Note the fact that each triangle has an additional attribute. - mesh.eextras++; - } } // Free up memory (virus pool should be empty anyway). - mesh.viri.Clear(); + viri.Clear(); } } } diff --git a/Triangle.NET/Triangle/Data/Triangle.cs b/Triangle.NET/Triangle/Data/Triangle.cs index adabc22..2777590 100644 --- a/Triangle.NET/Triangle/Data/Triangle.cs +++ b/Triangle.NET/Triangle/Data/Triangle.cs @@ -33,11 +33,11 @@ namespace TriangleNet.Data internal Otri[] neighbors; internal Vertex[] vertices; internal Osub[] subsegs; - internal double[] attributes; + internal int region; internal double area; internal bool infected; - public Triangle(int numAttributes) + public Triangle() { // Initialize the three adjoining triangles to be "outer space". neighbors = new Otri[3]; @@ -58,11 +58,6 @@ namespace TriangleNet.Data subsegs[2].seg = Mesh.dummysub; } - if (numAttributes > 0) - { - attributes = new double[numAttributes]; - } - // TODO: //if (Behavior.VarArea) //{ @@ -70,7 +65,6 @@ namespace TriangleNet.Data //} } - #region Public properties /// @@ -171,11 +165,11 @@ namespace TriangleNet.Data } /// - /// Gets the triangle attributes. + /// Region ID the triangle belongs to. /// - public double[] Attributes + public int Region { - get { return this.attributes; } + get { return this.region; } } #endregion diff --git a/Triangle.NET/Triangle/Geometry/ITriangle.cs b/Triangle.NET/Triangle/Geometry/ITriangle.cs index 8aa5dd1..75d9201 100644 --- a/Triangle.NET/Triangle/Geometry/ITriangle.cs +++ b/Triangle.NET/Triangle/Geometry/ITriangle.cs @@ -80,8 +80,8 @@ namespace TriangleNet.Geometry double Area { get; } /// - /// Triangle atributes. + /// Region ID the triangle belongs to. /// - double[] Attributes { get; } + int Region { get; } } } diff --git a/Triangle.NET/Triangle/Geometry/InputGeometry.cs b/Triangle.NET/Triangle/Geometry/InputGeometry.cs index 1461137..59674fc 100644 --- a/Triangle.NET/Triangle/Geometry/InputGeometry.cs +++ b/Triangle.NET/Triangle/Geometry/InputGeometry.cs @@ -197,11 +197,10 @@ namespace TriangleNet.Geometry /// /// X coordinate of the hole. /// Y coordinate of the hole. - /// The regions area constraint. - /// Region attribute. - public void AddRegion(double x, double y, double area, double attribute) + /// The region id. + public void AddRegion(double x, double y, int id) { - regions.Add(new RegionPointer(x, y, area, attribute)); + regions.Add(new RegionPointer(x, y, id)); } /// diff --git a/Triangle.NET/Triangle/Geometry/RegionPointer.cs b/Triangle.NET/Triangle/Geometry/RegionPointer.cs index 3267ac8..f44b872 100644 --- a/Triangle.NET/Triangle/Geometry/RegionPointer.cs +++ b/Triangle.NET/Triangle/Geometry/RegionPointer.cs @@ -16,45 +16,18 @@ namespace TriangleNet.Geometry public class RegionPointer { internal Point point; - internal double area; - internal double attribute; + internal int id; /// /// Initializes a new instance of the class. /// /// X coordinate of the region. /// Y coordinate of the region. - /// Area constraint. - /// Region attribute. - public RegionPointer(double x, double y, double area, double attribute) + /// Region id. + public RegionPointer(double x, double y, int id) { this.point = new Point(x, y); - this.area = area; - this.attribute = attribute; - } - - /// - /// Gets the location of the region. - /// - internal Point Point - { - get { return point; } - } - - /// - /// Gets the area constraint. - /// - internal double Area - { - get { return area; } - } - - /// - /// Gets the region attribute. - /// - internal double Attribute - { - get { return attribute; } + this.id = id; } } } diff --git a/Triangle.NET/Triangle/IO/DataReader.cs b/Triangle.NET/Triangle/IO/DataReader.cs index b0b0d32..78b6901 100644 --- a/Triangle.NET/Triangle/IO/DataReader.cs +++ b/Triangle.NET/Triangle/IO/DataReader.cs @@ -75,7 +75,6 @@ namespace TriangleNet.IO int numberofsegments = input.segments.Count; mesh.inelements = elements; - mesh.eextras = triangles[0].Attributes != null ? triangles[0].Attributes.Length : 0; // Create the triangles. for (i = 0; i < mesh.inelements; i++) @@ -134,9 +133,9 @@ namespace TriangleNet.IO } // Read the triangle's attributes. - tri.triangle.attributes = triangles[i].Attributes; + tri.triangle.region = triangles[i].Region; - // TODO + // TODO: VarArea if (mesh.behavior.VarArea) { tri.triangle.area = triangles[i].Area; diff --git a/Triangle.NET/Triangle/IO/FileReader.cs b/Triangle.NET/Triangle/IO/FileReader.cs index 254a283..753a987 100644 --- a/Triangle.NET/Triangle/IO/FileReader.cs +++ b/Triangle.NET/Triangle/IO/FileReader.cs @@ -447,19 +447,17 @@ namespace TriangleNet.IO throw new Exception("Can't read input file (region)."); } - if (line.Length < 5) + if (line.Length < 4) { - throw new Exception("Invalid region."); + throw new Exception("Invalid region attributes."); } data.AddRegion( // Region x and y - double.Parse(line[1]), - double.Parse(line[2]), - // Region attribute - double.Parse(line[3]), - // Region area constraint - double.Parse(line[4])); + double.Parse(line[1], nfi), + double.Parse(line[2], nfi), + // Region id + int.Parse(line[3])); } } } @@ -504,6 +502,7 @@ namespace TriangleNet.IO { // Read number of elements and number of attributes. string[] line; + bool validRegion = false; if (!TryReadLine(reader, out line)) { @@ -517,6 +516,12 @@ namespace TriangleNet.IO if (line.Length > 2) { attributes = int.Parse(line[2]); + validRegion = true; + } + + if (attributes > 1) + { + SimpleLog.Instance.Warning("Triangle attributes not supported.", "FileReader.Read"); } triangles = new List(intriangles); @@ -542,18 +547,12 @@ namespace TriangleNet.IO int.Parse(line[2]) - startIndex, int.Parse(line[3]) - startIndex); - // Read triangle attributes - if (attributes > 0) + // Read triangle region + if (attributes > 0 && validRegion) { - for (int j = 0; j < attributes; j++) - { - tri.attributes = new double[attributes]; - - if (line.Length > 4 + j) - { - tri.attributes[j] = double.Parse(line[4 + j]); - } - } + int region = 0; + validRegion = int.TryParse(line[4], out region); + tri.region = region; } triangles.Add(tri); diff --git a/Triangle.NET/Triangle/IO/FileWriter.cs b/Triangle.NET/Triangle/IO/FileWriter.cs index 7abcd58..1d7b2ed 100644 --- a/Triangle.NET/Triangle/IO/FileWriter.cs +++ b/Triangle.NET/Triangle/IO/FileWriter.cs @@ -142,6 +142,7 @@ namespace TriangleNet.IO { Otri tri = default(Otri); Vertex p1, p2, p3; + bool regions = mesh.behavior.UseRegions; int j = 0; @@ -150,7 +151,7 @@ namespace TriangleNet.IO using (StreamWriter writer = new StreamWriter(filename)) { // Number of triangles, vertices per triangle, attributes per triangle. - writer.WriteLine("{0} 3 {1}", mesh.triangles.Count, mesh.eextras); + writer.WriteLine("{0} 3 {1}", mesh.triangles.Count, regions ? 1 : 0); foreach (var item in mesh.triangles.Values) { @@ -163,9 +164,9 @@ namespace TriangleNet.IO // Triangle number, indices for three vertices. writer.Write("{0} {1} {2} {3}", j, p1.id, p2.id, p3.id); - for (int i = 0; i < mesh.eextras; i++) + if (regions) { - writer.Write(" {0}", tri.triangle.attributes[i].ToString(nfi)); + writer.Write(" {0}", tri.triangle.region); } writer.WriteLine(); @@ -260,9 +261,8 @@ namespace TriangleNet.IO writer.WriteLine("{0}", mesh.regions.Count); foreach (var region in mesh.regions) { - writer.WriteLine("{0} {1} {2} {3} {4}", j, region.point.X.ToString(nfi), - region.point.Y.ToString(nfi), region.attribute.ToString(nfi), - region.area.ToString(nfi)); + writer.WriteLine("{0} {1} {2} {3}", j, region.point.X.ToString(nfi), + region.point.Y.ToString(nfi), region.id); j++; } diff --git a/Triangle.NET/Triangle/IO/InputTriangle.cs b/Triangle.NET/Triangle/IO/InputTriangle.cs index 61c0939..68b6e0e 100644 --- a/Triangle.NET/Triangle/IO/InputTriangle.cs +++ b/Triangle.NET/Triangle/IO/InputTriangle.cs @@ -17,7 +17,7 @@ namespace TriangleNet.IO public class InputTriangle : ITriangle { internal int[] vertices; - internal double[] attributes; + internal int region; internal double area; public InputTriangle(int p0, int p1, int p2) @@ -106,11 +106,12 @@ namespace TriangleNet.IO } /// - /// Gets the triangle attributes. + /// Region ID the triangle belongs to. /// - public double[] Attributes + public int Region { - get { return null; } + get { return region; } + set { region = value; } } #endregion diff --git a/Triangle.NET/Triangle/Mesh.cs b/Triangle.NET/Triangle/Mesh.cs index 5c5a6f5..c8312b7 100644 --- a/Triangle.NET/Triangle/Mesh.cs +++ b/Triangle.NET/Triangle/Mesh.cs @@ -48,8 +48,6 @@ namespace TriangleNet internal List holes; internal List regions; - internal List viri; - // Other variables. internal BoundingBox bounds; // x and y bounds. internal int invertices; // Number of input vertices. @@ -59,7 +57,7 @@ namespace TriangleNet internal int edges; // Number of output edges. internal int mesh_dim; // Dimension (ought to be 2). internal int nextras; // Number of attributes per vertex. - internal int eextras; // Number of attributes per triangle. + //internal int eextras; // Number of attributes per triangle. internal int hullsize; // Number of edges in convex hull. internal int steinerleft; // Number of Steiner points not yet used. internal bool checksegments; // Are there segments in the triangulation yet? @@ -182,7 +180,6 @@ namespace TriangleNet triangles = new Dictionary(); subsegs = new Dictionary(); - viri = new List(); flipstack = new Stack(); holes = new List(); @@ -247,6 +244,7 @@ namespace TriangleNet // behavior.VarArea = true; //} + // TODO: remove if (!behavior.Poly) { // Be careful not to allocate space for element area constraints that @@ -255,9 +253,11 @@ namespace TriangleNet // Be careful not to add an extra attribute to each element unless the // input supports it (PSLG in, but not refining a preexisting mesh). - behavior.RegionAttrib = false; + behavior.UseRegions = false; } + behavior.UseRegions = input.Regions.Count > 0; + TransferNodes(input); // Read and reconstruct a mesh. @@ -296,6 +296,7 @@ namespace TriangleNet // behavior.UseBoundaryMarkers = true; //} + // TODO: remove if (!behavior.Poly) { // Be careful not to allocate space for element area constraints that @@ -304,9 +305,11 @@ namespace TriangleNet // Be careful not to add an extra attribute to each element unless the // input supports it (PSLG in, but not refining a preexisting mesh). - behavior.RegionAttrib = false; + behavior.UseRegions = false; } + behavior.UseRegions = input.Regions.Count > 0; + steinerleft = behavior.Steiner; TransferNodes(input); @@ -706,8 +709,6 @@ namespace TriangleNet { int hulledges = 0; - eextras = 0; - if (behavior.Algorithm == TriangulationAlgorithm.Dwyer) { Dwyer alg = new Dwyer(); @@ -746,7 +747,6 @@ namespace TriangleNet this.hash_seg = 0; this.hash_tri = 0; - viri.Clear(); flipstack.Clear(); hullsize = 0; @@ -771,6 +771,8 @@ namespace TriangleNet Statistic.InCircleCount = 0; Statistic.CounterClockwiseCount = 0; + Statistic.InCircleCountDecimal = 0; + Statistic.CounterClockwiseCountDecimal = 0; Statistic.Orient3dCount = 0; Statistic.HyperbolaCount = 0; Statistic.CircleTopCount = 0; @@ -805,7 +807,7 @@ namespace TriangleNet private void DummyInit() { // Set up 'dummytri', the 'triangle' that occupies "outer space." - dummytri = new Triangle(0); + dummytri = new Triangle(); dummytri.hash = -1; dummytri.id = -1; @@ -880,7 +882,7 @@ namespace TriangleNet /// Reference to the new triangle. internal void MakeTriangle(ref Otri newotri) { - Triangle tri = new Triangle(eextras); + Triangle tri = new Triangle(); tri.hash = this.hash_tri++; tri.id = tri.hash; @@ -981,14 +983,13 @@ namespace TriangleNet Vertex first; Vertex leftvertex, rightvertex, botvertex, topvertex, farvertex; Vertex segmentorg, segmentdest; - double attrib; + int region; double area; InsertVertexResult success; LocateResult intersect; bool doflip; bool mirrorflag; bool enq; - int i; if (splitseg.seg == null) { @@ -1094,11 +1095,8 @@ namespace TriangleNet newbotright.SetApex(newvertex); horiz.SetOrg(newvertex); - for (i = 0; i < eextras; i++) - { - // Set the element attributes of a new triangle. - newbotright.triangle.attributes[i] = botright.triangle.attributes[i]; - } + // Set the region of a new triangle. + newbotright.triangle.region = botright.triangle.region; if (behavior.VarArea) { @@ -1114,11 +1112,8 @@ namespace TriangleNet newtopright.SetApex(newvertex); topright.SetOrg(newvertex); - for (i = 0; i < eextras; i++) - { - // Set the element attributes of another new triangle. - newtopright.triangle.attributes[i] = topright.triangle.attributes[i]; - } + // Set the region of another new triangle. + newtopright.triangle.region = topright.triangle.region; if (behavior.VarArea) { @@ -1223,13 +1218,9 @@ namespace TriangleNet newbotright.SetApex(newvertex); horiz.SetApex(newvertex); - for (i = 0; i < eextras; i++) - { - // Set the element attributes of the new triangles. - attrib = horiz.triangle.attributes[i]; - newbotleft.triangle.attributes[i] = attrib; - newbotright.triangle.attributes[i] = attrib; - } + // Set the region of the new triangles. + newbotleft.triangle.region = horiz.triangle.region; + newbotright.triangle.region = horiz.triangle.region; if (behavior.VarArea) { @@ -1426,13 +1417,11 @@ namespace TriangleNet top.SetDest(farvertex); top.SetApex(leftvertex); - for (i = 0; i < eextras; i++) - { - // Take the average of the two triangles' attributes. - attrib = 0.5 * (top.triangle.attributes[i] + horiz.triangle.attributes[i]); - top.triangle.attributes[i] = attrib; - horiz.triangle.attributes[i] = attrib; - } + // Assign region. + // TODO: check region ok (no Math.Min necessary) + region = Math.Min(top.triangle.region, horiz.triangle.region); + top.triangle.region = region; + horiz.triangle.region = region; if (behavior.VarArea) { diff --git a/Triangle.NET/Triangle/Primitives.cs b/Triangle.NET/Triangle/Primitives.cs index a08f2a9..6d7855a 100644 --- a/Triangle.NET/Triangle/Primitives.cs +++ b/Triangle.NET/Triangle/Primitives.cs @@ -152,6 +152,8 @@ namespace TriangleNet private static decimal CounterClockwiseDecimal(Point pa, Point pb, Point pc) { + Statistic.CounterClockwiseCountDecimal++; + decimal detleft, detright, det, detsum; detleft = ((decimal)pa.x - (decimal)pc.x) * ((decimal)pb.y - (decimal)pc.y); @@ -258,6 +260,8 @@ namespace TriangleNet private static decimal InCircleDecimal(Point pa, Point pb, Point pc, Point pd) { + Statistic.InCircleCountDecimal++; + decimal adx, bdx, cdx, ady, bdy, cdy; decimal bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; decimal alift, blift, clift; diff --git a/Triangle.NET/Triangle/Quality.cs b/Triangle.NET/Triangle/Quality.cs index 5857090..6212a40 100644 --- a/Triangle.NET/Triangle/Quality.cs +++ b/Triangle.NET/Triangle/Quality.cs @@ -724,7 +724,7 @@ namespace TriangleNet split = 0.5; } - // Create the new vertex. + // Create the new vertex (interpolate coordinates). newvertex = new Vertex( eorg.x + split * (edest.x - eorg.x), eorg.y + split * (edest.y - eorg.y), @@ -738,7 +738,7 @@ namespace TriangleNet mesh.vertices.Add(newvertex.hash, newvertex); - // Interpolate its coordinate and attributes. + // Interpolate attributes. for (int i = 0; i < mesh.nextras; i++) { newvertex.attributes[i] = eorg.attributes[i] diff --git a/Triangle.NET/Triangle/Tools/RegionIterator.cs b/Triangle.NET/Triangle/Tools/RegionIterator.cs new file mode 100644 index 0000000..d09599f --- /dev/null +++ b/Triangle.NET/Triangle/Tools/RegionIterator.cs @@ -0,0 +1,139 @@ +// ----------------------------------------------------------------------- +// +// Original Matlab code by John Burkardt, Florida State University +// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ +// +// ----------------------------------------------------------------------- + +namespace TriangleNet.Tools +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using TriangleNet.Data; + + /// + /// Iterates the region a given triangle belongs to and applies an action + /// to each connected trianlge in that region. Default action is to set the + /// region id. + /// + public class RegionIterator + { + Mesh mesh; + List viri; + + public RegionIterator(Mesh mesh) + { + this.mesh = mesh; + this.viri = new List(); + } + + /// + /// Spread regional attributes and/or area constraints (from a .poly file) + /// throughout the mesh. + /// + /// + /// + /// + /// This procedure operates in two phases. The first phase spreads an + /// attribute and/or an area constraint through a (segment-bounded) region. + /// The triangles are marked to ensure that each triangle is added to the + /// virus pool only once, so the procedure will terminate. + /// + /// The second phase uninfects all infected triangles, returning them to + /// normal. + /// + void ProcessRegion(Action func) + { + Otri testtri = default(Otri); + Otri neighbor = default(Otri); + Osub neighborsubseg = default(Osub); + + Behavior behavior = mesh.behavior; + + // Loop through all the infected triangles, spreading the attribute + // and/or area constraint to their neighbors, then to their neighbors' + // neighbors. + for (int i = 0; i < viri.Count; i++) + { + // WARNING: Don't use foreach, viri list gets modified. + + testtri.triangle = viri[i]; + // A triangle is marked as infected by messing with one of its pointers + // to subsegments, setting it to an illegal value. Hence, we have to + // temporarily uninfect this triangle so that we can examine its + // adjacent subsegments. + // TODO: Not true in the C# version (so we could skip this). + testtri.Uninfect(); + + // Apply function. + func(testtri.triangle); + + // Check each of the triangle's three neighbors. + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) + { + // Find the neighbor. + testtri.Sym(ref neighbor); + // Check for a subsegment between the triangle and its neighbor. + testtri.SegPivot(ref neighborsubseg); + // Make sure the neighbor exists, is not already infected, and + // isn't protected by a subsegment. + if ((neighbor.triangle != Mesh.dummytri) && !neighbor.IsInfected() + && (neighborsubseg.seg == Mesh.dummysub)) + { + // Infect the neighbor. + neighbor.Infect(); + // Ensure that the neighbor's neighbors will be infected. + viri.Add(neighbor.triangle); + } + } + // Remark the triangle as infected, so it doesn't get added to the + // virus pool again. + testtri.Infect(); + } + + // Uninfect all triangles. + foreach (var virus in viri) + { + virus.infected = false; + } + + // Empty the virus pool. + viri.Clear(); + } + + /// + /// Set the region attribute of all trianlges connected to given triangle. + /// + public void Process(Triangle triangle) + { + // Default action is to just set the region id for all trianlges. + this.Process(triangle, (tri) => { tri.region = triangle.region; }); + } + + /// + /// Process all trianlges connected to given triangle and apply given action. + /// + public void Process(Triangle triangle, Action func) + { + if (triangle != Mesh.dummytri) + { + // Make sure the triangle under consideration still exists. + // It may have been eaten by the virus. + if (!Otri.IsDead(triangle)) + { + // Put one triangle in the virus pool. + triangle.infected = true; + viri.Add(triangle); + // Apply one region's attribute and/or area constraint. + ProcessRegion(func); + // The virus pool should be empty now. + } + } + + // Free up memory (virus pool should be empty anyway). + viri.Clear(); + } + } +} diff --git a/Triangle.NET/Triangle/Tools/Statistic.cs b/Triangle.NET/Triangle/Tools/Statistic.cs index 9b9f007..002d7bb 100644 --- a/Triangle.NET/Triangle/Tools/Statistic.cs +++ b/Triangle.NET/Triangle/Tools/Statistic.cs @@ -23,11 +23,13 @@ namespace TriangleNet.Tools /// Number of incircle tests performed. /// public static long InCircleCount = 0; + public static long InCircleCountDecimal = 0; /// /// Number of counterclockwise tests performed. /// public static long CounterClockwiseCount = 0; + public static long CounterClockwiseCountDecimal = 0; /// /// Number of 3D orientation tests performed. diff --git a/Triangle.NET/Triangle/Triangle.csproj b/Triangle.NET/Triangle/Triangle.csproj index 0cdc54f..1bf10c9 100644 --- a/Triangle.NET/Triangle/Triangle.csproj +++ b/Triangle.NET/Triangle/Triangle.csproj @@ -87,6 +87,7 @@ +