More code reorganization (3)

git-svn-id: https://triangle.svn.codeplex.com/svn@75023 0e2699bc-83d4-4a8f-98e7-55e24ab8c7a5
This commit is contained in:
SND\wo80_cp
2014-05-29 21:57:28 +00:00
parent faa7d4df47
commit a9de99f651
52 changed files with 1376 additions and 1465 deletions
+12 -6
View File
@@ -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);
+6 -9
View File
@@ -10,6 +10,7 @@ namespace MeshRenderer.Core
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.Tools;
using TriangleNet.Meshing.Iterators;
/// <summary>
/// Stores the current mesh in a rendering friendly data structure.
@@ -31,7 +32,7 @@ namespace MeshRenderer.Core
/// <summary>
/// Copy input geometry data.
/// </summary>
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());
}
/// <summary>
@@ -118,14 +115,14 @@ namespace MeshRenderer.Core
// Copy edges
var edges = new List<uint>(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)
{
+1 -1
View File
@@ -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);
+30 -21
View File
@@ -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);
+3 -26
View File
@@ -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")
@@ -7,9 +7,6 @@
namespace MeshExplorer.Generators
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Geometry;
/// <summary>
@@ -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)
{
+12 -15
View File
@@ -7,9 +7,6 @@
namespace MeshExplorer.Generators
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Geometry;
/// <summary>
@@ -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;
}
@@ -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;
}
@@ -6,10 +6,6 @@
namespace MeshExplorer.Generators
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Geometry;
/// <summary>
@@ -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);
}
}
@@ -6,10 +6,6 @@
namespace MeshExplorer.Generators
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Geometry;
/// <summary>
@@ -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;
@@ -7,9 +7,6 @@
namespace MeshExplorer.Generators
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Geometry;
/// <summary>
@@ -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;
+7 -10
View File
@@ -7,9 +7,6 @@
namespace MeshExplorer.Generators
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Geometry;
/// <summary>
@@ -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;
}
+13 -16
View File
@@ -7,9 +7,6 @@
namespace MeshExplorer.Generators
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Geometry;
/// <summary>
@@ -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;
}
+4 -4
View File
@@ -51,9 +51,9 @@ namespace MeshExplorer.IO
/// <summary>
/// Read an input geometry from given file.
/// </summary>
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
/// <returns></returns>
public static Mesh Import(string path)
{
IMeshFile provider = GetProviderInstance(path);
var provider = GetProviderInstance(path);
return provider.Import(path);
return (Mesh)provider.Import(path);
}
/// <summary>
+20 -20
View File
@@ -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<ITriangle> 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
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
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<string, object> points, ref int count)
private void ReadPoints(Polygon geometry, Dictionary<string, object> 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<string, object> segments, int count)
private void ReadSegments(Polygon geometry, Dictionary<string, object> 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)
);
));
}
}
@@ -8,9 +8,9 @@ namespace MeshExplorer.IO.Formats
{
using System;
using System.IO;
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.IO;
using TriangleNet.Meshing;
/// <summary>
/// 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);
}
}
}
@@ -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;
+1 -1
View File
@@ -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
//
+2 -2
View File
@@ -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)
{
}
+2 -2
View File
@@ -18,8 +18,8 @@ namespace MeshExplorer.Views
/// </summary>
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);
}
@@ -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)
{
}
+3 -3
View File
@@ -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 = "-";
+2 -17
View File
@@ -9,15 +9,12 @@ namespace TriangleNet
{
using System;
using TriangleNet.Geometry;
using TriangleNet.Logging;
/// <summary>
/// Controls the behavior of the meshing software.
/// </summary>
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<ITriangle, double, bool> usertest;
@@ -44,8 +40,6 @@ namespace TriangleNet
internal double maxGoodAngle = 0.0;
internal double offconstant = 0.0;
#endregion
/// <summary>
/// Creates an instance of the Behavior class.
/// </summary>
@@ -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; }
}
/// <summary>
/// Algorithm to use for triangulation.
/// </summary>
public TriangulationAlgorithm Algorithm
{
get { return algorithm; }
set { algorithm = value; }
}
/// <summary>
/// Suppresses boundary segment splitting.
/// </summary>
+7 -17
View File
@@ -8,14 +8,14 @@
namespace TriangleNet
{
/// <summary>
/// Implemented triangulation algorithms.
/// The type of the mesh vertex.
/// </summary>
public enum TriangulationAlgorithm
{
Dwyer,
Incremental,
SweepLine
};
public enum VertexType { InputVertex, SegmentVertex, FreeVertex, DeadVertex, UndeadVertex };
/// <summary>
/// Node renumbering algorithms.
/// </summary>
public enum NodeNumbering { None, Linear, CuthillMcKee };
/// <summary>
/// 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.
/// </remarks>
enum FindDirectionResult { Within, Leftcollinear, Rightcollinear };
/// <summary>
/// The type of the mesh vertex.
/// </summary>
public enum VertexType { InputVertex, SegmentVertex, FreeVertex, DeadVertex, UndeadVertex };
/// <summary>
/// Node renumbering algorithms.
/// </summary>
public enum NodeNumbering { None, Linear, CuthillMcKee };
}
@@ -0,0 +1,74 @@
namespace TriangleNet.Geometry
{
using TriangleNet.Meshing;
public static class IPolygonExtensions
{
/// <summary>
/// Triangulates a polygon.
/// </summary>
public static IMesh Triangulate(this IPolygon polygon)
{
var mesher = new GenericMesher();
return mesher.Triangulate(polygon, null, null);
}
/// <summary>
/// Triangulates a polygon, applying constraint options.
/// </summary>
/// <param name="options">Constraint options.</param>
public static IMesh Triangulate(this IPolygon polygon, ConstraintOptions options)
{
var mesher = new GenericMesher();
return mesher.Triangulate(polygon, options, null);
}
/// <summary>
/// Triangulates a polygon, applying quality options.
/// </summary>
/// <param name="quality">Quality options.</param>
public static IMesh Triangulate(this IPolygon polygon, QualityOptions quality)
{
var mesher = new GenericMesher();
return mesher.Triangulate(polygon, null, quality);
}
/// <summary>
/// Triangulates a polygon, applying quality and constraint options.
/// </summary>
/// <param name="options">Constraint options.</param>
/// <param name="quality">Quality options.</param>
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;
}
/// <summary>
/// Triangulates a polygon, applying quality and constraint options.
/// </summary>
/// <param name="options">Constraint options.</param>
/// <param name="quality">Quality options.</param>
/// <param name="triangulator">The triangulation algorithm.</param>
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;
}
}
}
@@ -1,257 +0,0 @@
// -----------------------------------------------------------------------
// <copyright file="InputGeometry.cs" company="">
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Geometry
{
using System;
using System.Collections.Generic;
using TriangleNet.Data;
/// <summary>
/// The input geometry which will be triangulated. May represent a
/// pointset or a planar straight line graph.
/// </summary>
public class InputGeometry
{
internal List<Vertex> points;
internal List<Edge> segments;
internal List<Point> holes;
internal List<RegionPointer> regions;
Rectangle bounds;
// Used to check consitent use of point attributes.
private int pointAttributes = -1;
/// <summary>
/// Initializes a new instance of the <see cref="InputGeometry" /> class.
/// </summary>
public InputGeometry()
: this(3)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="InputGeometry" /> class.
/// The point list will be initialized with a given capacity.
/// </summary>
/// <param name="capacity">Point list capacity.</param>
public InputGeometry(int capacity)
{
points = new List<Vertex>(capacity);
segments = new List<Edge>();
holes = new List<Point>();
regions = new List<RegionPointer>();
bounds = new Rectangle();
pointAttributes = -1;
}
/// <summary>
/// Gets the bounding box of the input geometry.
/// </summary>
public Rectangle Bounds
{
get { return bounds; }
}
/// <summary>
/// Indicates, whether the geometry should be treated as a PSLG.
/// </summary>
public bool HasSegments
{
get { return segments.Count > 0; }
}
/// <summary>
/// Gets the number of points.
/// </summary>
public int Count
{
get { return points.Count; }
}
/// <summary>
/// Gets the list of input points.
/// </summary>
public IEnumerable<Point> Points
{
get { return points; }
}
/// <summary>
/// Gets the list of input segments.
/// </summary>
public ICollection<Edge> Segments
{
get { return segments; }
}
/// <summary>
/// Gets the list of input holes.
/// </summary>
public ICollection<Point> Holes
{
get { return holes; }
}
/// <summary>
/// Gets the list of regions.
/// </summary>
public ICollection<RegionPointer> Regions
{
get { return regions; }
}
/// <summary>
/// Clear input geometry.
/// </summary>
public void Clear()
{
points.Clear();
segments.Clear();
holes.Clear();
regions.Clear();
pointAttributes = -1;
}
/// <summary>
/// Adds a point to the geometry.
/// </summary>
/// <param name="x">X coordinate.</param>
/// <param name="y">Y coordinate.</param>
public void AddPoint(double x, double y)
{
AddPoint(x, y, 0);
}
/// <summary>
/// Adds a point to the geometry.
/// </summary>
/// <param name="x">X coordinate.</param>
/// <param name="y">Y coordinate.</param>
/// <param name="boundary">Boundary marker.</param>
public void AddPoint(double x, double y, int boundary)
{
var v = new Vertex(x, y, boundary);
points.Add(v);
bounds.Expand(v);
}
/// <summary>
/// Adds a point to the geometry.
/// </summary>
/// <param name="x">X coordinate.</param>
/// <param name="y">Y coordinate.</param>
/// <param name="boundary">Boundary marker.</param>
/// <param name="attribute">Point attribute.</param>
public void AddPoint(double x, double y, int boundary, double attribute)
{
AddPoint(x, y, 0, new double[] { attribute });
}
/// <summary>
/// Adds a point to the geometry.
/// </summary>
/// <param name="x">X coordinate.</param>
/// <param name="y">Y coordinate.</param>
/// <param name="boundary">Boundary marker.</param>
/// <param name="attribs">Point attributes.</param>
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);
}
/// <summary>
/// Adds a vertex to the geometry.
/// </summary>
/// <param name="v">Vertex instance.</param>
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);
}
/// <summary>
/// Adds a hole location to the geometry.
/// </summary>
/// <param name="x">X coordinate of the hole.</param>
/// <param name="y">Y coordinate of the hole.</param>
public void AddHole(double x, double y)
{
holes.Add(new Point(x, y));
}
/// <summary>
/// Adds a hole location to the geometry.
/// </summary>
/// <param name="x">X coordinate of the hole.</param>
/// <param name="y">Y coordinate of the hole.</param>
/// <param name="id">The region id.</param>
public void AddRegion(double x, double y, int id)
{
regions.Add(new RegionPointer(x, y, id));
}
/// <summary>
/// Adds a segment to the geometry.
/// </summary>
/// <param name="p0">First endpoint.</param>
/// <param name="p1">Second endpoint.</param>
public void AddSegment(int p0, int p1)
{
AddSegment(p0, p1, 0);
}
/// <summary>
/// Adds a segment to the geometry.
/// </summary>
/// <param name="p0">First endpoint.</param>
/// <param name="p1">Second endpoint.</param>
/// <param name="boundary">Segment marker.</param>
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));
}
}
}
+9 -9
View File
@@ -34,16 +34,16 @@ namespace TriangleNet.Geometry
/// Initializes a new instance of the <see cref="Rectangle" /> class
/// with predefined bounds.
/// </summary>
/// <param name="xmin">Minimum x value.</param>
/// <param name="ymin">Minimum y value.</param>
/// <param name="xmax">Maximum x value.</param>
/// <param name="ymax">Maximum y value.</param>
public Rectangle(double xmin, double ymin, double xmax, double ymax)
/// <param name="x">Minimum x value (left).</param>
/// <param name="y">Minimum y value (bottom).</param>
/// <param name="width">Width of the rectangle.</param>
/// <param name="height">Height of the rectangle.</param>
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;
}
/// <summary>
+1 -1
View File
@@ -129,7 +129,7 @@ namespace TriangleNet.IO
}
}
private void WriteGeometry(InputGeometry geometry)
private void WriteGeometry(IPolygon geometry)
{
stream.WriteLine("#!G{0}", this.iteration++);
}
+4 -4
View File
@@ -35,7 +35,7 @@ namespace TriangleNet.IO
/// </summary>
/// <param name="filename">The path of the file to read.</param>
/// <returns>An instance of the <see cref="IPolygon" /> class.</returns>
public static InputGeometry Read(string filename)
public static IPolygon Read(string filename)
{
foreach (IPolygonFormat format in formats)
{
@@ -53,7 +53,7 @@ namespace TriangleNet.IO
/// </summary>
/// <param name="mesh">An instance of the <see cref="IPolygon" /> class.</param>
/// <param name="filename">The path of the file to save.</param>
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
/// </summary>
/// <param name="filename">The path of the file to read.</param>
/// <returns>An instance of the <see cref="IMesh" /> interface.</returns>
public static Mesh Import(string filename)
public static IMesh Import(string filename)
{
foreach (IMeshFormat format in formats)
{
@@ -93,7 +93,7 @@ namespace TriangleNet.IO
/// </summary>
/// <param name="mesh">An instance of the <see cref="IMesh" /> interface.</param>
/// <param name="filename">The path of the file to save.</param>
public static void Write(Mesh mesh, string filename)
public static void Write(IMesh mesh, string filename)
{
foreach (IMeshFormat format in formats)
{
+4 -3
View File
@@ -7,6 +7,7 @@
namespace TriangleNet.IO
{
using System.IO;
using TriangleNet.Meshing;
/// <summary>
/// Interface for mesh I/O.
@@ -18,20 +19,20 @@ namespace TriangleNet.IO
/// </summary>
/// <param name="filename">The path of the file to read.</param>
/// <returns>An instance of the <see cref="IMesh" /> interface.</returns>
Mesh Import(string filename);
IMesh Import(string filename);
/// <summary>
/// Save a mesh to disk.
/// </summary>
/// <param name="mesh">An instance of the <see cref="IMesh" /> interface.</param>
/// <param name="filename">The path of the file to save.</param>
void Write(Mesh mesh, string filename);
void Write(IMesh mesh, string filename);
/// <summary>
/// Save a mesh to a <see cref="StreamWriter" />.
/// </summary>
/// <param name="mesh">An instance of the <see cref="IMesh" /> interface.</param>
/// <param name="stream">The stream to save to.</param>
void Write(Mesh mesh, StreamWriter stream);
void Write(IMesh mesh, StreamWriter stream);
}
}
+3 -3
View File
@@ -19,20 +19,20 @@ namespace TriangleNet.IO
/// </summary>
/// <param name="filename">The path of the file to read.</param>
/// <returns>An instance of the <see cref="IPolygon" /> class.</returns>
InputGeometry Read(string filename);
IPolygon Read(string filename);
/// <summary>
/// Save a polygon geometry to disk.
/// </summary>
/// <param name="polygon">An instance of the <see cref="IPolygon" /> class.</param>
/// <param name="filename">The path of the file to save.</param>
void Write(InputGeometry polygon, string filename);
void Write(IPolygon polygon, string filename);
/// <summary>
/// Save a polygon geometry to a <see cref="StreamWriter" />.
/// </summary>
/// <param name="polygon">An instance of the <see cref="IPolygon" /> class.</param>
/// <param name="stream">The stream to save to.</param>
void Write(InputGeometry polygon, StreamWriter stream);
void Write(IPolygon polygon, StreamWriter stream);
}
}
+7 -7
View File
@@ -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<ITriangle> 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();
}
+23 -25
View File
@@ -59,7 +59,7 @@ namespace TriangleNet.IO
/// <param name="line">The current line.</param>
/// <param name="attributes">Number of point attributes</param>
/// <param name="marks">Number of point markers (0 or 1)</param>
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
/// <summary>
/// Reads geometry information from .node or .poly files.
/// </summary>
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
/// <summary>
/// Reads a mesh from .node, .poly or .ele files.
/// </summary>
public static void Read(string filename, out InputGeometry geometry, out List<ITriangle> triangles)
public static void Read(string filename, out Polygon geometry, out List<ITriangle> triangles)
{
triangles = null;
@@ -128,9 +128,9 @@ namespace TriangleNet.IO
/// <summary>
/// Reads geometry information from .node or .poly files.
/// </summary>
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
/// </summary>
/// <param name="nodefilename"></param>
/// <remarks>Will NOT read associated .ele by default.</remarks>
public static InputGeometry ReadNodeFile(string nodefilename)
public static Polygon ReadNodeFile(string nodefilename)
{
return ReadNodeFile(nodefilename, false);
}
@@ -154,9 +154,9 @@ namespace TriangleNet.IO
/// </summary>
/// <param name="nodefilename"></param>
/// <param name="readElements"></param>
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
/// </summary>
/// <param name="polyfilename"></param>
/// <remarks>Will NOT read associated .ele by default.</remarks>
public static InputGeometry ReadPolyFile(string polyfilename)
public static Polygon ReadPolyFile(string polyfilename)
{
return ReadPolyFile(polyfilename, false, false);
}
@@ -253,7 +253,7 @@ namespace TriangleNet.IO
/// <param name="polyfilename"></param>
/// <param name="readElements">If true, look for an associated .ele file.</param>
/// <remarks>Will NOT read associated .area by default.</remarks>
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
/// <param name="polyfilename"></param>
/// <param name="readElements">If true, look for an associated .ele file.</param>
/// <param name="readElements">If true, look for an associated .area file.</param>
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
}
}
}
+3 -3
View File
@@ -186,16 +186,16 @@ namespace TriangleNet.IO
/// <remarks>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.</remarks>
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");
+242 -273
View File
@@ -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;
/// <summary>
/// Mesh data structure.
/// </summary>
public class Mesh
public class Mesh : IMesh
{
#region Variables
ILog<LogItem> logger;
QualityMesher quality;
QualityMesher qualityMesher;
// Stack that maintains a list of recently flipped triangles.
Stack<Otri> flipstack;
@@ -85,14 +82,6 @@ namespace TriangleNet
#region Public properties
/// <summary>
/// Gets the mesh behavior instance.
/// </summary>
public Behavior Behavior
{
get { return this.behavior; }
}
/// <summary>
/// Gets the mesh bounding box.
/// </summary>
@@ -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 <see cref="Mesh" /> class.
/// </summary>
public Mesh()
: this(new Behavior())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Mesh" /> class.
/// </summary>
public Mesh(Behavior behavior)
{
this.behavior = behavior;
logger = Log.Instance;
behavior = new Behavior();
@@ -201,7 +180,7 @@ namespace TriangleNet
holes = new List<Point>();
regions = new List<RegionPointer>();
quality = new QualityMesher(this);
qualityMesher = new QualityMesher(this);
locator = new TriangleLocator(this);
@@ -214,217 +193,14 @@ namespace TriangleNet
}
}
/// <summary>
/// Triangulate given input file (.node or .poly).
/// </summary>
/// <param name="input"></param>
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);
}
/// <summary>
/// Triangulate given input data.
/// </summary>
/// <param name="input"></param>
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;
}
/// <summary>
/// Refines the current mesh by finding the maximum triangle area and setting
/// the a global area constraint to half its size.
/// </summary>
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();
}
}
/// <summary>
/// Refines the current mesh by setting a global area constraint.
/// </summary>
/// <param name="areaConstraint">Global area constraint.</param>
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;
}
/// <summary>
/// Refines the current mesh.
/// </summary>
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;
}
/// <summary>
/// Smooth the current mesh.
/// </summary>
public void Smooth()
{
numbering = NodeNumbering.None;
ISmoother smoother = new SimpleSmoother(this);
smoother.Smooth();
}
/// <summary>
@@ -481,33 +257,226 @@ namespace TriangleNet
#region Misc
/*
/// <summary>
/// Form a Delaunay triangulation.
/// Load a mesh from file (.node/poly and .ele).
/// </summary>
/// <returns>The number of points on the hull.</returns>
private int Delaunay()
public void Load(string filename)
{
int hulledges = 0;
List<ITriangle> 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)
}
/// <summary>
/// Reconstructs a mesh from raw input data.
/// </summary>
public void Load(InputGeometry input, List<ITriangle> 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;
}
*/
/// <summary>
/// Triangulate given input data.
/// </summary>
/// <param name="input"></param>
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;
}
/// <summary>
/// Refines the current mesh.
/// </summary>
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;
}
/// <summary>
@@ -625,12 +594,11 @@ namespace TriangleNet
/// Read the vertices from memory.
/// </summary>
/// <param name="data">The input data.</param>
internal void TransferNodes(InputGeometry data)
internal void TransferNodes(ICollection<Vertex> points)
{
List<Vertex> 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;
}
/// <summary>
@@ -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.
@@ -8,9 +8,9 @@
namespace TriangleNet.Meshing.Algorithm
{
using System;
using System.Collections.Generic;
using TriangleNet.Data;
using TriangleNet.Geometry;
using TriangleNet.Logging;
/// <summary>
/// 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.
/// </remarks>
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;
/// <summary>
/// Form a Delaunay triangulation by the divide-and-conquer method.
/// </summary>
/// <returns></returns>
/// <remarks>
/// Sorts the vertices, calls a recursive procedure to triangulate them, and
/// removes the bounding box, setting boundary markers as appropriate.
/// </remarks>
public IMesh Triangulate(ICollection<Vertex> 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;
}
/// <summary>
/// Sort an array of vertices by x-coordinate, using the y-coordinate as a secondary key.
/// </summary>
@@ -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;
}
/// <summary>
/// Form a Delaunay triangulation by the divide-and-conquer method.
/// </summary>
/// <returns></returns>
/// <remarks>
/// Sorts the vertices, calls a recursive procedure to triangulate them, and
/// removes the bounding box, setting boundary markers as appropriate.
/// </remarks>
public 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);
}
}
}
@@ -7,17 +7,54 @@
namespace TriangleNet.Meshing.Algorithm
{
using System.Collections.Generic;
using TriangleNet.Data;
using TriangleNet.Logging;
using TriangleNet.Geometry;
/// <summary>
/// Builds a delaunay triangulation using the incremental algorithm.
/// </summary>
class Incremental
public class Incremental : ITriangulator
{
Mesh mesh;
/// <summary>
/// Form a Delaunay triangulation by incrementally inserting vertices.
/// </summary>
/// <returns>Returns the number of edges on the convex hull of the
/// triangulation.</returns>
public IMesh Triangulate(ICollection<Vertex> 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;
}
/// <summary>
/// Form an "infinite" bounding triangle to insert vertices into.
/// </summary>
@@ -144,38 +181,5 @@ namespace TriangleNet.Meshing.Algorithm
return hullsize;
}
/// <summary>
/// Form a Delaunay triangulation by incrementally inserting vertices.
/// </summary>
/// <returns>Returns the number of edges on the convex hull of the
/// triangulation.</returns>
public 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();
}
}
}
@@ -11,18 +11,17 @@ namespace TriangleNet.Meshing.Algorithm
using System.Collections.Generic;
using TriangleNet.Data;
using TriangleNet.Geometry;
using TriangleNet.Logging;
using TriangleNet.Tools;
/// <summary>
/// Builds a delaunay triangulation using the sweepline algorithm.
/// </summary>
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<SplayNode> splaynodes;
public IMesh Triangulate(ICollection<Vertex> 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<SplayNode>();
splayroot = null;
CreateHeap(out eventheap);//, out events, out freeevents);
heapsize = mesh.invertices;
mesh.MakeTriangle(ref lefttri);
mesh.MakeTriangle(ref righttri);
lefttri.Bond(ref righttri);
lefttri.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<SplayNode>();
splayroot = null;
CreateHeap(out eventheap);//, out events, out freeevents);
heapsize = mesh.invertices;
mesh.MakeTriangle(ref lefttri);
mesh.MakeTriangle(ref righttri);
lefttri.Bond(ref righttri);
lefttri.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
/// <summary>
@@ -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.
/// </summary>
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++;
+7 -8
View File
@@ -12,14 +12,13 @@ namespace TriangleNet.Meshing
using System.Linq;
using TriangleNet.Data;
using TriangleNet.Geometry;
using TriangleNet.Logging;
/// <summary>
/// The DataReader class provides methods for mesh reconstruction.
/// </summary>
public class Converter
{
public Mesh ToMesh(InputGeometry polygon, IList<ITriangle> triangles)
public Mesh ToMesh(Polygon polygon, IList<ITriangle> 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.
/// </remarks>
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<Otri>[] vertexarray)
private static void SetSegments(Mesh mesh, Polygon polygon, List<Otri>[] 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++)
{
@@ -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;
}
/// <summary>
/// Triangulates a point set.
/// </summary>
/// <param name="points">Collection of points.</param>
/// <returns>Mesh</returns>
public IMesh Triangulate(ICollection<Vertex> points)
{
return triangulator.Triangulate(points);
}
/// <summary>
/// Triangulates a polygon.
/// </summary>
/// <param name="polygon">The polygon.</param>
/// <returns>Mesh</returns>
public IMesh Triangulate(IPolygon polygon)
{
return Triangulate(polygon, null, null);
}
/// <summary>
/// Triangulates a polygon, applying constraint options.
/// </summary>
/// <param name="polygon">The polygon.</param>
/// <param name="options">Constraint options.</param>
/// <returns>Mesh</returns>
public IMesh Triangulate(IPolygon polygon, ConstraintOptions options)
{
return Triangulate(polygon, options, null);
}
/// <summary>
/// Triangulates a polygon, applying quality options.
/// </summary>
/// <param name="polygon">The polygon.</param>
/// <param name="quality">Quality options.</param>
/// <returns>Mesh</returns>
public IMesh Triangulate(IPolygon polygon, QualityOptions quality)
{
return Triangulate(polygon, null, quality);
}
/// <summary>
/// Triangulates a polygon, applying quality and constraint options.
/// </summary>
/// <param name="polygon">The polygon.</param>
/// <param name="options">Constraint options.</param>
/// <param name="quality">Quality options.</param>
/// <returns>Mesh</returns>
public IMesh Triangulate(IPolygon polygon, ConstraintOptions options, QualityOptions quality)
{
var mesh = (Mesh)triangulator.Triangulate(polygon.Points);
mesh.ApplyConstraints(polygon, options, quality);
return mesh;
}
/// <summary>
/// Generates a structured mesh.
/// </summary>
/// <param name="bounds">Bounds of the mesh.</param>
/// <param name="nx">Number of segments in x direction.</param>
/// <param name="ny">Number of segments in y direction.</param>
/// <returns>Mesh</returns>
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);
}
}
}
@@ -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);
}
}
+20
View File
@@ -0,0 +1,20 @@

