diff --git a/Triangle.NET/MeshRenderer.Core/BoundingBox.cs b/Triangle.NET/MeshRenderer.Core/BoundingBox.cs index 9ec9e8d..9217232 100644 --- a/Triangle.NET/MeshRenderer.Core/BoundingBox.cs +++ b/Triangle.NET/MeshRenderer.Core/BoundingBox.cs @@ -1,11 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Drawing; - + namespace MeshRenderer.Core { + using System.Drawing; + using Rectangle = TriangleNet.Geometry.Rectangle; + public struct BoundingBox { public float Left; @@ -31,6 +29,14 @@ namespace MeshRenderer.Core this.Top = top; } + public BoundingBox(Rectangle rectangle) + { + this.Left = (float)rectangle.Left; + this.Right = (float)rectangle.Right; + this.Bottom = (float)rectangle.Bottom; + this.Top = (float)rectangle.Top; + } + public void Update(Point pt) { this.Update(pt.X, pt.Y); diff --git a/Triangle.NET/MeshRenderer.Core/RenderData.cs b/Triangle.NET/MeshRenderer.Core/RenderData.cs index c194e07..954078d 100644 --- a/Triangle.NET/MeshRenderer.Core/RenderData.cs +++ b/Triangle.NET/MeshRenderer.Core/RenderData.cs @@ -10,6 +10,7 @@ namespace MeshRenderer.Core using TriangleNet; using TriangleNet.Geometry; using TriangleNet.Tools; + using TriangleNet.Meshing.Iterators; /// /// Stores the current mesh in a rendering friendly data structure. @@ -31,7 +32,7 @@ namespace MeshRenderer.Core /// /// Copy input geometry data. /// - public void SetInputGeometry(InputGeometry data) + public void SetInputGeometry(IPolygon data) { // Clear unused buffers this.Segments = null; @@ -40,7 +41,7 @@ namespace MeshRenderer.Core this.VoronoiPoints = null; this.VoronoiEdges = null; - int n = data.Count; + int n = data.Points.Count; int i = 0; this.NumberOfRegions = data.Regions.Count; @@ -68,11 +69,7 @@ namespace MeshRenderer.Core this.Segments = segments.ToArray(); } - this.Bounds = new BoundingBox( - (float)data.Bounds.Left, - (float)data.Bounds.Right, - (float)data.Bounds.Bottom, - (float)data.Bounds.Top); + this.Bounds = new BoundingBox(data.Bounds()); } /// @@ -118,14 +115,14 @@ namespace MeshRenderer.Core // Copy edges var edges = new List(2 * mesh.NumberOfEdges); - EdgeEnumerator e = new EdgeEnumerator(mesh); + var e = new EdgeIterator(mesh); while (e.MoveNext()) { edges.Add((uint)e.Current.P0); edges.Add((uint)e.Current.P1); } - this.MeshEdges = edges.ToArray(); + this.MeshEdges = edges.ToArray(); if (this.NumberOfRegions > 0) { diff --git a/Triangle.NET/TestApp/FormGenerator.cs b/Triangle.NET/TestApp/FormGenerator.cs index bd2497a..e88145e 100644 --- a/Triangle.NET/TestApp/FormGenerator.cs +++ b/Triangle.NET/TestApp/FormGenerator.cs @@ -65,7 +65,7 @@ namespace MeshExplorer { try { - InputGeometry input = currentGenerator.Generate(sliderParam1.Value, + var input = currentGenerator.Generate(sliderParam1.Value, sliderParam2.Value, sliderParam3.Value); InputGenerated(input, EventArgs.Empty); diff --git a/Triangle.NET/TestApp/FormMain.cs b/Triangle.NET/TestApp/FormMain.cs index 83d299e..d8efa81 100644 --- a/Triangle.NET/TestApp/FormMain.cs +++ b/Triangle.NET/TestApp/FormMain.cs @@ -8,7 +8,9 @@ using MeshExplorer.IO; using MeshRenderer.Core; using TriangleNet; using TriangleNet.Geometry; -using TriangleNet.Logging; +using TriangleNet.Meshing; +using TriangleNet.Meshing.Algorithm; +using TriangleNet.Smoothing; using TriangleNet.Tools; namespace MeshExplorer @@ -16,7 +18,7 @@ namespace MeshExplorer public partial class FormMain : Form { Settings settings; - InputGeometry input; + IPolygon input; Mesh mesh; FormLog frmLog; @@ -108,7 +110,7 @@ namespace MeshExplorer void frmGenerator_InputGenerated(object sender, EventArgs e) { - this.input = sender as InputGeometry; + this.input = sender as IPolygon; if (input != null) { @@ -442,29 +444,25 @@ namespace MeshExplorer //Stopwatch sw = new Stopwatch(); - mesh = new Mesh(); + var options = new ConstraintOptions(); + var quality = new QualityOptions(); if (meshControlView.ParamConformDelChecked) { - mesh.Behavior.ConformingDelaunay = true; - } - - if (meshControlView.ParamSweeplineChecked) - { - mesh.Behavior.Algorithm = TriangulationAlgorithm.SweepLine; + options.ConformingDelaunay = true; } if (meshControlView.ParamQualityChecked) { - mesh.Behavior.Quality = true; + //mesh.Behavior.Quality = true; - mesh.Behavior.MinAngle = meshControlView.ParamMinAngleValue; + quality.MinimumAngle = meshControlView.ParamMinAngleValue; double maxAngle = meshControlView.ParamMaxAngleValue; if (maxAngle < 180) { - mesh.Behavior.MaxAngle = maxAngle; + quality.MaximumAngle = maxAngle; } // Ignore area constraints on initial triangulation. @@ -474,19 +472,26 @@ namespace MeshExplorer //{ // var size = input.Bounds; // double min = Math.Min(size.Width, size.Height); - // mesh.Behavior.MaxArea, area * min); + // mesh.SetOption(Options.MaxArea, area * min); //} } if (meshControlView.ParamConvexChecked) { - mesh.Behavior.Convex = true; + options.Convex = true; } try { //sw.Start(); - mesh.Triangulate(input); + if (meshControlView.ParamSweeplineChecked) + { + mesh = (Mesh)input.Triangulate(options, quality, new SweepLine()); + } + else + { + mesh = (Mesh)input.Triangulate(options, quality); + } //sw.Stop(); statisticView.UpdateStatistic(mesh); @@ -515,24 +520,26 @@ namespace MeshExplorer double area = meshControlView.ParamMaxAreaValue; + var quality = new QualityOptions(); + if (area > 0 && area < 1) { - mesh.Behavior.MaxArea = area * statisticView.Statistic.LargestArea; + quality.MaximumArea = area * statisticView.Statistic.LargestArea; } - mesh.Behavior.MinAngle = meshControlView.ParamMinAngleValue; + quality.MinimumAngle = meshControlView.ParamMinAngleValue; double maxAngle = meshControlView.ParamMaxAngleValue; if (maxAngle < 180) { - mesh.Behavior.MaxAngle = maxAngle; + quality.MaximumAngle = maxAngle; } try { sw.Start(); - mesh.Refine(); + mesh.Refine(quality); sw.Stop(); statisticView.UpdateStatistic(mesh); @@ -572,10 +579,12 @@ namespace MeshExplorer Stopwatch sw = new Stopwatch(); + var smoother = new SimpleSmoother(); + try { sw.Start(); - mesh.Smooth(); + smoother.Smooth(this.mesh); sw.Stop(); statisticView.UpdateStatistic(mesh); diff --git a/Triangle.NET/TestApp/FormTopology.cs b/Triangle.NET/TestApp/FormTopology.cs index 045db86..60ef393 100644 --- a/Triangle.NET/TestApp/FormTopology.cs +++ b/Triangle.NET/TestApp/FormTopology.cs @@ -1,13 +1,9 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Linq; -using System.Text; using System.Windows.Forms; using MeshExplorer.Topology; using TriangleNet; using TriangleNet.Geometry; +using TriangleNet.Meshing; using TriangleNet.Tools; namespace MeshExplorer @@ -25,10 +21,8 @@ namespace MeshExplorer private void FormTopology_Load(object sender, EventArgs e) { - var geometry = RectanglePolygon.Generate(4); - - mesh = new Mesh(); - mesh.Triangulate(geometry); + var mesher = new GenericMesher(); + mesh = (Mesh)mesher.StructurdMesh(new Rectangle(0.0, 0.0, 4.0, 4.0), 4, 4); renderControl.Initialize(mesh); @@ -75,23 +69,6 @@ namespace MeshExplorer return tree.Query(p.X, p.Y); } - private bool TriangleContainsPoint(ITriangle triangle, float x, float y) - { - bool t1, t2, t3; - - t1 = Sign(x, y, triangle.GetVertex(0), triangle.GetVertex(1)) < 0.0; - t2 = Sign(x, y, triangle.GetVertex(1), triangle.GetVertex(2)) < 0.0; - t3 = Sign(x, y, triangle.GetVertex(2), triangle.GetVertex(0)) < 0.0; - - return (t1 == t2) && (t2 == t3); - } - - private double Sign(double x, double y, Point p, Point q) - { - return (x - q.X) * (p.Y - q.Y) - (p.X - q.X) * (y - q.Y); - } - - private void InvokePrimitive(string name) { if (name == "sym") diff --git a/Triangle.NET/TestApp/Generators/BaseGenerator.cs b/Triangle.NET/TestApp/Generators/BaseGenerator.cs index 83e4d9b..d9ee619 100644 --- a/Triangle.NET/TestApp/Generators/BaseGenerator.cs +++ b/Triangle.NET/TestApp/Generators/BaseGenerator.cs @@ -7,9 +7,6 @@ namespace MeshExplorer.Generators { using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using TriangleNet.Geometry; /// @@ -53,7 +50,7 @@ namespace MeshExplorer.Generators return num.ToString(); } - public abstract InputGeometry Generate(double param0, double param1, double param2); + public abstract IPolygon Generate(double param0, double param1, double param2); protected int GetParamValueInt(int paramIndex, double paramOffset) { diff --git a/Triangle.NET/TestApp/Generators/BoxWithHole.cs b/Triangle.NET/TestApp/Generators/BoxWithHole.cs index efa5a4e..adb4f62 100644 --- a/Triangle.NET/TestApp/Generators/BoxWithHole.cs +++ b/Triangle.NET/TestApp/Generators/BoxWithHole.cs @@ -7,9 +7,6 @@ namespace MeshExplorer.Generators { using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using TriangleNet.Geometry; /// @@ -32,11 +29,11 @@ namespace MeshExplorer.Generators ranges[2] = new int[] { 5, 20 }; } - public override InputGeometry Generate(double param0, double param1, double param2) + public override IPolygon Generate(double param0, double param1, double param2) { int numPoints = GetParamValueInt(1, param1); - InputGeometry input = new InputGeometry(numPoints + 4); + var input = new Polygon(numPoints + 4); double x, y, step = 2 * Math.PI / numPoints; @@ -48,11 +45,11 @@ namespace MeshExplorer.Generators x = r * Math.Cos(i * step); y = r * Math.Sin(i * step); - input.AddPoint(x, y, 2); - input.AddSegment(i, (i + 1) % numPoints, 2); + input.Add(new Vertex(x, y, 2)); + input.Add(new Edge(i, (i + 1) % numPoints, 2)); } - numPoints = input.Count; + numPoints = input.Points.Count; int numPointsB = GetParamValueInt(0, param0); @@ -62,38 +59,38 @@ namespace MeshExplorer.Generators // Left box boundary points for (int i = 0; i < numPointsB; i++) { - input.AddPoint(-50, -50 + i * step, 1); + input.Add(new Vertex(-50, -50 + i * step, 1)); } // Top box boundary points for (int i = 0; i < numPointsB; i++) { - input.AddPoint(-50 + i * step, 50, 1); + input.Add(new Vertex(-50 + i * step, 50, 1)); } // Right box boundary points for (int i = 0; i < numPointsB; i++) { - input.AddPoint(50, 50 - i * step, 1); + input.Add(new Vertex(50, 50 - i * step, 1)); } // Bottom box boundary points for (int i = 0; i < numPointsB; i++) { - input.AddPoint(50 - i * step, -50, 1); + input.Add(new Vertex(50 - i * step, -50, 1)); } // Add box segments for (int i = numPoints; i < input.Count - 1; i++) { - input.AddSegment(i, i + 1, 1); + input.Add(new Edge(i, i + 1, 1)); } // Add last segments which closes the box - input.AddSegment(input.Count - 1, numPoints, 1); + input.Add(new Edge(input.Count - 1, numPoints, 1)); // Add hole - input.AddHole(0, 0); + input.Holes.Add(new Point(0, 0)); return input; } diff --git a/Triangle.NET/TestApp/Generators/CircleWithHole.cs b/Triangle.NET/TestApp/Generators/CircleWithHole.cs index bcd8c80..9fe517d 100644 --- a/Triangle.NET/TestApp/Generators/CircleWithHole.cs +++ b/Triangle.NET/TestApp/Generators/CircleWithHole.cs @@ -30,7 +30,7 @@ namespace MeshExplorer.Generators ranges[1] = new int[] { 2, 15 }; } - public override InputGeometry Generate(double param0, double param1, double param2) + public override IPolygon Generate(double param0, double param1, double param2) { // Number of points on the outer circle int n = GetParamValueInt(0, param0); @@ -44,7 +44,7 @@ namespace MeshExplorer.Generators // Current radius and step size double r, dphi; - InputGeometry input = new InputGeometry(n + 1); + var input = new Polygon(n + 1); // Inner cirlce (radius = 1) r = 1; @@ -52,8 +52,8 @@ namespace MeshExplorer.Generators 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); + input.Add(new Vertex(r * Math.Cos(i * dphi), r * Math.Sin(i * dphi), 1)); + input.Add(new Edge(i, (i + 1) % npoints, 1)); } count = input.Count; @@ -64,8 +64,8 @@ namespace MeshExplorer.Generators 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); + input.Add(new Vertex(r * Math.Cos(i * dphi), r * Math.Sin(i * dphi), 2)); + input.Add(new Edge(count + i, count + (i + 1) % npoints, 2)); } count = input.Count; @@ -76,17 +76,17 @@ namespace MeshExplorer.Generators 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.Add(new Vertex(r * Math.Cos(i * dphi), r * Math.Sin(i * dphi), 3)); + input.Add(new Edge(count + i, count + (i + 1) % npoints, 3)); } - input.AddHole(0, 0); + input.Holes.Add(new Point(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); + input.Regions.Add(new RegionPointer((r + 3.0) / 4.0, 0, 1)); + input.Regions.Add(new RegionPointer((3 * r + 1.0) / 4.0, 0, 2)); return input; } diff --git a/Triangle.NET/TestApp/Generators/IGenerator.cs b/Triangle.NET/TestApp/Generators/IGenerator.cs index 426ba97..116ae45 100644 --- a/Triangle.NET/TestApp/Generators/IGenerator.cs +++ b/Triangle.NET/TestApp/Generators/IGenerator.cs @@ -6,10 +6,6 @@ namespace MeshExplorer.Generators { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using TriangleNet.Geometry; /// @@ -22,6 +18,6 @@ namespace MeshExplorer.Generators int ParameterCount { get; } string ParameterDescription(int paramIndex); string ParameterDescription(int paramIndex, double paramValue); - InputGeometry Generate(double param1, double param2, double param3); + IPolygon Generate(double param1, double param2, double param3); } } diff --git a/Triangle.NET/TestApp/Generators/RandomPoints.cs b/Triangle.NET/TestApp/Generators/RandomPoints.cs index fe7c1c3..ded256d 100644 --- a/Triangle.NET/TestApp/Generators/RandomPoints.cs +++ b/Triangle.NET/TestApp/Generators/RandomPoints.cs @@ -6,10 +6,6 @@ namespace MeshExplorer.Generators { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using TriangleNet.Geometry; /// @@ -32,7 +28,7 @@ namespace MeshExplorer.Generators ranges[2] = new int[] { 10, 200 }; } - public override InputGeometry Generate(double param0, double param1, double param2) + public override IPolygon Generate(double param0, double param1, double param2) { int numPoints = GetParamValueInt(0, param0); numPoints = (numPoints / 10) * 10; @@ -42,15 +38,15 @@ namespace MeshExplorer.Generators numPoints = 5; } - InputGeometry input = new InputGeometry(numPoints); + var input = new Polygon(numPoints); int width = GetParamValueInt(1, param1); int height = GetParamValueInt(2, param2); for (int i = 0; i < numPoints; i++) { - input.AddPoint(Util.Random.NextDouble() * width, - Util.Random.NextDouble() * height); + input.Add(new Vertex(Util.Random.NextDouble() * width, + Util.Random.NextDouble() * height)); } return input; diff --git a/Triangle.NET/TestApp/Generators/RandomPointsCircle.cs b/Triangle.NET/TestApp/Generators/RandomPointsCircle.cs index 788be09..7dd3ef7 100644 --- a/Triangle.NET/TestApp/Generators/RandomPointsCircle.cs +++ b/Triangle.NET/TestApp/Generators/RandomPointsCircle.cs @@ -7,9 +7,6 @@ namespace MeshExplorer.Generators { using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using TriangleNet.Geometry; /// @@ -60,7 +57,7 @@ namespace MeshExplorer.Generators return ""; } - public override InputGeometry Generate(double param0, double param1, double param2) + public override IPolygon Generate(double param0, double param1, double param2) { int numPoints = GetParamValueInt(0, param0); numPoints = (numPoints / 10) * 10; @@ -72,7 +69,7 @@ namespace MeshExplorer.Generators double exp = (param1 + 10) / 100; - InputGeometry input = new InputGeometry(numPoints); + var input = new Polygon(numPoints); int i = 0, cNum = 2 * (int)Math.Floor(Math.Sqrt(numPoints)); @@ -84,8 +81,8 @@ namespace MeshExplorer.Generators // Add a little error r = Util.Random.NextDouble(); - input.AddPoint((radius + r) * Math.Cos(i * step), - (radius + r) * Math.Sin(i * step)); + input.Add(new Vertex((radius + r) * Math.Cos(i * step), + (radius + r) * Math.Sin(i * step))); } for (; i < numPoints; i++) @@ -94,7 +91,7 @@ namespace MeshExplorer.Generators r = Math.Pow(Util.Random.NextDouble(), exp) * radius; phi = Util.Random.NextDouble() * Math.PI * 2; - input.AddPoint(r * Math.Cos(phi), r * Math.Sin(phi)); + input.Add(new Vertex(r * Math.Cos(phi), r * Math.Sin(phi))); } return input; diff --git a/Triangle.NET/TestApp/Generators/RingPolygon.cs b/Triangle.NET/TestApp/Generators/RingPolygon.cs index dea4d7f..86386be 100644 --- a/Triangle.NET/TestApp/Generators/RingPolygon.cs +++ b/Triangle.NET/TestApp/Generators/RingPolygon.cs @@ -7,9 +7,6 @@ namespace MeshExplorer.Generators { using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using TriangleNet.Geometry; /// @@ -47,12 +44,12 @@ namespace MeshExplorer.Generators return ""; } - public override InputGeometry Generate(double param0, double param1, double param2) + public override IPolygon Generate(double param0, double param1, double param2) { int n = GetParamValueInt(0, param0); int m = n / 2; - InputGeometry input = new InputGeometry(n + 1); + var input = new Polygon(n + 1); double ro, r = 10; double step = 2 * Math.PI / m; @@ -60,8 +57,8 @@ namespace MeshExplorer.Generators // Inner ring for (int i = 0; i < m; i++) { - input.AddPoint(r * Math.Cos(i * step), r * Math.Sin(i * step)); - input.AddSegment(i, (i + 1) % m, 1); + input.Add(new Vertex(r * Math.Cos(i * step), r * Math.Sin(i * step))); + input.Add(new Edge(i, (i + 1) % m, 1)); } r = 1.5 * r; @@ -80,11 +77,11 @@ namespace MeshExplorer.Generators ro = r + r * Util.Random.NextDouble() * (param1 / 100); } - input.AddPoint(ro * Math.Cos(i * step + offset), ro * Math.Sin(i * step + offset)); - input.AddSegment(m + i, m + ((i + 1) % n), 2); + input.Add(new Vertex(ro * Math.Cos(i * step + offset), ro * Math.Sin(i * step + offset))); + input.Add(new Edge(m + i, m + ((i + 1) % n), 2)); } - input.AddHole(0, 0); + input.Holes.Add(new Point(0, 0)); return input; } diff --git a/Triangle.NET/TestApp/Generators/StarInBox.cs b/Triangle.NET/TestApp/Generators/StarInBox.cs index 3358072..9bd2e4a 100644 --- a/Triangle.NET/TestApp/Generators/StarInBox.cs +++ b/Triangle.NET/TestApp/Generators/StarInBox.cs @@ -7,9 +7,6 @@ namespace MeshExplorer.Generators { using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using TriangleNet.Geometry; /// @@ -28,13 +25,13 @@ namespace MeshExplorer.Generators ranges[0] = new int[] { 3, 61 }; } - public override InputGeometry Generate(double param0, double param1, double param2) + public override IPolygon Generate(double param0, double param1, double param2) { int numRays = GetParamValueInt(0, param0); - InputGeometry input = new InputGeometry(numRays + 4); + var input = new Polygon(numRays + 4); - input.AddPoint(0, 0); // Center + input.Add(new Vertex(0, 0)); // Center double x, y, r, e, step = 2 * Math.PI / numRays; @@ -45,20 +42,20 @@ namespace MeshExplorer.Generators x = r * Math.Cos(i * step + e); y = r * Math.Sin(i * step + e); - input.AddPoint(x, y, 2); - input.AddSegment(0, i + 1, 2); + input.Add(new Vertex(x, y, 2)); + input.Add(new Edge(0, i + 1, 2)); } - input.AddPoint(-1, -1, 1); // Box - input.AddPoint(1, -1, 1); - input.AddPoint(1, 1, 1); - input.AddPoint(-1, 1, 1); + input.Add(new Vertex(-1, -1, 1)); // Box + input.Add(new Vertex(1, -1, 1)); + input.Add(new Vertex(1, 1, 1)); + input.Add(new Vertex(-1, 1, 1)); numRays = input.Count; - input.AddSegment(numRays - 1, numRays - 2, 1); - input.AddSegment(numRays - 2, numRays - 3, 1); - input.AddSegment(numRays - 3, numRays - 4, 1); - input.AddSegment(numRays - 4, numRays - 1, 1); + input.Add(new Edge(numRays - 1, numRays - 2, 1)); + input.Add(new Edge(numRays - 2, numRays - 3, 1)); + input.Add(new Edge(numRays - 3, numRays - 4, 1)); + input.Add(new Edge(numRays - 4, numRays - 1, 1)); return input; } diff --git a/Triangle.NET/TestApp/IO/FileProcessor.cs b/Triangle.NET/TestApp/IO/FileProcessor.cs index 3e0d3c4..353a8f4 100644 --- a/Triangle.NET/TestApp/IO/FileProcessor.cs +++ b/Triangle.NET/TestApp/IO/FileProcessor.cs @@ -51,9 +51,9 @@ namespace MeshExplorer.IO /// /// Read an input geometry from given file. /// - public static InputGeometry Read(string path) + public static IPolygon Read(string path) { - IMeshFile provider = GetProviderInstance(path); + var provider = GetProviderInstance(path); return provider.Read(path); } @@ -65,9 +65,9 @@ namespace MeshExplorer.IO /// public static Mesh Import(string path) { - IMeshFile provider = GetProviderInstance(path); + var provider = GetProviderInstance(path); - return provider.Import(path); + return (Mesh)provider.Import(path); } /// diff --git a/Triangle.NET/TestApp/IO/Formats/JsonFile.cs b/Triangle.NET/TestApp/IO/Formats/JsonFile.cs index e0d39b7..e24590a 100644 --- a/Triangle.NET/TestApp/IO/Formats/JsonFile.cs +++ b/Triangle.NET/TestApp/IO/Formats/JsonFile.cs @@ -80,9 +80,9 @@ namespace MeshExplorer.IO.Formats throw new NotImplementedException(); } - public Mesh Import(string filename) + public IMesh Import(string filename) { - InputGeometry geometry = this.Read(filename); + var geometry = (Polygon)this.Read(filename); List triangles = null; @@ -92,7 +92,7 @@ namespace MeshExplorer.IO.Formats if (tri != null) { - triangles = ReadTriangles(tri, geometry.Count); + triangles = ReadTriangles(tri, geometry.Points.Count); } } @@ -101,7 +101,7 @@ namespace MeshExplorer.IO.Formats return converter.ToMesh(geometry, triangles); } - public void Write(Mesh mesh, string filename) + public void Write(IMesh mesh, string filename) { using (StreamWriter writer = new StreamWriter(filename)) { @@ -119,16 +119,16 @@ namespace MeshExplorer.IO.Formats writer.Write("\"dim\":2"); writer.Write("}"); - if (mesh.CurrentNumbering == NodeNumbering.None) + if (((Mesh)mesh).CurrentNumbering == NodeNumbering.None) { - mesh.Renumber(NodeNumbering.Linear); + ((Mesh)mesh).Renumber(NodeNumbering.Linear); } // Write the coordinates if (nv > 0) { writer.Write(","); - WritePoints(mesh, writer, nv); + WritePoints((Mesh)mesh, writer, nv); } // Write the segments @@ -156,7 +156,7 @@ namespace MeshExplorer.IO.Formats } } - public void Write(Mesh mesh, StreamWriter stream) + public void Write(IMesh mesh, StreamWriter stream) { throw new NotImplementedException(); } @@ -166,11 +166,11 @@ namespace MeshExplorer.IO.Formats /// /// /// - public InputGeometry Read(string filename) + public IPolygon Read(string filename) { ParseJson(filename); - InputGeometry data = new InputGeometry(); + var data = new Polygon(); if (json == null) { @@ -223,12 +223,12 @@ namespace MeshExplorer.IO.Formats return data; } - public void Write(InputGeometry polygon, string filename) + public void Write(IPolygon polygon, string filename) { throw new NotImplementedException(); } - public void Write(InputGeometry polygon, StreamWriter stream) + public void Write(IPolygon polygon, StreamWriter stream) { throw new NotImplementedException(); } @@ -248,7 +248,7 @@ namespace MeshExplorer.IO.Formats #region Read helpers - private void ReadPoints(InputGeometry geometry, Dictionary points, ref int count) + private void ReadPoints(Polygon geometry, Dictionary points, ref int count) { ArrayList data = points["data"] as ArrayList; @@ -286,16 +286,16 @@ namespace MeshExplorer.IO.Formats mark = int.Parse(markers[i / 2].ToString()); } - geometry.AddPoint( + geometry.Add(new Vertex( double.Parse(data[i].ToString(), Util.Nfi), double.Parse(data[i + 1].ToString(), Util.Nfi), mark - ); + )); } } } - private void ReadSegments(InputGeometry geometry, Dictionary segments, int count) + private void ReadSegments(Polygon geometry, Dictionary segments, int count) { ArrayList data = segments["data"] as ArrayList; @@ -334,12 +334,12 @@ namespace MeshExplorer.IO.Formats throw new Exception("JSON format error (segment index)."); } - geometry.AddSegment(p0, p1, mark); + geometry.Add(new Edge(p0, p1, mark)); } } } - private void ReadHoles(InputGeometry geometry, ArrayList holes) + private void ReadHoles(Polygon geometry, ArrayList holes) { int n = holes.Count; @@ -350,10 +350,10 @@ namespace MeshExplorer.IO.Formats for (int i = 0; i < n; i += 2) { - geometry.AddHole( + geometry.Holes.Add(new Point( double.Parse(holes[i].ToString(), Util.Nfi), double.Parse(holes[i + 1].ToString(), Util.Nfi) - ); + )); } } diff --git a/Triangle.NET/TestApp/IO/Formats/TriangleFile.cs b/Triangle.NET/TestApp/IO/Formats/TriangleFile.cs index 5aa43fe..5b3c622 100644 --- a/Triangle.NET/TestApp/IO/Formats/TriangleFile.cs +++ b/Triangle.NET/TestApp/IO/Formats/TriangleFile.cs @@ -8,9 +8,9 @@ namespace MeshExplorer.IO.Formats { using System; using System.IO; - using TriangleNet; using TriangleNet.Geometry; using TriangleNet.IO; + using TriangleNet.Meshing; /// /// Read and write files defined in classic Triangle format. @@ -47,27 +47,27 @@ namespace MeshExplorer.IO.Formats throw new NotImplementedException(); } - public InputGeometry Read(string filename) + public IPolygon Read(string filename) { return format.Read(filename); } - public void Write(InputGeometry polygon, string filename) + public void Write(IPolygon polygon, string filename) { format.Write(polygon, filename); } - public void Write(InputGeometry polygon, StreamWriter stream) + public void Write(IPolygon polygon, StreamWriter stream) { format.Write(polygon, stream); } - public Mesh Import(string filename) + public IMesh Import(string filename) { return format.Import(filename); } - public void Write(Mesh mesh, string filename) + public void Write(IMesh mesh, string filename) { if (mesh.Vertices.Count > 0) { @@ -75,9 +75,9 @@ namespace MeshExplorer.IO.Formats } } - public void Write(Mesh mesh, StreamWriter stream) + public void Write(IMesh mesh, StreamWriter stream) { - throw new NotImplementedException(); + format.Write(mesh, stream); } } } diff --git a/Triangle.NET/TestApp/Topology/RectanglePolygon.cs b/Triangle.NET/TestApp/Topology/RectanglePolygon.cs index 6deb24b..80def75 100644 --- a/Triangle.NET/TestApp/Topology/RectanglePolygon.cs +++ b/Triangle.NET/TestApp/Topology/RectanglePolygon.cs @@ -5,9 +5,9 @@ namespace MeshExplorer.Topology internal static class RectanglePolygon { - public static InputGeometry Generate(int n, double bounds = 10.0) + public static IPolygon Generate(int n, double bounds = 10.0) { - var geometry = new InputGeometry((n + 1) * (n + 1)); + var geometry = new Polygon((n + 1) * (n + 1)); double x, y, d = 2 * bounds / n; @@ -21,7 +21,7 @@ namespace MeshExplorer.Topology { x = -bounds + j * d; - geometry.AddPoint(x, y, mark); + geometry.Add(new Vertex(x, y, mark)); } } @@ -29,16 +29,16 @@ namespace MeshExplorer.Topology for (int i = 0; i < n; i++) { // Bottom - geometry.AddSegment(i, i + 1); + geometry.Add(new Edge(i, i + 1)); // Right - geometry.AddSegment(i * (n + 1) + n, (i + 1) * (n + 1) + n); + geometry.Add(new Edge(i * (n + 1) + n, (i + 1) * (n + 1) + n)); // Top - geometry.AddSegment(n * (n + 1) + i, n * (n + 1) + (i + 1)); + geometry.Add(new Edge(n * (n + 1) + i, n * (n + 1) + (i + 1))); // Left - geometry.AddSegment(i * (n + 1), (i + 1) * (n + 1)); + geometry.Add(new Edge(i * (n + 1), (i + 1) * (n + 1))); } return geometry; diff --git a/Triangle.NET/TestApp/Views/AboutView.Designer.cs b/Triangle.NET/TestApp/Views/AboutView.Designer.cs index a3d08e3..1db81f5 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 (2013-06-18)\r\nChristian Woltering\r\nMIT"; + this.label19.Text = "Beta 4 (2014-05-30)\r\nChristian Woltering\r\nMIT"; // // label18 // diff --git a/Triangle.NET/TestApp/Views/AboutView.cs b/Triangle.NET/TestApp/Views/AboutView.cs index 9c712ae..b3b23b2 100644 --- a/Triangle.NET/TestApp/Views/AboutView.cs +++ b/Triangle.NET/TestApp/Views/AboutView.cs @@ -32,11 +32,11 @@ namespace MeshExplorer.Views #region IView - public void HandleNewInput(InputGeometry geometry) + public void HandleNewInput(IPolygon geometry) { } - public void HandleMeshImport(InputGeometry geometry, Mesh mesh) + public void HandleMeshImport(IPolygon geometry, Mesh mesh) { } diff --git a/Triangle.NET/TestApp/Views/IView.cs b/Triangle.NET/TestApp/Views/IView.cs index 5b01b7e..42d5a50 100644 --- a/Triangle.NET/TestApp/Views/IView.cs +++ b/Triangle.NET/TestApp/Views/IView.cs @@ -18,8 +18,8 @@ namespace MeshExplorer.Views /// public interface IView { - void HandleNewInput(InputGeometry geometry); - void HandleMeshImport(InputGeometry geometry, Mesh mesh); + void HandleNewInput(IPolygon geometry); + void HandleMeshImport(IPolygon geometry, Mesh mesh); void HandleMeshUpdate(Mesh mesh); void HandleMeshChange(Mesh mesh); } diff --git a/Triangle.NET/TestApp/Views/MeshControlView.cs b/Triangle.NET/TestApp/Views/MeshControlView.cs index 4342350..c478d5d 100644 --- a/Triangle.NET/TestApp/Views/MeshControlView.cs +++ b/Triangle.NET/TestApp/Views/MeshControlView.cs @@ -76,11 +76,11 @@ namespace MeshExplorer.Views #region IView - public void HandleNewInput(InputGeometry geometry) + public void HandleNewInput(IPolygon geometry) { } - public void HandleMeshImport(InputGeometry geometry, Mesh mesh) + public void HandleMeshImport(IPolygon geometry, Mesh mesh) { } diff --git a/Triangle.NET/TestApp/Views/StatisticView.cs b/Triangle.NET/TestApp/Views/StatisticView.cs index aa85c79..d462ced 100644 --- a/Triangle.NET/TestApp/Views/StatisticView.cs +++ b/Triangle.NET/TestApp/Views/StatisticView.cs @@ -34,14 +34,14 @@ namespace MeshExplorer.Views #region IView - public void HandleNewInput(InputGeometry geometry) + public void HandleNewInput(IPolygon geometry) { // Reset labels lbNumVert2.Text = "-"; lbNumTri2.Text = "-"; lbNumSeg2.Text = "-"; - lbNumVert.Text = geometry.Count.ToString(); + lbNumVert.Text = geometry.Points.Count.ToString(); lbNumSeg.Text = geometry.Segments.Count().ToString(); lbNumTri.Text = "0"; @@ -62,7 +62,7 @@ namespace MeshExplorer.Views angleHistogram1.SetData(null, null); } - public void HandleMeshImport(InputGeometry geometry, Mesh mesh) + public void HandleMeshImport(IPolygon geometry, Mesh mesh) { // Previous mesh stats lbNumVert2.Text = "-"; diff --git a/Triangle.NET/Triangle/Behavior.cs b/Triangle.NET/Triangle/Behavior.cs index 591e347..b0d9d91 100644 --- a/Triangle.NET/Triangle/Behavior.cs +++ b/Triangle.NET/Triangle/Behavior.cs @@ -9,15 +9,12 @@ namespace TriangleNet { using System; using TriangleNet.Geometry; - using TriangleNet.Logging; /// /// Controls the behavior of the meshing software. /// - public class Behavior + class Behavior { - #region Class members - bool poly = false; bool quality = false; bool varArea = false; @@ -26,7 +23,6 @@ namespace TriangleNet bool boundaryMarkers = true; bool noHoles = false; bool conformDel = false; - TriangulationAlgorithm algorithm = TriangulationAlgorithm.Dwyer; Func usertest; @@ -44,8 +40,6 @@ namespace TriangleNet internal double maxGoodAngle = 0.0; internal double offconstant = 0.0; - #endregion - /// /// Creates an instance of the Behavior class. /// @@ -153,7 +147,7 @@ namespace TriangleNet set { maxArea = value; - fixedArea = value > 0; + fixedArea = value > 0.0; } } @@ -202,15 +196,6 @@ namespace TriangleNet set { conformDel = value; } } - /// - /// Algorithm to use for triangulation. - /// - public TriangulationAlgorithm Algorithm - { - get { return algorithm; } - set { algorithm = value; } - } - /// /// Suppresses boundary segment splitting. /// diff --git a/Triangle.NET/Triangle/Enums.cs b/Triangle.NET/Triangle/Enums.cs index fc094bc..f77f0e0 100644 --- a/Triangle.NET/Triangle/Enums.cs +++ b/Triangle.NET/Triangle/Enums.cs @@ -8,14 +8,14 @@ namespace TriangleNet { /// - /// Implemented triangulation algorithms. + /// The type of the mesh vertex. /// - public enum TriangulationAlgorithm - { - Dwyer, - Incremental, - SweepLine - }; + public enum VertexType { InputVertex, SegmentVertex, FreeVertex, DeadVertex, UndeadVertex }; + + /// + /// Node renumbering algorithms. + /// + public enum NodeNumbering { None, Linear, CuthillMcKee }; /// /// Labels that signify the result of point location. @@ -43,14 +43,4 @@ namespace TriangleNet /// direction triangle, or along the right edge of the direction triangle. /// enum FindDirectionResult { Within, Leftcollinear, Rightcollinear }; - - /// - /// The type of the mesh vertex. - /// - public enum VertexType { InputVertex, SegmentVertex, FreeVertex, DeadVertex, UndeadVertex }; - - /// - /// Node renumbering algorithms. - /// - public enum NodeNumbering { None, Linear, CuthillMcKee }; } diff --git a/Triangle.NET/Triangle/Geometry/IPolygonExtensions.cs b/Triangle.NET/Triangle/Geometry/IPolygonExtensions.cs new file mode 100644 index 0000000..a33ccf6 --- /dev/null +++ b/Triangle.NET/Triangle/Geometry/IPolygonExtensions.cs @@ -0,0 +1,74 @@ + +namespace TriangleNet.Geometry +{ + using TriangleNet.Meshing; + + public static class IPolygonExtensions + { + /// + /// Triangulates a polygon. + /// + public static IMesh Triangulate(this IPolygon polygon) + { + var mesher = new GenericMesher(); + + return mesher.Triangulate(polygon, null, null); + } + + /// + /// Triangulates a polygon, applying constraint options. + /// + /// Constraint options. + public static IMesh Triangulate(this IPolygon polygon, ConstraintOptions options) + { + var mesher = new GenericMesher(); + + return mesher.Triangulate(polygon, options, null); + } + + /// + /// Triangulates a polygon, applying quality options. + /// + /// Quality options. + public static IMesh Triangulate(this IPolygon polygon, QualityOptions quality) + { + var mesher = new GenericMesher(); + + return mesher.Triangulate(polygon, null, quality); + } + + /// + /// Triangulates a polygon, applying quality and constraint options. + /// + /// Constraint options. + /// Quality options. + public static IMesh Triangulate(this IPolygon polygon, ConstraintOptions options, QualityOptions quality) + { + var mesher = new GenericMesher(); + + var mesh = (Mesh)mesher.Triangulate(polygon.Points); + + mesh.ApplyConstraints(polygon, options, quality); + + return mesh; + } + + /// + /// Triangulates a polygon, applying quality and constraint options. + /// + /// Constraint options. + /// Quality options. + /// The triangulation algorithm. + public static IMesh Triangulate(this IPolygon polygon, ConstraintOptions options, QualityOptions quality, + ITriangulator triangulator) + { + var mesher = new GenericMesher(triangulator); + + var mesh = (Mesh)mesher.Triangulate(polygon.Points); + + mesh.ApplyConstraints(polygon, options, quality); + + return mesh; + } + } +} diff --git a/Triangle.NET/Triangle/Geometry/InputGeometry.cs b/Triangle.NET/Triangle/Geometry/InputGeometry.cs deleted file mode 100644 index 1c04358..0000000 --- a/Triangle.NET/Triangle/Geometry/InputGeometry.cs +++ /dev/null @@ -1,257 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ -// -// ----------------------------------------------------------------------- - -namespace TriangleNet.Geometry -{ - using System; - using System.Collections.Generic; - using TriangleNet.Data; - - /// - /// The input geometry which will be triangulated. May represent a - /// pointset or a planar straight line graph. - /// - public class InputGeometry - { - internal List points; - internal List segments; - internal List holes; - internal List regions; - - Rectangle bounds; - - // Used to check consitent use of point attributes. - private int pointAttributes = -1; - - /// - /// Initializes a new instance of the class. - /// - public InputGeometry() - : this(3) - { - } - - /// - /// Initializes a new instance of the class. - /// The point list will be initialized with a given capacity. - /// - /// Point list capacity. - public InputGeometry(int capacity) - { - points = new List(capacity); - segments = new List(); - holes = new List(); - regions = new List(); - - bounds = new Rectangle(); - - pointAttributes = -1; - } - - /// - /// Gets the bounding box of the input geometry. - /// - public Rectangle Bounds - { - get { return bounds; } - } - - /// - /// Indicates, whether the geometry should be treated as a PSLG. - /// - public bool HasSegments - { - get { return segments.Count > 0; } - } - - /// - /// Gets the number of points. - /// - public int Count - { - get { return points.Count; } - } - - /// - /// Gets the list of input points. - /// - public IEnumerable Points - { - get { return points; } - } - - /// - /// Gets the list of input segments. - /// - public ICollection Segments - { - get { return segments; } - } - - /// - /// Gets the list of input holes. - /// - public ICollection Holes - { - get { return holes; } - } - - /// - /// Gets the list of regions. - /// - public ICollection Regions - { - get { return regions; } - } - - /// - /// Clear input geometry. - /// - public void Clear() - { - points.Clear(); - segments.Clear(); - holes.Clear(); - regions.Clear(); - - pointAttributes = -1; - } - - /// - /// Adds a point to the geometry. - /// - /// X coordinate. - /// Y coordinate. - public void AddPoint(double x, double y) - { - AddPoint(x, y, 0); - } - - /// - /// Adds a point to the geometry. - /// - /// X coordinate. - /// Y coordinate. - /// Boundary marker. - public void AddPoint(double x, double y, int boundary) - { - var v = new Vertex(x, y, boundary); - points.Add(v); - bounds.Expand(v); - } - - /// - /// Adds a point to the geometry. - /// - /// X coordinate. - /// Y coordinate. - /// Boundary marker. - /// Point attribute. - public void AddPoint(double x, double y, int boundary, double attribute) - { - AddPoint(x, y, 0, new double[] { attribute }); - } - - /// - /// Adds a point to the geometry. - /// - /// X coordinate. - /// Y coordinate. - /// Boundary marker. - /// Point attributes. - public void AddPoint(double x, double y, int boundary, double[] attribs) - { - if (pointAttributes < 0) - { - pointAttributes = attribs == null ? 0 : attribs.Length; - } - else if (attribs == null && pointAttributes > 0) - { - throw new ArgumentException("Inconsitent use of point attributes."); - } - else if (attribs != null && pointAttributes != attribs.Length) - { - throw new ArgumentException("Inconsitent use of point attributes."); - } - - var v = new Vertex(x, y, boundary) { attributes = attribs }; - points.Add(v); - bounds.Expand(v); - } - - /// - /// Adds a vertex to the geometry. - /// - /// Vertex instance. - public void AddPoint(Vertex v) - { - var attribs = v.attributes; - - if (pointAttributes < 0) - { - pointAttributes = attribs == null ? 0 : attribs.Length; - } - else if (attribs == null && pointAttributes > 0) - { - throw new ArgumentException("Inconsitent use of point attributes."); - } - else if (attribs != null && pointAttributes != attribs.Length) - { - throw new ArgumentException("Inconsitent use of point attributes."); - } - - points.Add(v); - bounds.Expand(v); - } - - /// - /// Adds a hole location to the geometry. - /// - /// X coordinate of the hole. - /// Y coordinate of the hole. - public void AddHole(double x, double y) - { - holes.Add(new Point(x, y)); - } - - /// - /// Adds a hole location to the geometry. - /// - /// X coordinate of the hole. - /// Y coordinate of the hole. - /// The region id. - public void AddRegion(double x, double y, int id) - { - regions.Add(new RegionPointer(x, y, id)); - } - - /// - /// Adds a segment to the geometry. - /// - /// First endpoint. - /// Second endpoint. - public void AddSegment(int p0, int p1) - { - AddSegment(p0, p1, 0); - } - - /// - /// Adds a segment to the geometry. - /// - /// First endpoint. - /// Second endpoint. - /// Segment marker. - public void AddSegment(int p0, int p1, int boundary) - { - if (p0 == p1 || p0 < 0 || p1 < 0) - { - throw new NotSupportedException("Invalid endpoints."); - } - - segments.Add(new Edge(p0, p1, boundary)); - } - } -} diff --git a/Triangle.NET/Triangle/Geometry/Rectangle.cs b/Triangle.NET/Triangle/Geometry/Rectangle.cs index 5b90eea..63f616a 100644 --- a/Triangle.NET/Triangle/Geometry/Rectangle.cs +++ b/Triangle.NET/Triangle/Geometry/Rectangle.cs @@ -34,16 +34,16 @@ namespace TriangleNet.Geometry /// Initializes a new instance of the class /// with predefined bounds. /// - /// Minimum x value. - /// Minimum y value. - /// Maximum x value. - /// Maximum y value. - public Rectangle(double xmin, double ymin, double xmax, double ymax) + /// Minimum x value (left). + /// Minimum y value (bottom). + /// Width of the rectangle. + /// Height of the rectangle. + public Rectangle(double x, double y, double width, double height) { - this.xmin = xmin; - this.xmax = xmax; - this.ymin = ymin; - this.ymax = ymax; + this.xmin = x; + this.ymin = y; + this.xmax = x + width; + this.ymax = y + height; } /// diff --git a/Triangle.NET/Triangle/IO/DebugWriter.cs b/Triangle.NET/Triangle/IO/DebugWriter.cs index 09da57a..a8f2409 100644 --- a/Triangle.NET/Triangle/IO/DebugWriter.cs +++ b/Triangle.NET/Triangle/IO/DebugWriter.cs @@ -129,7 +129,7 @@ namespace TriangleNet.IO } } - private void WriteGeometry(InputGeometry geometry) + private void WriteGeometry(IPolygon geometry) { stream.WriteLine("#!G{0}", this.iteration++); } diff --git a/Triangle.NET/Triangle/IO/FileProcessor.cs b/Triangle.NET/Triangle/IO/FileProcessor.cs index 46d20d7..e96fe78 100644 --- a/Triangle.NET/Triangle/IO/FileProcessor.cs +++ b/Triangle.NET/Triangle/IO/FileProcessor.cs @@ -35,7 +35,7 @@ namespace TriangleNet.IO /// /// The path of the file to read. /// An instance of the class. - public static InputGeometry Read(string filename) + public static IPolygon Read(string filename) { foreach (IPolygonFormat format in formats) { @@ -53,7 +53,7 @@ namespace TriangleNet.IO /// /// An instance of the class. /// The path of the file to save. - public static void Write(InputGeometry polygon, string filename) + public static void Write(IPolygon polygon, string filename) { foreach (IPolygonFormat format in formats) { @@ -75,7 +75,7 @@ namespace TriangleNet.IO /// /// The path of the file to read. /// An instance of the interface. - public static Mesh Import(string filename) + public static IMesh Import(string filename) { foreach (IMeshFormat format in formats) { @@ -93,7 +93,7 @@ namespace TriangleNet.IO /// /// An instance of the interface. /// The path of the file to save. - public static void Write(Mesh mesh, string filename) + public static void Write(IMesh mesh, string filename) { foreach (IMeshFormat format in formats) { diff --git a/Triangle.NET/Triangle/IO/IMeshFormat.cs b/Triangle.NET/Triangle/IO/IMeshFormat.cs index 1524f3a..ec91624 100644 --- a/Triangle.NET/Triangle/IO/IMeshFormat.cs +++ b/Triangle.NET/Triangle/IO/IMeshFormat.cs @@ -7,6 +7,7 @@ namespace TriangleNet.IO { using System.IO; + using TriangleNet.Meshing; /// /// Interface for mesh I/O. @@ -18,20 +19,20 @@ namespace TriangleNet.IO /// /// The path of the file to read. /// An instance of the interface. - Mesh Import(string filename); + IMesh Import(string filename); /// /// Save a mesh to disk. /// /// An instance of the interface. /// The path of the file to save. - void Write(Mesh mesh, string filename); + void Write(IMesh mesh, string filename); /// /// Save a mesh to a . /// /// An instance of the interface. /// The stream to save to. - void Write(Mesh mesh, StreamWriter stream); + void Write(IMesh mesh, StreamWriter stream); } } diff --git a/Triangle.NET/Triangle/IO/IPolygonFormat.cs b/Triangle.NET/Triangle/IO/IPolygonFormat.cs index db9f0e7..e726442 100644 --- a/Triangle.NET/Triangle/IO/IPolygonFormat.cs +++ b/Triangle.NET/Triangle/IO/IPolygonFormat.cs @@ -19,20 +19,20 @@ namespace TriangleNet.IO /// /// The path of the file to read. /// An instance of the class. - InputGeometry Read(string filename); + IPolygon Read(string filename); /// /// Save a polygon geometry to disk. /// /// An instance of the class. /// The path of the file to save. - void Write(InputGeometry polygon, string filename); + void Write(IPolygon polygon, string filename); /// /// Save a polygon geometry to a . /// /// An instance of the class. /// The stream to save to. - void Write(InputGeometry polygon, StreamWriter stream); + void Write(IPolygon polygon, StreamWriter stream); } } diff --git a/Triangle.NET/Triangle/IO/TriangleFormat.cs b/Triangle.NET/Triangle/IO/TriangleFormat.cs index 6dcadbe..87ed979 100644 --- a/Triangle.NET/Triangle/IO/TriangleFormat.cs +++ b/Triangle.NET/Triangle/IO/TriangleFormat.cs @@ -29,14 +29,14 @@ namespace TriangleNet.IO return false; } - public Mesh Import(string filename) + public IMesh Import(string filename) { string ext = Path.GetExtension(filename); if (ext == ".node" || ext == ".poly" || ext == ".ele") { List triangles; - InputGeometry geometry; + Polygon geometry; TriangleReader.Read(filename, out geometry, out triangles); @@ -51,18 +51,18 @@ namespace TriangleNet.IO throw new NotSupportedException("Could not load '" + filename + "' file."); } - public void Write(Mesh mesh, string filename) + public void Write(IMesh mesh, string filename) { TriangleWriter.WritePoly((Mesh)mesh, Path.ChangeExtension(filename, ".poly")); TriangleWriter.WriteElements((Mesh)mesh, Path.ChangeExtension(filename, ".ele")); } - public void Write(Mesh mesh, StreamWriter stream) + public void Write(IMesh mesh, StreamWriter stream) { throw new NotImplementedException(); } - public InputGeometry Read(string filename) + public IPolygon Read(string filename) { string ext = Path.GetExtension(filename); @@ -80,12 +80,12 @@ namespace TriangleNet.IO } - public void Write(InputGeometry polygon, string filename) + public void Write(IPolygon polygon, string filename) { TriangleWriter.WritePoly(polygon, filename); } - public void Write(InputGeometry polygon, StreamWriter stream) + public void Write(IPolygon polygon, StreamWriter stream) { throw new NotImplementedException(); } diff --git a/Triangle.NET/Triangle/IO/TriangleReader.cs b/Triangle.NET/Triangle/IO/TriangleReader.cs index 01e38fd..71aed56 100644 --- a/Triangle.NET/Triangle/IO/TriangleReader.cs +++ b/Triangle.NET/Triangle/IO/TriangleReader.cs @@ -59,7 +59,7 @@ namespace TriangleNet.IO /// The current line. /// Number of point attributes /// Number of point markers (0 or 1) - static void ReadVertex(InputGeometry data, int index, string[] line, int attributes, int marks) + static void ReadVertex(Polygon data, int index, string[] line, int attributes, int marks) { double x = double.Parse(line[1], nfi); double y = double.Parse(line[2], nfi); @@ -81,7 +81,7 @@ namespace TriangleNet.IO mark = int.Parse(line[3 + attributes]); } - data.AddPoint(x, y, mark, attribs); + data.Add(new Vertex(x, y, mark), attribs); } #endregion @@ -91,7 +91,7 @@ namespace TriangleNet.IO /// /// Reads geometry information from .node or .poly files. /// - public static void Read(string filename, out InputGeometry geometry) + public static void Read(string filename, out Polygon geometry) { geometry = null; @@ -111,7 +111,7 @@ namespace TriangleNet.IO /// /// Reads a mesh from .node, .poly or .ele files. /// - public static void Read(string filename, out InputGeometry geometry, out List triangles) + public static void Read(string filename, out Polygon geometry, out List triangles) { triangles = null; @@ -128,9 +128,9 @@ namespace TriangleNet.IO /// /// Reads geometry information from .node or .poly files. /// - public static InputGeometry Read(string filename) + public static IPolygon Read(string filename) { - InputGeometry geometry = null; + Polygon geometry = null; TriangleReader.Read(filename, out geometry); @@ -144,7 +144,7 @@ namespace TriangleNet.IO /// /// /// Will NOT read associated .ele by default. - public static InputGeometry ReadNodeFile(string nodefilename) + public static Polygon ReadNodeFile(string nodefilename) { return ReadNodeFile(nodefilename, false); } @@ -154,9 +154,9 @@ namespace TriangleNet.IO /// /// /// - public static InputGeometry ReadNodeFile(string nodefilename, bool readElements) + public static Polygon ReadNodeFile(string nodefilename, bool readElements) { - InputGeometry data; + Polygon data; startIndex = 0; @@ -197,7 +197,7 @@ namespace TriangleNet.IO nodemarkers = int.Parse(line[3]); } - data = new InputGeometry(invertices); + data = new Polygon(invertices); // Read the vertices. if (invertices > 0) @@ -242,7 +242,7 @@ namespace TriangleNet.IO /// /// /// Will NOT read associated .ele by default. - public static InputGeometry ReadPolyFile(string polyfilename) + public static Polygon ReadPolyFile(string polyfilename) { return ReadPolyFile(polyfilename, false, false); } @@ -253,7 +253,7 @@ namespace TriangleNet.IO /// /// If true, look for an associated .ele file. /// Will NOT read associated .area by default. - public static InputGeometry ReadPolyFile(string polyfilename, bool readElements) + public static Polygon ReadPolyFile(string polyfilename, bool readElements) { return ReadPolyFile(polyfilename, readElements, false); } @@ -264,10 +264,10 @@ namespace TriangleNet.IO /// /// If true, look for an associated .ele file. /// If true, look for an associated .area file. - public static InputGeometry ReadPolyFile(string polyfilename, bool readElements, bool readArea) + public static Polygon ReadPolyFile(string polyfilename, bool readElements, bool readArea) { // Read poly file - InputGeometry data; + Polygon data; startIndex = 0; @@ -306,7 +306,7 @@ namespace TriangleNet.IO // Read the vertices. if (invertices > 0) { - data = new InputGeometry(invertices); + data = new Polygon(invertices); for (int i = 0; i < invertices; i++) { @@ -335,7 +335,7 @@ namespace TriangleNet.IO // the vertices should be read from a separate .node file. string nodefile = Path.ChangeExtension(polyfilename, ".node"); data = ReadNodeFile(nodefile); - invertices = data.Count; + invertices = data.Points.Count; } if (data.Points == null) @@ -401,7 +401,7 @@ namespace TriangleNet.IO } else { - data.AddSegment(end1, end2, mark); + data.Add(new Edge(end1, end2, mark)); } } @@ -428,8 +428,8 @@ namespace TriangleNet.IO throw new Exception("Invalid hole."); } - data.AddHole(double.Parse(line[1], nfi), - double.Parse(line[2], nfi)); + data.Holes.Add(new Point(double.Parse(line[1], nfi), + double.Parse(line[2], nfi))); } } @@ -452,12 +452,10 @@ namespace TriangleNet.IO throw new Exception("Invalid region attributes."); } - data.AddRegion( - // Region x and y - double.Parse(line[1], nfi), - double.Parse(line[2], nfi), - // Region id - int.Parse(line[3])); + data.Regions.Add(new RegionPointer( + double.Parse(line[1], nfi), // Region x + double.Parse(line[2], nfi), // Region y + int.Parse(line[3]))); // Region id } } } diff --git a/Triangle.NET/Triangle/IO/TriangleWriter.cs b/Triangle.NET/Triangle/IO/TriangleWriter.cs index 473d390..b3c9ab8 100644 --- a/Triangle.NET/Triangle/IO/TriangleWriter.cs +++ b/Triangle.NET/Triangle/IO/TriangleWriter.cs @@ -186,16 +186,16 @@ namespace TriangleNet.IO /// If the nodes should not be written into this file, /// make sure a .node file was written before, so that the nodes /// are numbered right. - public static void WritePoly(InputGeometry polygon, string filename) + public static void WritePoly(IPolygon polygon, string filename) { - bool hasMarkers = true; + bool hasMarkers = polygon.HasSegmentMarkers; using (StreamWriter writer = new StreamWriter(filename)) { // TODO: write vertex attributes // Write nodes to this file. - TriangleWriter.WriteNodes(writer, polygon.points, true, 0, false); + TriangleWriter.WriteNodes(writer, polygon.Points, polygon.HasPointMarkers, 0, false); // Number of segments, number of boundary markers (zero or one). writer.WriteLine("{0} {1}", polygon.Segments.Count, hasMarkers ? "1" : "0"); diff --git a/Triangle.NET/Triangle/Mesh.cs b/Triangle.NET/Triangle/Mesh.cs index 0fd647e..b03ff1a 100644 --- a/Triangle.NET/Triangle/Mesh.cs +++ b/Triangle.NET/Triangle/Mesh.cs @@ -9,26 +9,23 @@ namespace TriangleNet { using System; using System.Collections.Generic; - using System.Linq; using TriangleNet.Data; - using TriangleNet.Logging; - using TriangleNet.IO; - using TriangleNet.Meshing; - using TriangleNet.Meshing.Algorithm; - using TriangleNet.Smoothing; using TriangleNet.Geometry; + using TriangleNet.Logging; + using TriangleNet.Meshing; + using TriangleNet.Meshing.Iterators; using TriangleNet.Tools; /// /// Mesh data structure. /// - public class Mesh + public class Mesh : IMesh { #region Variables ILog logger; - QualityMesher quality; + QualityMesher qualityMesher; // Stack that maintains a list of recently flipped triangles. Stack flipstack; @@ -85,14 +82,6 @@ namespace TriangleNet #region Public properties - /// - /// Gets the mesh behavior instance. - /// - public Behavior Behavior - { - get { return this.behavior; } - } - /// /// Gets the mesh bounding box. /// @@ -140,7 +129,7 @@ namespace TriangleNet { get { - EdgeEnumerator e = new EdgeEnumerator(this); + var e = new EdgeIterator(this); while (e.MoveNext()) { yield return e.Current; @@ -177,17 +166,7 @@ namespace TriangleNet /// Initializes a new instance of the class. /// public Mesh() - : this(new Behavior()) { - } - - /// - /// Initializes a new instance of the class. - /// - public Mesh(Behavior behavior) - { - this.behavior = behavior; - logger = Log.Instance; behavior = new Behavior(); @@ -201,7 +180,7 @@ namespace TriangleNet holes = new List(); regions = new List(); - quality = new QualityMesher(this); + qualityMesher = new QualityMesher(this); locator = new TriangleLocator(this); @@ -214,217 +193,14 @@ namespace TriangleNet } } - /// - /// Triangulate given input file (.node or .poly). - /// - /// - public void Triangulate(string inputFile) + public void Refine(QualityOptions quality) { - InputGeometry input = TriangleReader.Read(inputFile); + behavior.MinAngle = quality.MinimumAngle; + behavior.MaxAngle = quality.MaximumAngle; - this.Triangulate(input); - } - - /// - /// Triangulate given input data. - /// - /// - public void Triangulate(InputGeometry input) - { - ResetData(); - - behavior.Poly = input.HasSegments; - //behavior.useSegments = input.HasSegments; - - //if (input.EdgeMarkers != null) - //{ - // behavior.UseBoundaryMarkers = true; - //} - - // TODO: remove - if (!behavior.Poly) - { - // Be careful not to allocate space for element area constraints that - // will never be assigned any value (other than the default -1.0). - behavior.VarArea = false; - - // 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.useRegions = false; - } - - behavior.useRegions = input.Regions.Count > 0; - - steinerleft = behavior.SteinerPoints; - - TransferNodes(input); - - hullsize = Delaunay(); // Triangulate the vertices. - - // Ensure that no vertex can be mistaken for a triangular bounding - // box vertex in insertvertex(). - infvertex1 = null; - infvertex2 = null; - infvertex3 = null; - - // Insert segments, carving holes. - var mesher = new ConstraintMesher(this); - - if (behavior.useSegments) - { - // Segments will be introduced next. - checksegments = true; - - // Insert PSLG segments and/or convex hull segments. - mesher.FormSkeleton(input); - } - - if (behavior.Poly && (triangles.Count > 0)) - { - // Copy holes - foreach (var item in input.holes) - { - holes.Add(item); - } - - // Copy regions - foreach (var item in input.regions) - { - regions.Add(item); - } - - //dummytri.neighbors[2].triangle = dummytri; - - // Carve out holes and concavities. - mesher.CarveHoles(); - } - else - { - // Without a PSLG, there can be no holes or regional attributes - // or area constraints. The following are set to zero to avoid - // an accidental free() later. - // - // TODO: - - holes.Clear(); - regions.Clear(); - } - - if ((behavior.Quality || behavior.ConformingDelaunay) && triangles.Count > 0) - { - quality.EnforceQuality(); // Enforce angle and area constraints. - } - - // Calculate the number of edges. - edges = (3 * triangles.Count + hullsize) / 2; - } - - /// - /// Refines the current mesh by finding the maximum triangle area and setting - /// the a global area constraint to half its size. - /// - public void Refine(bool halfArea) - { - if (halfArea) - { - double tmp, maxArea = 0; - - foreach (var t in this.triangles.Values) - { - tmp = (t.vertices[2].x - t.vertices[0].x) * (t.vertices[1].y - t.vertices[0].y) - - (t.vertices[1].x - t.vertices[0].x) * (t.vertices[2].y - t.vertices[0].y); - - tmp = Math.Abs(tmp) / 2.0; - - if (tmp > maxArea) - { - maxArea = tmp; - } - } - - this.Refine(maxArea / 2); - } - else - { - Refine(); - } - } - - /// - /// Refines the current mesh by setting a global area constraint. - /// - /// Global area constraint. - public void Refine(double areaConstraint) - { - behavior.fixedArea = true; - behavior.MaxArea = areaConstraint; + behavior.MaxArea = quality.MaximumArea; this.Refine(); - - // Reset option for sanity - behavior.fixedArea = false; - behavior.MaxArea = -1.0; - } - - /// - /// Refines the current mesh. - /// - public void Refine() - { - inelements = triangles.Count; - invertices = vertices.Count; - - // TODO: Set all vertex types to input (i.e. NOT free)? - - if (behavior.Poly) - { - if (behavior.useSegments) - { - insegments = subsegs.Count; - } - else - { - insegments = (int)hullsize; - } - } - - Reset(); - - steinerleft = behavior.SteinerPoints; - - // Ensure that no vertex can be mistaken for a triangular bounding - // box vertex in insertvertex(). - infvertex1 = null; - infvertex2 = null; - infvertex3 = null; - - if (behavior.useSegments) - { - checksegments = true; - } - - // TODO - //holes.Clear(); - //regions.Clear(); - - if (triangles.Count > 0) - { - // Enforce angle and area constraints. - quality.EnforceQuality(); - } - - // Calculate the number of edges. - edges = (3 * triangles.Count + hullsize) / 2; - } - - /// - /// Smooth the current mesh. - /// - public void Smooth() - { - numbering = NodeNumbering.None; - - ISmoother smoother = new SimpleSmoother(this); - smoother.Smooth(); } /// @@ -481,33 +257,226 @@ namespace TriangleNet #region Misc + /* /// - /// Form a Delaunay triangulation. + /// Load a mesh from file (.node/poly and .ele). /// - /// The number of points on the hull. - private int Delaunay() + public void Load(string filename) { - int hulledges = 0; + List triangles; + InputGeometry geometry; - if (behavior.Algorithm == TriangulationAlgorithm.Dwyer) + FileReader.Read(filename, out geometry, out triangles); + + if (geometry != null && triangles != null) { - Dwyer alg = new Dwyer(); - hulledges = alg.Triangulate(this); + Load(geometry, triangles); } - else if (behavior.Algorithm == TriangulationAlgorithm.SweepLine) + } + + /// + /// Reconstructs a mesh from raw input data. + /// + public void Load(InputGeometry input, List triangles) + { + if (input == null || triangles == null) { - SweepLine alg = new SweepLine(); - hulledges = alg.Triangulate(this); - } - else - { - Incremental alg = new Incremental(); - hulledges = alg.Triangulate(this); + throw new ArgumentException("Invalid input (argument is null)."); } - // The input vertices may all be collinear, so there are - // no triangles. - return (triangles.Count == 0) ? 0 : hulledges; + // Clear all data structures / reset hash seeds + this.ResetData(); + + if (input.HasSegments) + { + behavior.Poly = true; + + this.holes.AddRange(input.Holes); + } + + //if (input.EdgeMarkers != null) + //{ + // behavior.UseBoundaryMarkers = true; + //} + + //if (input.TriangleAreas != null) + //{ + // behavior.VarArea = true; + //} + + // TODO: remove + if (!behavior.Poly) + { + // Be careful not to allocate space for element area constraints that + // will never be assigned any value (other than the default -1.0). + behavior.VarArea = false; + + // 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.useRegions = false; + } + + behavior.useRegions = input.Regions.Count > 0; + + TransferNodes(input.points); + + // Read and reconstruct a mesh. + hullsize = DataReader.Reconstruct(this, input, triangles.ToArray()); + + // Calculate the number of edges. + edges = (3 * triangles.Count + hullsize) / 2; + } + */ + + /// + /// Triangulate given input data. + /// + /// + internal void ApplyConstraints(IPolygon input, ConstraintOptions options, QualityOptions quality) + { + behavior.Poly = input.Segments.Count > 0; + + // Copy constraint options + if (options != null) + { + behavior.ConformingDelaunay = options.ConformingDelaunay; + behavior.Convex = options.Convex; + behavior.NoBisect = options.SegmentSplitting; + + if (behavior.ConformingDelaunay) + { + behavior.Quality = true; + } + } + + // Copy quality options + if (quality != null) + { + behavior.MinAngle = quality.MinimumAngle; + behavior.MaxAngle = quality.MaximumAngle; + behavior.MaxArea = quality.MaximumArea; + behavior.UserTest = quality.UserTest; + + behavior.Quality = true; + } + + //if (input.EdgeMarkers != null) + //{ + // behavior.UseBoundaryMarkers = true; + //} + + // TODO: remove + if (!behavior.Poly) + { + // Be careful not to allocate space for element area constraints that + // will never be assigned any value (other than the default -1.0). + behavior.VarArea = false; + } + + behavior.useRegions = input.Regions.Count > 0; + + steinerleft = behavior.SteinerPoints; + + // Ensure that no vertex can be mistaken for a triangular bounding + // box vertex in insertvertex(). + infvertex1 = null; + infvertex2 = null; + infvertex3 = null; + + // Insert segments, carving holes. + var mesher = new ConstraintMesher(this); + + if (behavior.useSegments) + { + // Segments will be introduced next. + checksegments = true; + + // Insert PSLG segments and/or convex hull segments. + mesher.FormSkeleton(input); + } + + if (behavior.Poly && (triangles.Count > 0)) + { + // Copy holes + foreach (var item in input.Holes) + { + holes.Add(item); + } + + // Copy regions + foreach (var item in input.Regions) + { + regions.Add(item); + } + + // Carve out holes and concavities. + mesher.CarveHoles(); + } + + if (behavior.Quality && triangles.Count > 0) + { + // Enforce angle and area constraints. + qualityMesher.EnforceQuality(); + } + + // Calculate the number of edges. + edges = (3 * triangles.Count + hullsize) / 2; + } + + /// + /// Refines the current mesh. + /// + internal void Refine() + { + inelements = triangles.Count; + invertices = vertices.Count; + + // TODO: Set all vertex types to input (i.e. NOT free)? + + if (behavior.Poly) + { + insegments = behavior.useSegments ? subsegs.Count : hullsize; + } + + Reset(); + + steinerleft = behavior.SteinerPoints; + + // Ensure that no vertex can be mistaken for a triangular bounding + // box vertex in insertvertex(). + infvertex1 = infvertex2 = infvertex3 = null; + + if (behavior.useSegments) + { + checksegments = true; + } + + if (triangles.Count > 0) + { + // Enforce angle and area constraints. + qualityMesher.EnforceQuality(); + } + + // Calculate the number of edges. + edges = (3 * triangles.Count + hullsize) / 2; + } + + internal void CopyTo(Mesh target) + { + target.vertices = this.vertices; + target.triangles = this.triangles; + target.subsegs = this.subsegs; + + target.holes = this.holes; + target.regions = this.regions; + + target.hash_vtx = this.hash_vtx; + target.hash_seg = this.hash_seg; + target.hash_tri = this.hash_tri; + + target.numbering = this.numbering; + target.hullsize = this.hullsize; + target.edges = this.edges; } /// @@ -625,12 +594,11 @@ namespace TriangleNet /// Read the vertices from memory. /// /// The input data. - internal void TransferNodes(InputGeometry data) + internal void TransferNodes(ICollection points) { - List points = data.points; - this.invertices = points.Count; this.mesh_dim = 2; + this.bounds = new Rectangle(); if (this.invertices < 3) { @@ -638,17 +606,21 @@ namespace TriangleNet throw new Exception("Input must have at least three input vertices."); } - this.nextras = points[0].attributes == null ? 0 : points[0].attributes.Length; + bool first = true; - foreach (Vertex vertex in points) + foreach (Vertex p in points) { - vertex.hash = this.hash_vtx++; - vertex.id = vertex.hash; + if (first) + { + this.nextras = p.attributes == null ? 0 : p.attributes.Length; + first = false; + } - this.vertices.Add(vertex.hash, vertex); + p.hash = p.id = this.hash_vtx++; + + this.vertices.Add(p.hash, p); + this.bounds.Expand(p); } - - this.bounds = data.Bounds; } /// @@ -862,7 +834,7 @@ namespace TriangleNet encroached.org = brokensubseg.Org(); encroached.dest = brokensubseg.Dest(); - quality.AddBadSubseg(encroached); + qualityMesher.AddBadSubseg(encroached); } } // Return a handle whose primary edge contains the vertex, @@ -1100,7 +1072,7 @@ namespace TriangleNet if (segmentflaws) { // Does the new vertex encroach upon this subsegment? - if (quality.CheckSeg4Encroach(ref checksubseg) > 0) + if (qualityMesher.CheckSeg4Encroach(ref checksubseg) > 0) { success = InsertVertexResult.Encroaching; } @@ -1266,7 +1238,7 @@ namespace TriangleNet if (triflaws) { // Check the triangle 'horiz' for quality. - quality.TestTriangle(ref horiz); + qualityMesher.TestTriangle(ref horiz); } // Look for the next edge around the newly inserted vertex. @@ -1338,12 +1310,9 @@ namespace TriangleNet oppotri.SegBond(ref newsubseg); newsubseg.seg.boundary = subsegmark; } - else + else if (newsubseg.seg.boundary == 0) { - if (newsubseg.seg.boundary == 0) - { - newsubseg.seg.boundary = subsegmark; - } + newsubseg.seg.boundary = subsegmark; } } @@ -1718,7 +1687,7 @@ namespace TriangleNet { // Check the quality of the newly committed triangle. besttri.Sym(ref testtri); - quality.TestTriangle(ref testtri); + qualityMesher.TestTriangle(ref testtri); } } // Return the base triangle. @@ -1795,7 +1764,7 @@ namespace TriangleNet deltri.SetOrg(neworg); if (behavior.NoBisect == 0) { - quality.TestTriangle(ref deltri); + qualityMesher.TestTriangle(ref deltri); } // Delete the two spliced-out triangles. diff --git a/Triangle.NET/Triangle/Meshing/Algorithm/Dwyer.cs b/Triangle.NET/Triangle/Meshing/Algorithm/Dwyer.cs index c481a41..424e6ce 100644 --- a/Triangle.NET/Triangle/Meshing/Algorithm/Dwyer.cs +++ b/Triangle.NET/Triangle/Meshing/Algorithm/Dwyer.cs @@ -8,9 +8,9 @@ namespace TriangleNet.Meshing.Algorithm { using System; + using System.Collections.Generic; using TriangleNet.Data; using TriangleNet.Geometry; - using TriangleNet.Logging; /// /// Builds a delaunay triangulation using the divide-and-conquer algorithm. @@ -43,7 +43,7 @@ namespace TriangleNet.Meshing.Algorithm /// The bounding box also makes it easy to traverse the convex hull, as the /// divide-and-conquer algorithm needs to do. /// - class Dwyer + public class Dwyer : ITriangulator { static Random rand = new Random(DateTime.Now.Millisecond); @@ -52,6 +52,83 @@ namespace TriangleNet.Meshing.Algorithm Vertex[] sortarray; Mesh mesh; + /// + /// Form a Delaunay triangulation by the divide-and-conquer method. + /// + /// + /// + /// Sorts the vertices, calls a recursive procedure to triangulate them, and + /// removes the bounding box, setting boundary markers as appropriate. + /// + public IMesh Triangulate(ICollection points) + { + this.mesh = new Mesh(); + this.mesh.TransferNodes(points); + + Otri hullleft = default(Otri), hullright = default(Otri); + int divider; + int i, j, n = points.Count; + + //DebugWriter.Session.Start("test-dbg"); + + // Allocate an array of pointers to vertices for sorting. + // TODO: use ToArray + this.sortarray = new Vertex[n]; + i = 0; + foreach (var v in points) + { + sortarray[i++] = v; + } + // Sort the vertices. + //Array.Sort(sortarray); + 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)) + { + if (Log.Verbose) + { + Log.Instance.Warning( + String.Format("A duplicate vertex appeared and was ignored (ID {0}).", sortarray[j].hash), + "Dwyer.Triangulate()"); + } + 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; + } + /// /// Sort an array of vertices by x-coordinate, using the y-coordinate as a secondary key. /// @@ -103,16 +180,14 @@ namespace TriangleNet.Meshing.Algorithm left++; } while ((left <= right) && ((sortarray[left].x < pivotx) || - ((sortarray[left].x == pivotx) && - (sortarray[left].y < pivoty)))); + ((sortarray[left].x == pivotx) && (sortarray[left].y < pivoty)))); // Search for a vertex whose x-coordinate is too small for the right. do { right--; } while ((left <= right) && ((sortarray[right].x > pivotx) || - ((sortarray[right].x == pivotx) && - (sortarray[right].y > pivoty)))); + ((sortarray[right].x == pivotx) && (sortarray[right].y > pivoty)))); if (left < right) { @@ -183,16 +258,14 @@ namespace TriangleNet.Meshing.Algorithm left++; } while ((left <= right) && ((sortarray[left][axis] < pivot1) || - ((sortarray[left][axis] == pivot1) && - (sortarray[left][1 - axis] < pivot2)))); + ((sortarray[left][axis] == pivot1) && (sortarray[left][1 - axis] < pivot2)))); // Search for a vertex whose x-coordinate is too small for the right. do { right--; } while ((left <= right) && ((sortarray[right][axis] > pivot1) || - ((sortarray[right][axis] == pivot1) && - (sortarray[right][1 - axis] > pivot2)))); + ((sortarray[right][axis] == pivot1) && (sortarray[right][1 - axis] > pivot2)))); if (left < right) { // Swap the left and right vertices. @@ -823,80 +896,5 @@ namespace TriangleNet.Meshing.Algorithm return hullsize; } - - /// - /// Form a Delaunay triangulation by the divide-and-conquer method. - /// - /// - /// - /// Sorts the vertices, calls a recursive procedure to triangulate them, and - /// removes the bounding box, setting boundary markers as appropriate. - /// - public int Triangulate(Mesh m) - { - Otri hullleft = default(Otri), hullright = default(Otri); - int divider; - int i, j; - - this.mesh = m; - - //DebugWriter.Session.Start("test-dbg"); - - // Allocate an array of pointers to vertices for sorting. - // TODO: use ToArray - this.sortarray = new Vertex[m.invertices]; - i = 0; - foreach (var v in m.vertices.Values) - { - sortarray[i++] = v; - } - // Sort the vertices. - //Array.Sort(sortarray); - VertexSort(0, m.invertices - 1); - // Discard duplicate vertices, which can really mess up the algorithm. - i = 0; - for (j = 1; j < m.invertices; j++) - { - if ((sortarray[i].x == sortarray[j].x) - && (sortarray[i].y == sortarray[j].y)) - { - if (Log.Verbose) - { - Log.Instance.Warning( - String.Format("A duplicate vertex appeared and was ignored (ID {0}).", sortarray[j].hash), - "DivConquer.DivconqDelaunay()"); - } - sortarray[j].type = VertexType.UndeadVertex; - m.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(); - - return RemoveGhosts(ref hullleft); - } } } diff --git a/Triangle.NET/Triangle/Meshing/Algorithm/Incremental.cs b/Triangle.NET/Triangle/Meshing/Algorithm/Incremental.cs index b7e08e2..fc06e39 100644 --- a/Triangle.NET/Triangle/Meshing/Algorithm/Incremental.cs +++ b/Triangle.NET/Triangle/Meshing/Algorithm/Incremental.cs @@ -7,17 +7,54 @@ namespace TriangleNet.Meshing.Algorithm { + using System.Collections.Generic; using TriangleNet.Data; - using TriangleNet.Logging; using TriangleNet.Geometry; /// /// Builds a delaunay triangulation using the incremental algorithm. /// - class Incremental + public class Incremental : ITriangulator { Mesh mesh; + /// + /// Form a Delaunay triangulation by incrementally inserting vertices. + /// + /// Returns the number of edges on the convex hull of the + /// triangulation. + public IMesh Triangulate(ICollection points) + { + this.mesh = new Mesh(); + this.mesh.TransferNodes(points); + + Otri starttri = new Otri(); + + // Create a triangular bounding box. + GetBoundingBox(); + + foreach (var v in mesh.vertices.Values) + { + starttri.triangle = Mesh.dummytri; + Osub tmp = default(Osub); + if (mesh.InsertVertex(v, ref starttri, ref tmp, false, false) == InsertVertexResult.Duplicate) + { + if (Log.Verbose) + { + Log.Instance.Warning("A duplicate vertex appeared and was ignored.", + "Incremental.IncrementalDelaunay()"); + } + v.type = VertexType.UndeadVertex; + mesh.undeads++; + } + } + + // Remove the bounding box. + this.mesh.hullsize = RemoveBox(); + + return this.mesh; + } + /// /// Form an "infinite" bounding triangle to insert vertices into. /// @@ -144,38 +181,5 @@ namespace TriangleNet.Meshing.Algorithm return hullsize; } - - /// - /// Form a Delaunay triangulation by incrementally inserting vertices. - /// - /// Returns the number of edges on the convex hull of the - /// triangulation. - public int Triangulate(Mesh mesh) - { - this.mesh = mesh; - - Otri starttri = new Otri(); - - // Create a triangular bounding box. - GetBoundingBox(); - - foreach (var v in mesh.vertices.Values) - { - starttri.triangle = Mesh.dummytri; - Osub tmp = default(Osub); - if (mesh.InsertVertex(v, ref starttri, ref tmp, false, false) == InsertVertexResult.Duplicate) - { - if (Log.Verbose) - { - Log.Instance.Warning("A duplicate vertex appeared and was ignored.", - "Incremental.IncrementalDelaunay()"); - } - v.type = VertexType.UndeadVertex; - mesh.undeads++; - } - } - // Remove the bounding box. - return RemoveBox(); - } } } diff --git a/Triangle.NET/Triangle/Meshing/Algorithm/SweepLine.cs b/Triangle.NET/Triangle/Meshing/Algorithm/SweepLine.cs index 48535c5..59529bb 100644 --- a/Triangle.NET/Triangle/Meshing/Algorithm/SweepLine.cs +++ b/Triangle.NET/Triangle/Meshing/Algorithm/SweepLine.cs @@ -11,18 +11,17 @@ namespace TriangleNet.Meshing.Algorithm using System.Collections.Generic; using TriangleNet.Data; using TriangleNet.Geometry; - using TriangleNet.Logging; using TriangleNet.Tools; /// /// Builds a delaunay triangulation using the sweepline algorithm. /// - class SweepLine + public class SweepLine : ITriangulator { static int randomseed = 1; static int SAMPLERATE = 10; - int randomnation(int choices) + static int randomnation(int choices) { randomseed = (randomseed * 1366 + 150889) % 714025; return randomseed / (714025 / choices + 1); @@ -32,6 +31,226 @@ namespace TriangleNet.Meshing.Algorithm double xminextreme; // Nonexistent x value used as a flag in sweepline. List splaynodes; + public IMesh Triangulate(ICollection points) + { + this.mesh = new Mesh(); + this.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(); + 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.LnextSelf(); + righttri.LprevSelf(); + lefttri.Bond(ref righttri); + lefttri.LnextSelf(); + righttri.LprevSelf(); + lefttri.Bond(ref righttri); + firstvertex = eventheap[0].vertexEvent; + + HeapDelete(eventheap, heapsize, 0); + heapsize--; + do + { + if (heapsize == 0) + { + Log.Instance.Error("Input vertices are all identical.", "SweepLine.Triangulate()"); + 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)) + { + if (Log.Verbose) + { + Log.Instance.Warning("A duplicate vertex appeared and was ignored (ID " + secondvertex.id + ").", + "SweepLine.Triangulate().1"); + } + 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.SymSelf(); + 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)) + { + if (Log.Verbose) + { + Log.Instance.Warning("A duplicate vertex appeared and was ignored (ID " + nextvertex.id + ").", + "SweepLine.Triangulate().2"); + } + 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.LnextSelf(); + righttri.LprevSelf(); + lefttri.Bond(ref righttri); + lefttri.LnextSelf(); + righttri.LprevSelf(); + 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.LprevSelf(); + + this.mesh.hullsize = RemoveGhosts(ref bottommost); + + return this.mesh; + } + #region Heap void HeapInsert(SweepEvent[] heap, int heapsize, SweepEvent newevent) @@ -519,222 +738,6 @@ namespace TriangleNet.Meshing.Algorithm return hullsize; } - public int Triangulate(Mesh mesh) - { - this.mesh = mesh; - - // 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(); - 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.LnextSelf(); - righttri.LprevSelf(); - lefttri.Bond(ref righttri); - lefttri.LnextSelf(); - righttri.LprevSelf(); - lefttri.Bond(ref righttri); - firstvertex = eventheap[0].vertexEvent; - - HeapDelete(eventheap, heapsize, 0); - heapsize--; - do - { - if (heapsize == 0) - { - Log.Instance.Error("Input vertices are all identical.", "SweepLine.Triangulate()"); - 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)) - { - if (Log.Verbose) - { - Log.Instance.Warning("A duplicate vertex appeared and was ignored (ID " + secondvertex.id + ").", - "SweepLine.Triangulate().1"); - } - 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.SymSelf(); - 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)) - { - if (Log.Verbose) - { - Log.Instance.Warning("A duplicate vertex appeared and was ignored (ID " + nextvertex.id + ").", - "SweepLine.Triangulate().2"); - } - 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.LnextSelf(); - righttri.LprevSelf(); - lefttri.Bond(ref righttri); - lefttri.LnextSelf(); - righttri.LprevSelf(); - 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.LprevSelf(); - return RemoveGhosts(ref bottommost); - } - #region Internal classes /// diff --git a/Triangle.NET/Triangle/Meshing/ConstraintMesher.cs b/Triangle.NET/Triangle/Meshing/ConstraintMesher.cs index c7c15f0..643993f 100644 --- a/Triangle.NET/Triangle/Meshing/ConstraintMesher.cs +++ b/Triangle.NET/Triangle/Meshing/ConstraintMesher.cs @@ -12,7 +12,7 @@ namespace TriangleNet.Meshing using TriangleNet.Data; using TriangleNet.Geometry; using TriangleNet.Logging; - using TriangleNet.Tools; + using TriangleNet.Meshing.Iterators; internal class ConstraintMesher { @@ -165,7 +165,7 @@ namespace TriangleNet.Meshing /// Create the segments of a triangulation, including PSLG segments and edges /// on the convex hull. /// - public void FormSkeleton(InputGeometry input) + public void FormSkeleton(IPolygon input) { Vertex endpoint1, endpoint2; int end1, end2; @@ -184,7 +184,7 @@ namespace TriangleNet.Meshing // If segments are to be inserted, compute a mapping // from vertices to triangles. - if (input.HasSegments) + if (input.Segments.Count > 0) { mesh.MakeVertexMap(); } @@ -192,7 +192,7 @@ namespace TriangleNet.Meshing boundmarker = 0; // Read and insert the segments. - foreach (var seg in input.segments) + foreach (var seg in input.Segments) { mesh.insegments++; diff --git a/Triangle.NET/Triangle/Meshing/Converter.cs b/Triangle.NET/Triangle/Meshing/Converter.cs index d9cb0f1..452b9f4 100644 --- a/Triangle.NET/Triangle/Meshing/Converter.cs +++ b/Triangle.NET/Triangle/Meshing/Converter.cs @@ -12,14 +12,13 @@ namespace TriangleNet.Meshing using System.Linq; using TriangleNet.Data; using TriangleNet.Geometry; - using TriangleNet.Logging; /// /// The DataReader class provides methods for mesh reconstruction. /// public class Converter { - public Mesh ToMesh(InputGeometry polygon, IList triangles) + public Mesh ToMesh(Polygon polygon, IList triangles) { return ToMesh(polygon, triangles.ToArray()); } @@ -46,7 +45,7 @@ namespace TriangleNet.Meshing /// the corresponding pointer is adjusted to refer to a subsegment rather /// than the next triangle of the stack. /// - public Mesh ToMesh(InputGeometry polygon, ITriangle[] triangles) + public Mesh ToMesh(Polygon polygon, ITriangle[] triangles) { Otri tri = default(Otri); Osub subseg = default(Osub); @@ -57,7 +56,7 @@ namespace TriangleNet.Meshing var mesh = new Mesh(); - mesh.TransferNodes(polygon); + mesh.TransferNodes(polygon.Points); mesh.inelements = elements; mesh.regions.AddRange(polygon.Regions); @@ -213,7 +212,7 @@ namespace TriangleNet.Meshing return vertexarray; } - private static void SetSegments(Mesh mesh, InputGeometry polygon, List[] vertexarray) + private static void SetSegments(Mesh mesh, Polygon polygon, List[] vertexarray) { Otri checktri = default(Otri); Otri nexttri; // Triangle @@ -242,9 +241,9 @@ namespace TriangleNet.Meshing { subseg.seg = item; - end[0] = polygon.segments[i].P0; - end[1] = polygon.segments[i].P1; - boundmarker = polygon.segments[i].Boundary; + end[0] = polygon.Segments[i].P0; + end[1] = polygon.Segments[i].P1; + boundmarker = polygon.Segments[i].Boundary; for (int j = 0; j < 2; j++) { diff --git a/Triangle.NET/Triangle/Meshing/GenericMesher.cs b/Triangle.NET/Triangle/Meshing/GenericMesher.cs new file mode 100644 index 0000000..b849eb0 --- /dev/null +++ b/Triangle.NET/Triangle/Meshing/GenericMesher.cs @@ -0,0 +1,170 @@ + +namespace TriangleNet.Meshing +{ + using System.Collections.Generic; + using TriangleNet.Geometry; + using TriangleNet.IO; + using TriangleNet.Meshing.Algorithm; + + public class GenericMesher : ITriangulator, IConstraintMesher, IQualityMesher + { + ITriangulator triangulator; + + public GenericMesher() + : this(new Dwyer()) + { + } + + public GenericMesher(ITriangulator triangulator) + { + this.triangulator = triangulator; + } + + /// + /// Triangulates a point set. + /// + /// Collection of points. + /// Mesh + public IMesh Triangulate(ICollection points) + { + return triangulator.Triangulate(points); + } + + /// + /// Triangulates a polygon. + /// + /// The polygon. + /// Mesh + public IMesh Triangulate(IPolygon polygon) + { + return Triangulate(polygon, null, null); + } + + /// + /// Triangulates a polygon, applying constraint options. + /// + /// The polygon. + /// Constraint options. + /// Mesh + public IMesh Triangulate(IPolygon polygon, ConstraintOptions options) + { + return Triangulate(polygon, options, null); + } + + /// + /// Triangulates a polygon, applying quality options. + /// + /// The polygon. + /// Quality options. + /// Mesh + public IMesh Triangulate(IPolygon polygon, QualityOptions quality) + { + return Triangulate(polygon, null, quality); + } + + /// + /// Triangulates a polygon, applying quality and constraint options. + /// + /// The polygon. + /// Constraint options. + /// Quality options. + /// Mesh + public IMesh Triangulate(IPolygon polygon, ConstraintOptions options, QualityOptions quality) + { + var mesh = (Mesh)triangulator.Triangulate(polygon.Points); + + mesh.ApplyConstraints(polygon, options, quality); + + return mesh; + } + + /// + /// Generates a structured mesh. + /// + /// Bounds of the mesh. + /// Number of segments in x direction. + /// Number of segments in y direction. + /// Mesh + public IMesh StructurdMesh(Rectangle bounds, int nx, int ny) + { + var polygon = new Polygon((nx + 1) * (ny + 1)); + + double x, y, dx, dy, left, bottom; + + dx = bounds.Width / nx; + dy = bounds.Height / ny; + + left = bounds.Left; + bottom = bounds.Bottom; + + int i, j, k, l, n; + + // Add vertices. + var points = polygon.Points; + + for (i = 0; i <= nx; i++) + { + x = left + i * dx; + + for (j = 0; j <= ny; j++) + { + y = bottom + j * dy; + + points.Add(new Vertex(x, y)); + } + } + + n = 0; + + // Set vertex id and hash. + foreach (var v in points) + { + v.id = v.hash = n++; + } + + // Add boundary segments. + var segments = polygon.Segments; + + segments.Capacity = 2 * (nx + ny); + + for (j = 0; j < ny; j++) + { + // Left + segments.Add(new Edge(j, j + 1)); + + // Right + segments.Add(new Edge(nx * (ny + 1) + j, nx * (ny + 1) + (j + 1))); + } + + for (i = 0; i < nx; i++) + { + // Bottom + segments.Add(new Edge(i * (ny + 1), (i + 1) * (ny + 1))); + + // Top + segments.Add(new Edge(i * (ny + 1) + nx, (i + 1) * (ny + 1) + nx)); + } + + // Add triangles. + var triangles = new InputTriangle[2 * nx * ny]; + + n = 0; + + for (i = 0; i < nx; i++) + { + for (j = 0; j < ny; j++) + { + k = j + (ny + 1) * i; + l = j + (ny + 1) * (i + 1); + + triangles[n++] = new InputTriangle(k, l, l + 1); + triangles[n++] = new InputTriangle(k, l + 1, k + 1); + } + } + + var converter = new Converter(); + + return converter.ToMesh(polygon, triangles); + } + } +} diff --git a/Triangle.NET/Triangle/Meshing/IConstraintMesher.cs b/Triangle.NET/Triangle/Meshing/IConstraintMesher.cs index a7190da..c5fabde 100644 --- a/Triangle.NET/Triangle/Meshing/IConstraintMesher.cs +++ b/Triangle.NET/Triangle/Meshing/IConstraintMesher.cs @@ -5,7 +5,7 @@ namespace TriangleNet.Meshing public interface IConstraintMesher { - Mesh Triangulate(IPolygon polygon); - Mesh Triangulate(IPolygon polygon, ConstraintOptions options); + IMesh Triangulate(IPolygon polygon); + IMesh Triangulate(IPolygon polygon, ConstraintOptions options); } } diff --git a/Triangle.NET/Triangle/Meshing/IMesh.cs b/Triangle.NET/Triangle/Meshing/IMesh.cs new file mode 100644 index 0000000..f72260b --- /dev/null +++ b/Triangle.NET/Triangle/Meshing/IMesh.cs @@ -0,0 +1,20 @@ + +namespace TriangleNet.Meshing +{ + using System.Collections.Generic; + using TriangleNet.Data; + using TriangleNet.Geometry; + + public interface IMesh + { + ICollection Vertices { get; } + IEnumerable Edges { get; } + ICollection Segments { get; } + ICollection Triangles { get; } + IList Holes { get; } + + Rectangle Bounds { get; } + + void Refine(QualityOptions quality); + } +} diff --git a/Triangle.NET/Triangle/Meshing/IQualityMesher.cs b/Triangle.NET/Triangle/Meshing/IQualityMesher.cs index 4e8f5f9..d5f1075 100644 --- a/Triangle.NET/Triangle/Meshing/IQualityMesher.cs +++ b/Triangle.NET/Triangle/Meshing/IQualityMesher.cs @@ -5,7 +5,7 @@ namespace TriangleNet.Meshing public interface IQualityMesher { - Mesh Triangulate(IPolygon polygon, QualityOptions quality); - Mesh Triangulate(IPolygon polygon, ConstraintOptions options, QualityOptions quality); + IMesh Triangulate(IPolygon polygon, QualityOptions quality); + IMesh Triangulate(IPolygon polygon, ConstraintOptions options, QualityOptions quality); } } diff --git a/Triangle.NET/Triangle/Meshing/ITriangulator.cs b/Triangle.NET/Triangle/Meshing/ITriangulator.cs index c66aa35..8b55018 100644 --- a/Triangle.NET/Triangle/Meshing/ITriangulator.cs +++ b/Triangle.NET/Triangle/Meshing/ITriangulator.cs @@ -14,6 +14,6 @@ namespace TriangleNet.Meshing /// public interface ITriangulator { - Mesh Triangulate(ICollection points); + IMesh Triangulate(ICollection points); } } diff --git a/Triangle.NET/Triangle/Geometry/EdgeEnumerator.cs b/Triangle.NET/Triangle/Meshing/Iterators/EdgeIterator.cs similarity index 86% rename from Triangle.NET/Triangle/Geometry/EdgeEnumerator.cs rename to Triangle.NET/Triangle/Meshing/Iterators/EdgeIterator.cs index 81ba7fc..28d42cc 100644 --- a/Triangle.NET/Triangle/Geometry/EdgeEnumerator.cs +++ b/Triangle.NET/Triangle/Meshing/Iterators/EdgeIterator.cs @@ -4,18 +4,16 @@ // // ----------------------------------------------------------------------- -namespace TriangleNet.Geometry +namespace TriangleNet.Meshing.Iterators { - using System; using System.Collections.Generic; - using System.Linq; - using System.Text; using TriangleNet.Data; + using TriangleNet.Geometry; /// /// Enumerates the edges of a triangulation. /// - public class EdgeEnumerator : IEnumerator + public class EdgeIterator : IEnumerator { IEnumerator triangles; Otri tri = default(Otri); @@ -25,9 +23,9 @@ namespace TriangleNet.Geometry Vertex p1, p2; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public EdgeEnumerator(Mesh mesh) + public EdgeIterator(Mesh mesh) { triangles = mesh.triangles.Values.GetEnumerator(); triangles.MoveNext(); diff --git a/Triangle.NET/Triangle/Tools/RegionIterator.cs b/Triangle.NET/Triangle/Meshing/Iterators/RegionIterator.cs similarity index 95% rename from Triangle.NET/Triangle/Tools/RegionIterator.cs rename to Triangle.NET/Triangle/Meshing/Iterators/RegionIterator.cs index 6bd3b5e..02e1be5 100644 --- a/Triangle.NET/Triangle/Tools/RegionIterator.cs +++ b/Triangle.NET/Triangle/Meshing/Iterators/RegionIterator.cs @@ -5,12 +5,10 @@ // // ----------------------------------------------------------------------- -namespace TriangleNet.Tools +namespace TriangleNet.Meshing.Iterators { using System; using System.Collections.Generic; - using System.Linq; - using System.Text; using TriangleNet.Data; /// diff --git a/Triangle.NET/Triangle/Smoothing/ISmoother.cs b/Triangle.NET/Triangle/Smoothing/ISmoother.cs index 6bfc264..621a2c5 100644 --- a/Triangle.NET/Triangle/Smoothing/ISmoother.cs +++ b/Triangle.NET/Triangle/Smoothing/ISmoother.cs @@ -6,16 +6,14 @@ namespace TriangleNet.Smoothing { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; + using TriangleNet.Meshing; /// /// Interface for mesh smoothers. /// public interface ISmoother { - void Smooth(); + void Smooth(IMesh mesh); + void Smooth(IMesh mesh, int limit); } } \ No newline at end of file diff --git a/Triangle.NET/Triangle/Smoothing/SimpleSmoother.cs b/Triangle.NET/Triangle/Smoothing/SimpleSmoother.cs index 8f82731..ca13b74 100644 --- a/Triangle.NET/Triangle/Smoothing/SimpleSmoother.cs +++ b/Triangle.NET/Triangle/Smoothing/SimpleSmoother.cs @@ -6,11 +6,8 @@ namespace TriangleNet.Smoothing { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using TriangleNet.Geometry; + using TriangleNet.Meshing; using TriangleNet.Tools; /// @@ -22,35 +19,42 @@ namespace TriangleNet.Smoothing /// public class SimpleSmoother : ISmoother { - Mesh mesh; + ConstraintOptions options; - public SimpleSmoother(Mesh mesh) + public SimpleSmoother() { - this.mesh = mesh; + this.options = new ConstraintOptions() { ConformingDelaunay = true }; } - public void Smooth() + public void Smooth(IMesh mesh) { - mesh.behavior.Quality = false; + Smooth(mesh, 10); + } + + public void Smooth(IMesh mesh, int limit) + { + var smoothedMesh = (Mesh)mesh; // Take a few smoothing rounds. - for (int i = 0; i < 5; i++) + for (int i = 0; i < limit; i++) { - Step(); + Step(smoothedMesh); // Actually, we only want to rebuild, if mesh is no longer // Delaunay. Flipping edges could be the right choice instead // of re-triangulating... - mesh.Triangulate(Rebuild()); + smoothedMesh = (Mesh)Rebuild(smoothedMesh).Triangulate(options); } + + smoothedMesh.CopyTo((Mesh)mesh); } /// /// Smooth all free nodes. /// - private void Step() + private void Step(Mesh mesh) { - BoundedVoronoi voronoi = new BoundedVoronoi(this.mesh, false); + BoundedVoronoi voronoi = new BoundedVoronoi(mesh, false); var cells = voronoi.Regions; @@ -76,31 +80,24 @@ namespace TriangleNet.Smoothing /// /// Rebuild the input geometry. /// - private InputGeometry Rebuild() + private Polygon Rebuild(Mesh mesh) { - InputGeometry geometry = new InputGeometry(mesh.vertices.Count); + var data = new Polygon(mesh.vertices.Count); - foreach (var vertex in mesh.vertices.Values) + foreach (var v in mesh.vertices.Values) { - geometry.AddPoint(vertex.x, vertex.y, vertex.mark); + // Reset to input vertex. + v.type = VertexType.InputVertex; + + data.Points.Add(v); } - foreach (var segment in mesh.subsegs.Values) - { - geometry.AddSegment(segment.P0, segment.P1, segment.Boundary); - } + data.Segments.AddRange(mesh.subsegs.Values); - foreach (var hole in mesh.holes) - { - geometry.AddHole(hole.x, hole.y); - } + data.Holes.AddRange(mesh.holes); + data.Regions.AddRange(mesh.regions); - foreach (var region in mesh.regions) - { - geometry.AddRegion(region.point.x, region.point.y, region.id); - } - - return geometry; + return data; } } } diff --git a/Triangle.NET/Triangle/Tools/BoundedVoronoi.cs b/Triangle.NET/Triangle/Tools/BoundedVoronoi.cs index 526a81c..efbe1ea 100644 --- a/Triangle.NET/Triangle/Tools/BoundedVoronoi.cs +++ b/Triangle.NET/Triangle/Tools/BoundedVoronoi.cs @@ -8,8 +8,6 @@ namespace TriangleNet.Tools { using System; using System.Collections.Generic; - using System.Linq; - using System.Text; using TriangleNet.Data; using TriangleNet.Geometry; @@ -285,7 +283,7 @@ namespace TriangleNet.Tools if (f_init.Org() != vertex) { - throw new Exception("ConstructBvdCell: inconsistent topology."); + throw new Exception("ConstructCell: inconsistent topology."); } // Let f be initialized to f_init @@ -399,7 +397,7 @@ namespace TriangleNet.Tools if (f_init.Org() != vertex) { - throw new Exception("ConstructBoundaryBvdCell: inconsistent topology."); + throw new Exception("ConstructBoundaryCell: inconsistent topology."); } // Let f be initialized to f_init f_init.Copy(ref f); diff --git a/Triangle.NET/Triangle/Tools/QuadTree.cs b/Triangle.NET/Triangle/Tools/QuadTree.cs index 3052233..3e1c7f6 100644 --- a/Triangle.NET/Triangle/Tools/QuadTree.cs +++ b/Triangle.NET/Triangle/Tools/QuadTree.cs @@ -108,6 +108,7 @@ namespace TriangleNet.Tools // Point is inside or on the edge of this triangle. return true; } + return false; } @@ -115,315 +116,314 @@ namespace TriangleNet.Tools { return p.X * q.X + p.Y * q.Y; } - } - #region QuadNode class - - /// - /// A node of the quadtree. - /// - class QuadNode - { - const int SW = 0; - const int SE = 1; - const int NW = 2; - const int NE = 3; - - const double EPS = 1e-6; - - static readonly byte[] BITVECTOR = { 0x1, 0x2, 0x4, 0x8 }; - - Rectangle bounds; - Point pivot; - QuadTree tree; - QuadNode[] regions; - List triangles; - - byte bitRegions; - - public QuadNode(Rectangle box, QuadTree tree) - : this(box, tree, false) + /// + /// A node of the quadtree. + /// + class QuadNode { - } + const int SW = 0; + const int SE = 1; + const int NW = 2; + const int NE = 3; - public QuadNode(Rectangle box, QuadTree tree, bool init) - { - this.tree = tree; + const double EPS = 1e-6; - this.bounds = new Rectangle(box.Left, box.Bottom, box.Right, box.Top); - this.pivot = new Point((box.Left + box.Right) / 2, (box.Bottom + box.Top) / 2); + static readonly byte[] BITVECTOR = { 0x1, 0x2, 0x4, 0x8 }; - this.bitRegions = 0; + Rectangle bounds; + Point pivot; + QuadTree tree; + QuadNode[] regions; + List triangles; - this.regions = new QuadNode[4]; - this.triangles = new List(); + byte bitRegions; - if (init) + public QuadNode(Rectangle box, QuadTree tree) + : this(box, tree, false) { - int count = tree.triangles.Length; + } - // Allocate memory upfront - triangles.Capacity = count; + public QuadNode(Rectangle box, QuadTree tree, bool init) + { + this.tree = tree; - for (int i = 0; i < count; i++) + this.bounds = new Rectangle(box.Left, box.Bottom, box.Width, box.Height); + this.pivot = new Point((box.Left + box.Right) / 2, (box.Bottom + box.Top) / 2); + + this.bitRegions = 0; + + this.regions = new QuadNode[4]; + this.triangles = new List(); + + if (init) { - triangles.Add(i); + int count = tree.triangles.Length; + + // Allocate memory upfront + triangles.Capacity = count; + + for (int i = 0; i < count; i++) + { + triangles.Add(i); + } } } - } - public List FindTriangles(Point searchPoint) - { - int region = FindRegion(searchPoint); - if (regions[region] == null) + public List FindTriangles(Point searchPoint) { - return triangles; - } - return regions[region].FindTriangles(searchPoint); - } - - public void CreateSubRegion(int currentDepth) - { - // The four sub regions of the quad tree - // +--------------+ - // | nw | ne | - // |------+pivot--| - // | sw | se | - // +--------------+ - Rectangle box; - - // 1. region south west - box = new Rectangle(bounds.Left, bounds.Bottom, pivot.X, pivot.Y); - regions[0] = new QuadNode(box, tree); - - // 2. region south east - box = new Rectangle(pivot.X, bounds.Bottom, bounds.Right, pivot.Y); - regions[1] = new QuadNode(box, tree); - - // 3. region north west - box = new Rectangle(bounds.Left, pivot.Y, pivot.X, bounds.Top); - regions[2] = new QuadNode(box, tree); - - // 4. region north east - box = new Rectangle(pivot.X, pivot.Y, bounds.Right, bounds.Top); - regions[3] = new QuadNode(box, tree); - - Point[] triangle = new Point[3]; - - // Find region for every triangle vertex - foreach (var index in triangles) - { - ITriangle tri = tree.triangles[index]; - - triangle[0] = tri.GetVertex(0); - triangle[1] = tri.GetVertex(1); - triangle[2] = tri.GetVertex(2); - - AddTriangleToRegion(triangle, index); - } - - for (int i = 0; i < 4; i++) - { - if (regions[i].triangles.Count > tree.sizeBound && currentDepth < tree.maxDepth) + int region = FindRegion(searchPoint); + if (regions[region] == null) { - regions[i].CreateSubRegion(currentDepth + 1); + return triangles; + } + return regions[region].FindTriangles(searchPoint); + } + + public void CreateSubRegion(int currentDepth) + { + // The four sub regions of the quad tree + // +--------------+ + // | nw | ne | + // |------+pivot--| + // | sw | se | + // +--------------+ + Rectangle box; + + var width = bounds.Right - pivot.X; + var height = bounds.Top - pivot.Y; + + // 1. region south west + box = new Rectangle(bounds.Left, bounds.Bottom, width, height); + regions[0] = new QuadNode(box, tree); + + // 2. region south east + box = new Rectangle(pivot.X, bounds.Bottom, width, height); + regions[1] = new QuadNode(box, tree); + + // 3. region north west + box = new Rectangle(bounds.Left, pivot.Y, width, height); + regions[2] = new QuadNode(box, tree); + + // 4. region north east + box = new Rectangle(pivot.X, pivot.Y, width, height); + regions[3] = new QuadNode(box, tree); + + Point[] triangle = new Point[3]; + + // Find region for every triangle vertex + foreach (var index in triangles) + { + ITriangle tri = tree.triangles[index]; + + triangle[0] = tri.GetVertex(0); + triangle[1] = tri.GetVertex(1); + triangle[2] = tri.GetVertex(2); + + AddTriangleToRegion(triangle, index); + } + + for (int i = 0; i < 4; i++) + { + if (regions[i].triangles.Count > tree.sizeBound && currentDepth < tree.maxDepth) + { + regions[i].CreateSubRegion(currentDepth + 1); + } } } - } - void AddTriangleToRegion(Point[] triangle, int index) - { - bitRegions = 0; - if (QuadTree.IsPointInTriangle(pivot, triangle[0], triangle[1], triangle[2])) + void AddTriangleToRegion(Point[] triangle, int index) { - AddToRegion(index, SW); - AddToRegion(index, SE); - AddToRegion(index, NW); - AddToRegion(index, NE); - return; - } - - FindTriangleIntersections(triangle, index); - - if (bitRegions == 0) - { - // we didn't find any intersection so we add this triangle to a point's region - int region = FindRegion(triangle[0]); - regions[region].triangles.Add(index); - } - } - - void FindTriangleIntersections(Point[] triangle, int index) - { - // PLEASE NOTE: - // Handling of component comparison is tightly associated with the implementation - // of the findRegion() function. That means when the point to be compared equals - // the pivot point the triangle must be put at least into region 2. - // - // Linear equations are in parametric form. - // pivot.x = triangle[0].x + t * (triangle[1].x - triangle[0].x) - // pivot.y = triangle[0].y + t * (triangle[1].y - triangle[0].y) - - int k = 2; - - double dx, dy; - // Iterate through all triangle laterals and find bounding box intersections - for (int i = 0; i < 3; k = i++) - { - dx = triangle[i].X - triangle[k].X; - dy = triangle[i].Y - triangle[k].Y; - - if (dx != 0.0) - { - FindIntersectionsWithX(dx, dy, triangle, index, k); - } - if (dy != 0.0) - { - FindIntersectionsWithY(dx, dy, triangle, index, k); - } - } - } - - void FindIntersectionsWithX(double dx, double dy, Point[] triangle, int index, int k) - { - double t; - - // find intersection with plane x = m_pivot.dX - t = (pivot.X - triangle[k].X) / dx; - if (t < (1 + EPS) && t > -EPS) - { - // we have an intersection - double yComponent = triangle[k].Y + t * dy; - - if (yComponent < pivot.Y && yComponent >= bounds.Bottom) + bitRegions = 0; + if (QuadTree.IsPointInTriangle(pivot, triangle[0], triangle[1], triangle[2])) { AddToRegion(index, SW); AddToRegion(index, SE); - } - else if (yComponent <= bounds.Top) - { AddToRegion(index, NW); AddToRegion(index, NE); + return; + } + + FindTriangleIntersections(triangle, index); + + if (bitRegions == 0) + { + // we didn't find any intersection so we add this triangle to a point's region + int region = FindRegion(triangle[0]); + regions[region].triangles.Add(index); } } - // find intersection with plane x = m_boundingBox[0].dX - t = (bounds.Left - triangle[k].X) / dx; - if (t < (1 + EPS) && t > -EPS) + void FindTriangleIntersections(Point[] triangle, int index) { - // we have an intersection - double yComponent = triangle[k].Y + t * dy; + // PLEASE NOTE: + // Handling of component comparison is tightly associated with the implementation + // of the findRegion() function. That means when the point to be compared equals + // the pivot point the triangle must be put at least into region 2. + // + // Linear equations are in parametric form. + // pivot.x = triangle[0].x + t * (triangle[1].x - triangle[0].x) + // pivot.y = triangle[0].y + t * (triangle[1].y - triangle[0].y) - if (yComponent < pivot.Y && yComponent >= bounds.Bottom) + int k = 2; + + double dx, dy; + // Iterate through all triangle laterals and find bounding box intersections + for (int i = 0; i < 3; k = i++) { - AddToRegion(index, SW); - } - else if (yComponent <= bounds.Top) // TODO: check && yComponent >= pivot.Y - { - AddToRegion(index, NW); + dx = triangle[i].X - triangle[k].X; + dy = triangle[i].Y - triangle[k].Y; + + if (dx != 0.0) + { + FindIntersectionsWithX(dx, dy, triangle, index, k); + } + if (dy != 0.0) + { + FindIntersectionsWithY(dx, dy, triangle, index, k); + } } } - // find intersection with plane x = m_boundingBox[1].dX - t = (bounds.Right - triangle[k].X) / dx; - if (t < (1 + EPS) && t > -EPS) + void FindIntersectionsWithX(double dx, double dy, Point[] triangle, int index, int k) { - // we have an intersection - double yComponent = triangle[k].Y + t * dy; + double t; - if (yComponent < pivot.Y && yComponent >= bounds.Bottom) + // find intersection with plane x = m_pivot.dX + t = (pivot.X - triangle[k].X) / dx; + if (t < (1 + EPS) && t > -EPS) { - AddToRegion(index, SE); + // we have an intersection + double yComponent = triangle[k].Y + t * dy; + + if (yComponent < pivot.Y && yComponent >= bounds.Bottom) + { + AddToRegion(index, SW); + AddToRegion(index, SE); + } + else if (yComponent <= bounds.Top) + { + AddToRegion(index, NW); + AddToRegion(index, NE); + } } - else if (yComponent <= bounds.Top) + + // find intersection with plane x = m_boundingBox[0].dX + t = (bounds.Left - triangle[k].X) / dx; + if (t < (1 + EPS) && t > -EPS) { - AddToRegion(index, NE); + // we have an intersection + double yComponent = triangle[k].Y + t * dy; + + if (yComponent < pivot.Y && yComponent >= bounds.Bottom) + { + AddToRegion(index, SW); + } + else if (yComponent <= bounds.Top) // TODO: check && yComponent >= pivot.Y + { + AddToRegion(index, NW); + } } - } - } - void FindIntersectionsWithY(double dx, double dy, Point[] triangle, int index, int k) - { - double t, xComponent; - - // find intersection with plane y = m_pivot.dY - t = (pivot.Y - triangle[k].Y) / dy; - if (t < (1 + EPS) && t > -EPS) - { - // we have an intersection - xComponent = triangle[k].X + t * dx; - - if (xComponent > pivot.X && xComponent <= bounds.Right) + // find intersection with plane x = m_boundingBox[1].dX + t = (bounds.Right - triangle[k].X) / dx; + if (t < (1 + EPS) && t > -EPS) { - AddToRegion(index, SE); - AddToRegion(index, NE); - } - else if (xComponent >= bounds.Left) - { - AddToRegion(index, SW); - AddToRegion(index, NW); + // we have an intersection + double yComponent = triangle[k].Y + t * dy; + + if (yComponent < pivot.Y && yComponent >= bounds.Bottom) + { + AddToRegion(index, SE); + } + else if (yComponent <= bounds.Top) + { + AddToRegion(index, NE); + } } } - // find intersection with plane y = m_boundingBox[0].dY - t = (bounds.Bottom - triangle[k].Y) / dy; - if (t < (1 + EPS) && t > -EPS) + void FindIntersectionsWithY(double dx, double dy, Point[] triangle, int index, int k) { - // we have an intersection - xComponent = triangle[k].X + t * dx; + double t, xComponent; - if (xComponent > pivot.X && xComponent <= bounds.Right) + // find intersection with plane y = m_pivot.dY + t = (pivot.Y - triangle[k].Y) / dy; + if (t < (1 + EPS) && t > -EPS) { - AddToRegion(index, SE); + // we have an intersection + xComponent = triangle[k].X + t * dx; + + if (xComponent > pivot.X && xComponent <= bounds.Right) + { + AddToRegion(index, SE); + AddToRegion(index, NE); + } + else if (xComponent >= bounds.Left) + { + AddToRegion(index, SW); + AddToRegion(index, NW); + } } - else if (xComponent >= bounds.Left) + + // find intersection with plane y = m_boundingBox[0].dY + t = (bounds.Bottom - triangle[k].Y) / dy; + if (t < (1 + EPS) && t > -EPS) { - AddToRegion(index, SW); + // we have an intersection + xComponent = triangle[k].X + t * dx; + + if (xComponent > pivot.X && xComponent <= bounds.Right) + { + AddToRegion(index, SE); + } + else if (xComponent >= bounds.Left) + { + AddToRegion(index, SW); + } + } + + // find intersection with plane y = m_boundingBox[1].dY + t = (bounds.Top - triangle[k].Y) / dy; + if (t < (1 + EPS) && t > -EPS) + { + // we have an intersection + xComponent = triangle[k].X + t * dx; + + if (xComponent > pivot.X && xComponent <= bounds.Right) + { + AddToRegion(index, NE); + } + else if (xComponent >= bounds.Left) + { + AddToRegion(index, NW); + } } } - // find intersection with plane y = m_boundingBox[1].dY - t = (bounds.Top - triangle[k].Y) / dy; - if (t < (1 + EPS) && t > -EPS) + int FindRegion(Point point) { - // we have an intersection - xComponent = triangle[k].X + t * dx; - - if (xComponent > pivot.X && xComponent <= bounds.Right) + int b = 2; + if (point.Y < pivot.Y) { - AddToRegion(index, NE); + b = 0; } - else if (xComponent >= bounds.Left) + if (point.X > pivot.X) { - AddToRegion(index, NW); + b++; } + return b; } - } - int FindRegion(Point point) - { - int b = 2; - if (point.Y < pivot.Y) + void AddToRegion(int index, int region) { - b = 0; - } - if (point.X > pivot.X) - { - b++; - } - return b; - } - - void AddToRegion(int index, int region) - { - //if (!(m_bitRegions & BITVECTOR[region])) - if ((bitRegions & BITVECTOR[region]) == 0) - { - regions[region].triangles.Add(index); - bitRegions |= BITVECTOR[region]; + //if (!(m_bitRegions & BITVECTOR[region])) + if ((bitRegions & BITVECTOR[region]) == 0) + { + regions[region].triangles.Add(index); + bitRegions |= BITVECTOR[region]; + } } } } - - #endregion } diff --git a/Triangle.NET/Triangle/Triangle.csproj b/Triangle.NET/Triangle/Triangle.csproj index ad925d6..4827075 100644 --- a/Triangle.NET/Triangle/Triangle.csproj +++ b/Triangle.NET/Triangle/Triangle.csproj @@ -43,12 +43,15 @@ + + + @@ -64,8 +67,7 @@ - - + @@ -99,7 +101,7 @@ - +