namespace TriangleNet.Meshing
{
using System.Collections.Generic;
using TriangleNet.Data;
using TriangleNet.Geometry;
public interface IMesh
{
ICollection<Vertex> Vertices { get; }
IEnumerable<Edge> Edges { get; }
ICollection<Segment> Segments { get; }
ICollection<Triangle> Triangles { get; }
IList<Point> Holes { get; }
Rectangle Bounds { get; }
void Refine(QualityOptions quality);
}
}
@@ -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);
}
}
@@ -14,6 +14,6 @@ namespace TriangleNet.Meshing
/// </summary>
public interface ITriangulator
{
Mesh Triangulate(ICollection<Vertex> points);
IMesh Triangulate(ICollection<Vertex> points);
}
}
@@ -4,18 +4,16 @@
// </copyright>
// -----------------------------------------------------------------------
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;
/// <summary>
/// Enumerates the edges of a triangulation.
/// </summary>
public class EdgeEnumerator : IEnumerator<Edge>
public class EdgeIterator : IEnumerator<Edge>
{
IEnumerator<Triangle> triangles;
Otri tri = default(Otri);
@@ -25,9 +23,9 @@ namespace TriangleNet.Geometry
Vertex p1, p2;
/// <summary>
/// Initializes a new instance of the <see cref="EdgeEnumerator" /> class.
/// Initializes a new instance of the <see cref="EdgeIterator" /> class.
/// </summary>
public EdgeEnumerator(Mesh mesh)
public EdgeIterator(Mesh mesh)
{
triangles = mesh.triangles.Values.GetEnumerator();
triangles.MoveNext();
@@ -5,12 +5,10 @@
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet.Tools
namespace TriangleNet.Meshing.Iterators
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Data;
/// <summary>
+3 -5
View File
@@ -6,16 +6,14 @@
namespace TriangleNet.Smoothing
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Meshing;
/// <summary>
/// Interface for mesh smoothers.
/// </summary>
public interface ISmoother
{
void Smooth();
void Smooth(IMesh mesh);
void Smooth(IMesh mesh, int limit);
}
}
@@ -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;
/// <summary>
@@ -22,35 +19,42 @@ namespace TriangleNet.Smoothing
/// </remarks>
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);
}
/// <summary>
/// Smooth all free nodes.
/// </summary>
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
/// <summary>
/// Rebuild the input geometry.
/// </summary>
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;
}
}
}
@@ -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);
+247 -247
View File
@@ -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
/// <summary>
/// A node of the quadtree.
/// </summary>
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<int> triangles;
byte bitRegions;
public QuadNode(Rectangle box, QuadTree tree)
: this(box, tree, false)
/// <summary>
/// A node of the quadtree.
/// </summary>
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<int> triangles;
this.regions = new QuadNode[4];
this.triangles = new List<int>();
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<int>();
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<int> FindTriangles(Point searchPoint)
{
int region = FindRegion(searchPoint);
if (regions[region] == null)
public List<int> 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
}
+5 -3
View File
@@ -43,12 +43,15 @@
<ItemGroup>
<Compile Include="Geometry\IEdge.cs" />
<Compile Include="Geometry\IPolygon.cs" />
<Compile Include="Geometry\IPolygonExtensions.cs" />
<Compile Include="Geometry\Polygon.cs" />
<Compile Include="IO\FileProcessor.cs" />
<Compile Include="IO\IFileFormat.cs" />
<Compile Include="Meshing\ConstraintOptions.cs" />
<Compile Include="Meshing\Converter.cs" />
<Compile Include="Meshing\GenericMesher.cs" />
<Compile Include="Meshing\IConstraintMesher.cs" />
<Compile Include="Meshing\IMesh.cs" />
<Compile Include="Meshing\IQualityMesher.cs" />
<Compile Include="Meshing\ITriangulator.cs" />
<Compile Include="Data\BadTriQueue.cs" />
@@ -64,8 +67,7 @@
<Compile Include="Meshing\Algorithm\Dwyer.cs" />
<Compile Include="Geometry\Rectangle.cs" />
<Compile Include="Geometry\Edge.cs" />
<Compile Include="Geometry\EdgeEnumerator.cs" />
<Compile Include="Geometry\InputGeometry.cs" />
<Compile Include="Meshing\Iterators\EdgeIterator.cs" />
<Compile Include="Geometry\ITriangle.cs" />
<Compile Include="Geometry\Point.cs" />
<Compile Include="Geometry\RegionPointer.cs" />
@@ -99,7 +101,7 @@
<Compile Include="Tools\IVoronoi.cs" />
<Compile Include="Tools\QuadTree.cs" />
<Compile Include="Tools\QualityMeasure.cs" />
<Compile Include="Tools\RegionIterator.cs" />
<Compile Include="Meshing\Iterators\RegionIterator.cs" />
<Compile Include="Tools\Statistic.cs" />
<Compile Include="Meshing\Algorithm\SweepLine.cs" />
<Compile Include="Tools\Voronoi.cs" />