diff --git a/Triangle.NET/MeshRenderer.Core/GDI/RenderControl.cs b/Triangle.NET/MeshRenderer.Core/GDI/RenderControl.cs
index bf64193..6d8e0c9 100644
--- a/Triangle.NET/MeshRenderer.Core/GDI/RenderControl.cs
+++ b/Triangle.NET/MeshRenderer.Core/GDI/RenderControl.cs
@@ -77,11 +77,8 @@ namespace MeshRenderer.Core.GDI
get { return showVoronoi; }
set
{
- if (showVoronoi != value)
- {
- this.Render();
- }
showVoronoi = value;
+ this.Render();
}
}
@@ -90,11 +87,8 @@ namespace MeshRenderer.Core.GDI
get { return showRegions; }
set
{
- if (showRegions != value)
- {
- this.Render();
- }
showRegions = value;
+ this.Render();
}
}
diff --git a/Triangle.NET/TestApp/FormGenerator.cs b/Triangle.NET/TestApp/FormGenerator.cs
index eaee467..bd2497a 100644
--- a/Triangle.NET/TestApp/FormGenerator.cs
+++ b/Triangle.NET/TestApp/FormGenerator.cs
@@ -22,8 +22,8 @@ namespace MeshExplorer
if (currentGenerator.ParameterCount > 0)
{
sliderParam1.Enabled = true;
- lbParam1.Text = currentGenerator.ParameterDescription(1);
- lbParam1Val.Text = currentGenerator.ParameterDescription(1, sliderParam1.Value);
+ lbParam1.Text = currentGenerator.ParameterDescription(0);
+ lbParam1Val.Text = currentGenerator.ParameterDescription(0, sliderParam1.Value);
}
else
{
@@ -35,8 +35,8 @@ namespace MeshExplorer
if (currentGenerator.ParameterCount > 1)
{
sliderParam2.Enabled = true;
- lbParam2.Text = currentGenerator.ParameterDescription(2);
- lbParam2Val.Text = currentGenerator.ParameterDescription(2, sliderParam2.Value);
+ lbParam2.Text = currentGenerator.ParameterDescription(1);
+ lbParam2Val.Text = currentGenerator.ParameterDescription(1, sliderParam2.Value);
}
else
{
@@ -48,8 +48,8 @@ namespace MeshExplorer
if (currentGenerator.ParameterCount > 2)
{
sliderParam3.Enabled = true;
- lbParam3.Text = currentGenerator.ParameterDescription(3);
- lbParam3Val.Text = currentGenerator.ParameterDescription(3, sliderParam3.Value);
+ lbParam3.Text = currentGenerator.ParameterDescription(2);
+ lbParam3Val.Text = currentGenerator.ParameterDescription(2, sliderParam3.Value);
}
else
{
@@ -115,7 +115,7 @@ namespace MeshExplorer
{
if (currentGenerator != null)
{
- lbParam1Val.Text = currentGenerator.ParameterDescription(1, sliderParam1.Value);
+ lbParam1Val.Text = currentGenerator.ParameterDescription(0, sliderParam1.Value);
}
}
@@ -123,7 +123,7 @@ namespace MeshExplorer
{
if (currentGenerator != null)
{
- lbParam2Val.Text = currentGenerator.ParameterDescription(2, sliderParam2.Value);
+ lbParam2Val.Text = currentGenerator.ParameterDescription(1, sliderParam2.Value);
}
}
@@ -131,7 +131,7 @@ namespace MeshExplorer
{
if (currentGenerator != null)
{
- lbParam3Val.Text = currentGenerator.ParameterDescription(3, sliderParam3.Value);
+ lbParam3Val.Text = currentGenerator.ParameterDescription(2, sliderParam3.Value);
}
}
diff --git a/Triangle.NET/TestApp/FormMain.cs b/Triangle.NET/TestApp/FormMain.cs
index 8846c48..c7b5f12 100644
--- a/Triangle.NET/TestApp/FormMain.cs
+++ b/Triangle.NET/TestApp/FormMain.cs
@@ -663,6 +663,13 @@ namespace MeshExplorer
return;
}
+ if (menuViewVoronoi.Checked)
+ {
+ renderManager.ShowVoronoi = false;
+ menuViewVoronoi.Checked = false;
+ return;
+ }
+
IVoronoi voronoi;
if (mesh.IsPolygon)
@@ -677,7 +684,7 @@ namespace MeshExplorer
renderData.SetVoronoi(voronoi);
renderManager.SetData(renderData);
- menuViewVoronoi.Checked = !menuViewVoronoi.Checked;
+ menuViewVoronoi.Checked = true;
}
private void menuViewLog_Click(object sender, EventArgs e)
diff --git a/Triangle.NET/TestApp/Generators/BaseGenerator.cs b/Triangle.NET/TestApp/Generators/BaseGenerator.cs
new file mode 100644
index 0000000..83e4d9b
--- /dev/null
+++ b/Triangle.NET/TestApp/Generators/BaseGenerator.cs
@@ -0,0 +1,87 @@
+// -----------------------------------------------------------------------
+//
+// TODO: Update copyright text.
+//
+// -----------------------------------------------------------------------
+
+namespace MeshExplorer.Generators
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using TriangleNet.Geometry;
+
+ ///
+ /// TODO: Update summary.
+ ///
+ public abstract class BaseGenerator : IGenerator
+ {
+ private static int MAX_PARAMS = 3;
+
+ protected string name = "Name";
+ protected string description = "Description";
+ protected int parameter = 0;
+
+ protected string[] descriptions = new string[MAX_PARAMS];
+ protected int[][] ranges = new int[MAX_PARAMS][];
+
+ public virtual string Name { get { return name; } }
+ public virtual string Description { get { return description; } }
+ public virtual int ParameterCount { get { return parameter; } }
+
+ public virtual string ParameterDescription(int paramIndex)
+ {
+ if (descriptions[paramIndex] == null)
+ {
+ return String.Empty;
+ }
+
+ return descriptions[paramIndex];
+ }
+
+ public virtual string ParameterDescription(int paramIndex, double paramValue)
+ {
+ int[] range = ranges[paramIndex];
+
+ if (range == null)
+ {
+ return String.Empty;
+ }
+
+ int num = GetParamValueInt(paramIndex, paramValue);
+ return num.ToString();
+ }
+
+ public abstract InputGeometry Generate(double param0, double param1, double param2);
+
+ protected int GetParamValueInt(int paramIndex, double paramOffset)
+ {
+ int[] range = ranges[paramIndex];
+
+ if (range == null)
+ {
+ return 0;
+ }
+
+ return (int)((range[1] - range[0]) / 100.0 * paramOffset + range[0]);
+ }
+
+ protected double GetParamValueDouble(int paramIndex, double paramOffset)
+ {
+ int[] range = ranges[paramIndex];
+
+ if (range == null)
+ {
+ return 0;
+ }
+
+ return ((range[1] - range[0]) / 100.0 * paramOffset + range[0]);
+ }
+
+ public override string ToString()
+ {
+ return this.Name;
+ }
+ }
+}
diff --git a/Triangle.NET/TestApp/Generators/BoxWithHole.cs b/Triangle.NET/TestApp/Generators/BoxWithHole.cs
index 6424e9c..efa5a4e 100644
--- a/Triangle.NET/TestApp/Generators/BoxWithHole.cs
+++ b/Triangle.NET/TestApp/Generators/BoxWithHole.cs
@@ -15,89 +15,32 @@ namespace MeshExplorer.Generators
///
/// Generates a star contained in a box.
///
- public class BoxWithHole : IGenerator
+ public class BoxWithHole : BaseGenerator
{
- public string Name
+ public BoxWithHole()
{
- get { return "Box with Hole"; }
+ name = "Box with Hole";
+ description = "";
+ parameter = 3;
+
+ descriptions[0] = "Points on box sides:";
+ descriptions[1] = "Points on hole:";
+ descriptions[2] = "Radius:";
+
+ ranges[0] = new int[] { 5, 50 };
+ ranges[1] = new int[] { 10, 200 };
+ ranges[2] = new int[] { 5, 20 };
}
- public string Description
+ public override InputGeometry Generate(double param0, double param1, double param2)
{
- get { return ""; }
- }
-
- public int ParameterCount
- {
- get { return 3; }
- }
-
- public string ParameterDescription(int paramIndex)
- {
- if (paramIndex == 1)
- {
- return "Points on box sides:";
- }
-
- if (paramIndex == 2)
- {
- return "Points on hole:";
- }
-
- if (paramIndex == 3)
- {
- return "Radius:";
- }
-
- return "";
- }
-
- public string ParameterDescription(int paramIndex, double paramValue)
- {
- if (paramIndex == 1)
- {
- int numPoints = (int)((50.0 - 5.0) / 100.0 * paramValue + 5.0);
-
- if (numPoints < 5)
- {
- numPoints = 5;
- }
-
- return numPoints.ToString();
- }
-
- if (paramIndex == 2)
- {
- int numPoints = (int)((100.0 - 10.0) / 100.0 * paramValue + 10.0);
- numPoints = (numPoints / 5) * 5;
-
- if (numPoints < 10)
- {
- numPoints = 10;
- }
-
- return numPoints.ToString();
- }
-
- if (paramIndex == 3)
- {
- int radius = (int)((20.0 - 5.0) / 100.0 * paramValue + 5.0);
-
- return radius.ToString();
- }
-
- return "";
- }
-
- public InputGeometry Generate(double param1, double param2, double param3)
- {
- int numPoints = (int)((100.0 - 10.0) / 100.0 * param2 + 10.0);
+ int numPoints = GetParamValueInt(1, param1);
InputGeometry input = new InputGeometry(numPoints + 4);
double x, y, step = 2 * Math.PI / numPoints;
- double r = (int)((20.0 - 5.0) / 100.0 * param3 + 5.0);
+ double r = GetParamValueInt(2, param2);
// Generate circle
for (int i = 0; i < numPoints; i++)
@@ -111,7 +54,7 @@ namespace MeshExplorer.Generators
numPoints = input.Count;
- int numPointsB = (int)((50.0 - 5.0) / 100.0 * param1 + 5.0);
+ int numPointsB = GetParamValueInt(0, param0);
// Box sides are 100 units long
step = 100.0 / numPointsB;
@@ -154,10 +97,5 @@ namespace MeshExplorer.Generators
return input;
}
-
- public override string ToString()
- {
- return this.Name;
- }
}
}
diff --git a/Triangle.NET/TestApp/Generators/CircleWithHole.cs b/Triangle.NET/TestApp/Generators/CircleWithHole.cs
index ccac84f..bcd8c80 100644
--- a/Triangle.NET/TestApp/Generators/CircleWithHole.cs
+++ b/Triangle.NET/TestApp/Generators/CircleWithHole.cs
@@ -15,68 +15,28 @@ namespace MeshExplorer.Generators
///
/// Generates a ring polygon.
///
- public class CircleWithHole : IGenerator
+ public class CircleWithHole : BaseGenerator
{
- // Parameters range
- double[] range1 = { 100, 250 };
- double[] range2 = { 2, 15 };
-
- public string Name
+ public CircleWithHole()
{
- get { return "Circle with Hole"; }
+ name = "Circle with Hole";
+ description = "";
+ parameter = 2;
+
+ descriptions[0] = "Number of points:";
+ descriptions[1] = "Outer radius:";
+
+ ranges[0] = new int[] { 100, 250 };
+ ranges[1] = new int[] { 2, 15 };
}
- public string Description
- {
- get { return ""; }
- }
-
- public int ParameterCount
- {
- get { return 2; }
- }
-
- public string ParameterDescription(int paramIndex)
- {
- if (paramIndex == 1)
- {
- return "Number of points:";
- }
-
- if (paramIndex == 2)
- {
- return "Outer radius:";
- }
-
- return "";
- }
-
- public string ParameterDescription(int paramIndex, double paramValue)
- {
- if (paramIndex == 1)
- {
- int num = (int)((range1[1] - range1[0]) / 100.0 * paramValue + range1[0]);
-
- return num.ToString();
- }
-
- if (paramIndex == 2)
- {
- int radius = (int)((range2[1] - range2[0]) / 100.0 * paramValue + range2[0]);
-
- return radius.ToString();
- }
-
- return "";
- }
-
- public InputGeometry Generate(double param1, double param2, double param3)
+ public override InputGeometry Generate(double param0, double param1, double param2)
{
// Number of points on the outer circle
- int n = (int)((range1[1] - range1[0]) / 100.0 * param1 + range1[0]);
+ int n = GetParamValueInt(0, param0);
int count, npoints;
- double radius = (int)((range2[1] - range2[0]) / 100.0 * param2 + range2[0]);
+ double radius = GetParamValueInt(1, param1);
// Step size on the outer circle
double h = 2 * Math.PI * radius / n;
@@ -122,7 +82,7 @@ namespace MeshExplorer.Generators
input.AddHole(0, 0);
- // Regions: |------|------|---|
+ // Regions: |++++++|++++++|---|
// r 1 0
input.AddRegion((r + 3.0) / 4.0, 0, 1);
@@ -130,10 +90,5 @@ namespace MeshExplorer.Generators
return input;
}
-
- public override string ToString()
- {
- return this.Name;
- }
}
}
diff --git a/Triangle.NET/TestApp/Generators/RandomPoints.cs b/Triangle.NET/TestApp/Generators/RandomPoints.cs
index 15f6f8f..fe7c1c3 100644
--- a/Triangle.NET/TestApp/Generators/RandomPoints.cs
+++ b/Triangle.NET/TestApp/Generators/RandomPoints.cs
@@ -15,66 +15,26 @@ namespace MeshExplorer.Generators
///
/// Simple random points generator.
///
- public class RandomPoints : IGenerator
+ public class RandomPoints : BaseGenerator
{
- public string Name
+ public RandomPoints()
{
- get { return "Random Points"; }
+ name = "Random Points";
+ description = "";
+ parameter = 3;
+
+ descriptions[0] = "Number of points:";
+ descriptions[1] = "Width:";
+ descriptions[2] = "Height:";
+
+ ranges[0] = new int[] { 5, 5000 };
+ ranges[1] = new int[] { 10, 200 };
+ ranges[2] = new int[] { 10, 200 };
}
- public string Description
+ public override InputGeometry Generate(double param0, double param1, double param2)
{
- get { return ""; }
- }
-
- public int ParameterCount
- {
- get { return 3; }
- }
-
- public string ParameterDescription(int paramIndex)
- {
- if (paramIndex == 1)
- {
- return "Number of points:";
- }
-
- if (paramIndex == 2)
- {
- return "Width:";
- }
-
- return "Height:";
- }
-
- public string ParameterDescription(int paramIndex, double paramValue)
- {
- if (paramIndex == 1)
- {
- int numPoints = (int)((5000.0 - 5.0) / 100.0 * paramValue + 5.0);
- numPoints = (numPoints / 10) * 10;
-
- if (numPoints < 5)
- {
- numPoints = 5;
- }
-
- return numPoints.ToString();
- }
-
- if (paramIndex == 2 || paramIndex == 3)
- {
- int numPoints = (int)paramValue + 100;
-
- return numPoints.ToString();
- }
-
- return "";
- }
-
- public InputGeometry Generate(double param1, double param2, double param3)
- {
- int numPoints = (int)((5000.0 - 5.0) / 100.0 * param1 + 5.0);
+ int numPoints = GetParamValueInt(0, param0);
numPoints = (numPoints / 10) * 10;
if (numPoints < 5)
@@ -84,8 +44,8 @@ namespace MeshExplorer.Generators
InputGeometry input = new InputGeometry(numPoints);
- int width = (int)param2 + 100;
- int height = (int)param3 + 100;
+ int width = GetParamValueInt(1, param1);
+ int height = GetParamValueInt(2, param2);
for (int i = 0; i < numPoints; i++)
{
@@ -95,10 +55,5 @@ namespace MeshExplorer.Generators
return input;
}
-
- public override string ToString()
- {
- return this.Name;
- }
}
}
diff --git a/Triangle.NET/TestApp/Generators/RandomPointsCircle.cs b/Triangle.NET/TestApp/Generators/RandomPointsCircle.cs
index 40f4a95..788be09 100644
--- a/Triangle.NET/TestApp/Generators/RandomPointsCircle.cs
+++ b/Triangle.NET/TestApp/Generators/RandomPointsCircle.cs
@@ -15,43 +15,26 @@ namespace MeshExplorer.Generators
///
/// Simple random points generator (points distributed in a circle).
///
- public class RandomPointsCircle : IGenerator
+ public class RandomPointsCircle : BaseGenerator
{
- public string Name
+ public RandomPointsCircle()
{
- get { return "Random Points (Circle)"; }
+ name = "Random Points (Circle)";
+ description = "";
+ parameter = 2;
+
+ descriptions[0] = "Number of points:";
+ descriptions[1] = "Distribution:";
+
+ ranges[0] = new int[] { 5, 5000 };
+ ranges[1] = new int[] { 0, 1 };
}
- public string Description
+ public override string ParameterDescription(int paramIndex, double paramValue)
{
- get { return ""; }
- }
-
- public int ParameterCount
- {
- get { return 2; }
- }
-
- public string ParameterDescription(int paramIndex)
- {
- if (paramIndex == 1)
+ if (paramIndex == 0)
{
- return "Number of points:";
- }
-
- if (paramIndex == 2)
- {
- return "Distribution:";
- }
-
- return "";
- }
-
- public string ParameterDescription(int paramIndex, double paramValue)
- {
- if (paramIndex == 1)
- {
- int numPoints = (int)((5000.0 - 5.0) / 100.0 * paramValue + 5.0);
+ int numPoints = GetParamValueInt(paramIndex, paramValue);
numPoints = (numPoints / 10) * 10;
if (numPoints < 5)
@@ -62,7 +45,7 @@ namespace MeshExplorer.Generators
return numPoints.ToString();
}
- if (paramIndex == 2)
+ if (paramIndex == 1)
{
double exp = (paramValue + 10) / 100;
@@ -77,9 +60,9 @@ namespace MeshExplorer.Generators
return "";
}
- public InputGeometry Generate(double param1, double param2, double param3)
+ public override InputGeometry Generate(double param0, double param1, double param2)
{
- int numPoints = (int)((5000.0 - 5.0) / 100.0 * param1 + 5.0);
+ int numPoints = GetParamValueInt(0, param0);
numPoints = (numPoints / 10) * 10;
if (numPoints < 5)
@@ -87,7 +70,7 @@ namespace MeshExplorer.Generators
numPoints = 5;
}
- double exp = (param2 + 10) / 100;
+ double exp = (param1 + 10) / 100;
InputGeometry input = new InputGeometry(numPoints);
@@ -116,10 +99,5 @@ namespace MeshExplorer.Generators
return input;
}
-
- public override string ToString()
- {
- return this.Name;
- }
}
}
diff --git a/Triangle.NET/TestApp/Generators/RingPolygon.cs b/Triangle.NET/TestApp/Generators/RingPolygon.cs
index 00049a1..b508452 100644
--- a/Triangle.NET/TestApp/Generators/RingPolygon.cs
+++ b/Triangle.NET/TestApp/Generators/RingPolygon.cs
@@ -15,60 +15,41 @@ namespace MeshExplorer.Generators
///
/// Generates a ring polygon.
///
- public class RingPolygon : IGenerator
+ public class RingPolygon : BaseGenerator
{
- public string Name
+ public RingPolygon()
{
- get { return "Ring"; }
+ name = "Ring";
+ description = "";
+ parameter = 2;
+
+ descriptions[0] = "Number of points:";
+ descriptions[1] = "Variation:";
+
+ ranges[0] = new int[] { 50, 250 };
+ ranges[1] = new int[] { 0, 1 };
}
- public string Description
+ public override string ParameterDescription(int paramIndex, double paramValue)
{
- get { return ""; }
- }
-
- public int ParameterCount
- {
- get { return 2; }
- }
-
- public string ParameterDescription(int paramIndex)
- {
- if (paramIndex == 1)
+ if (paramIndex == 0)
{
- return "Number of points:";
- }
-
- if (paramIndex == 2)
- {
- return "Variation:";
- }
-
- return "";
- }
-
- public string ParameterDescription(int paramIndex, double paramValue)
- {
- if (paramIndex == 1)
- {
- int numRays = (int)((250.0 - 50.0) / 100.0 * paramValue + 50.0);
-
+ int numRays = GetParamValueInt(paramIndex, paramValue);
return numRays.ToString();
}
- if (paramIndex == 2)
+ if (paramIndex == 1)
{
- double variation = (paramValue + 1) / 100;
-
+ double variation = GetParamValueDouble(paramIndex, paramValue);
return variation.ToString("0.0", Util.Nfi);
}
return "";
}
- public InputGeometry Generate(double param1, double param2, double param3)
+ public override InputGeometry Generate(double param0, double param1, double param2)
{
- int n = (int)((250.0 - 50.0) / 100.0 * param1 + 50.0);
+ int n = GetParamValueInt(0, param0);
int m = n / 2;
InputGeometry input = new InputGeometry(n + 1);
@@ -96,7 +77,7 @@ namespace MeshExplorer.Generators
if (i % 2 == 0)
{
- ro = r + r * Util.Random.NextDouble() * (param2 / 100);
+ ro = r + r * Util.Random.NextDouble() * (param1 / 100);
}
input.AddPoint(ro * Math.Cos(i * step + offset), ro * Math.Sin(i * step + offset));
diff --git a/Triangle.NET/TestApp/Generators/StarInBox.cs b/Triangle.NET/TestApp/Generators/StarInBox.cs
index ecc40ea..3358072 100644
--- a/Triangle.NET/TestApp/Generators/StarInBox.cs
+++ b/Triangle.NET/TestApp/Generators/StarInBox.cs
@@ -15,48 +15,22 @@ namespace MeshExplorer.Generators
///
/// Generates a star contained in a box.
///
- public class StarInBox : IGenerator
+ public class StarInBox : BaseGenerator
{
- public string Name
+ public StarInBox()
{
- get { return "Star in Box"; }
+ name = "Star in Box";
+ description = "";
+ parameter = 1;
+
+ descriptions[0] = "Number of rays:";
+
+ ranges[0] = new int[] { 3, 61 };
}
- public string Description
+ public override InputGeometry Generate(double param0, double param1, double param2)
{
- get { return ""; }
- }
-
- public int ParameterCount
- {
- get { return 1; }
- }
-
- public string ParameterDescription(int paramIndex)
- {
- if (paramIndex == 1)
- {
- return "Number of rays:";
- }
-
- return "";
- }
-
- public string ParameterDescription(int paramIndex, double paramValue)
- {
- if (paramIndex == 1)
- {
- int numRays = (int)((61.0 - 3.0) / 100.0 * paramValue + 3.0);
-
- return numRays.ToString();
- }
-
- return "";
- }
-
- public InputGeometry Generate(double param1, double param2, double param3)
- {
- int numRays = (int)((61.0 - 3.0) / 100.0 * param1 + 3.0);
+ int numRays = GetParamValueInt(0, param0);
InputGeometry input = new InputGeometry(numRays + 4);
@@ -88,10 +62,5 @@ namespace MeshExplorer.Generators
return input;
}
-
- public override string ToString()
- {
- return this.Name;
- }
}
}
diff --git a/Triangle.NET/TestApp/Mesh Explorer.csproj b/Triangle.NET/TestApp/Mesh Explorer.csproj
index d174071..3788f9e 100644
--- a/Triangle.NET/TestApp/Mesh Explorer.csproj
+++ b/Triangle.NET/TestApp/Mesh Explorer.csproj
@@ -96,6 +96,7 @@
FormMain.cs
+
diff --git a/Triangle.NET/Triangle/Algorithm/Dwyer.cs b/Triangle.NET/Triangle/Algorithm/Dwyer.cs
index ebf83df..335729d 100644
--- a/Triangle.NET/Triangle/Algorithm/Dwyer.cs
+++ b/Triangle.NET/Triangle/Algorithm/Dwyer.cs
@@ -756,13 +756,13 @@ namespace TriangleNet.Algorithm
divider = vertices >> 1;
// Recursively triangulate each half.
DivconqRecurse(left, left + divider - 1, 1 - axis, ref farleft, ref innerleft);
- ///dbgWriter.Write();
+ //DebugWriter.Session.Write(mesh, true);
DivconqRecurse(left + divider, right, 1 - axis, ref innerright, ref farright);
- ///dbgWriter.Write();
+ //DebugWriter.Session.Write(mesh, true);
// Merge the two triangulations into one.
MergeHulls(ref farleft, ref innerleft, ref innerright, ref farright, axis);
- ///dbgWriter.Write();
+ //DebugWriter.Session.Write(mesh, true);
}
}
@@ -838,6 +838,8 @@ namespace TriangleNet.Algorithm
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];
@@ -888,7 +890,9 @@ namespace TriangleNet.Algorithm
// Form the Delaunay triangulation.
DivconqRecurse(0, i-1, 0, ref hullleft, ref hullright);
- //trifree((VOID*)sortarray);
+
+ //DebugWriter.Session.Write(mesh);
+ //DebugWriter.Session.Finish();
return RemoveGhosts(ref hullleft);
}
diff --git a/Triangle.NET/Triangle/Carver.cs b/Triangle.NET/Triangle/Carver.cs
index 1e51d3c..12e20f7 100644
--- a/Triangle.NET/Triangle/Carver.cs
+++ b/Triangle.NET/Triangle/Carver.cs
@@ -332,7 +332,7 @@ namespace TriangleNet
if (Primitives.CounterClockwise(searchorg, searchdest, hole) > 0.0)
{
// Find a triangle that contains the hole.
- intersect = mesh.Locate(hole, ref searchtri);
+ intersect = mesh.locator.Locate(hole, ref searchtri);
if ((intersect != LocateResult.Outside) && (!searchtri.IsInfected()))
{
// Infect the triangle. This is done by marking the triangle
@@ -374,7 +374,7 @@ namespace TriangleNet
if (Primitives.CounterClockwise(searchorg, searchdest, region.point) > 0.0)
{
// Find a triangle that contains the region point.
- intersect = mesh.Locate(region.point, ref searchtri);
+ intersect = mesh.locator.Locate(region.point, ref searchtri);
if ((intersect != LocateResult.Outside) && (!searchtri.IsInfected()))
{
// Record the triangle for processing after the
diff --git a/Triangle.NET/Triangle/Geometry/BoundingBox.cs b/Triangle.NET/Triangle/Geometry/BoundingBox.cs
index 1f12aa4..b3f30ae 100644
--- a/Triangle.NET/Triangle/Geometry/BoundingBox.cs
+++ b/Triangle.NET/Triangle/Geometry/BoundingBox.cs
@@ -103,6 +103,19 @@ namespace TriangleNet.Geometry
ymax = Math.Max(ymax, y);
}
+ ///
+ /// Scale bounds.
+ ///
+ /// Add dx to left and right bounds.
+ /// Add dy to top and bottom bounds.
+ public void Scale(double dx, double dy)
+ {
+ xmin -= dx;
+ xmax += dx;
+ ymin -= dy;
+ ymax += dy;
+ }
+
///
/// Check if given point is inside bounding box.
///
diff --git a/Triangle.NET/Triangle/IO/DataReader.cs b/Triangle.NET/Triangle/IO/DataReader.cs
index 78b6901..6ef37be 100644
--- a/Triangle.NET/Triangle/IO/DataReader.cs
+++ b/Triangle.NET/Triangle/IO/DataReader.cs
@@ -75,6 +75,7 @@ namespace TriangleNet.IO
int numberofsegments = input.segments.Count;
mesh.inelements = elements;
+ mesh.regions.AddRange(input.regions);
// Create the triangles.
for (i = 0; i < mesh.inelements; i++)
diff --git a/Triangle.NET/Triangle/IO/DebugWriter.cs b/Triangle.NET/Triangle/IO/DebugWriter.cs
index 2948f19..09da57a 100644
--- a/Triangle.NET/Triangle/IO/DebugWriter.cs
+++ b/Triangle.NET/Triangle/IO/DebugWriter.cs
@@ -7,9 +7,12 @@
namespace TriangleNet.IO
{
using System;
- using System.IO;
using System.Globalization;
+ using System.IO;
+ using System.IO.Compression;
+ using System.Text;
using TriangleNet.Data;
+ using TriangleNet.Geometry;
///
/// Writes a the current mesh into a text file.
@@ -18,147 +21,243 @@ namespace TriangleNet.IO
/// File format:
///
/// num_nodes
- /// nid_1 nx ny mark
+ /// id_1 nx ny mark
/// ...
- /// nid_n nx ny mark
+ /// id_n nx ny mark
///
/// num_segs
- /// sid_1 p1 p2 mark
+ /// id_1 p1 p2 mark
/// ...
- /// sid_n p1 p2 mark
+ /// id_n p1 p2 mark
///
/// num_tris
- /// tid_1 p1 p2 p3 n1 n2 n3
+ /// id_1 p1 p2 p3 n1 n2 n3
/// ...
- /// tid_n p1 p2 p3 n1 n2 n3
+ /// id_n p1 p2 p3 n1 n2 n3
///
class DebugWriter
{
static NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat;
- Mesh mesh;
int iteration;
- string name;
+ string session;
+ StreamWriter stream;
+ string tmpFile;
+ int[] vertices;
+ int triangles;
- public DebugWriter(Mesh mesh)
+ #region Singleton pattern
+
+ private static readonly DebugWriter instance = new DebugWriter();
+
+ // Explicit static constructor to tell C# compiler
+ // not to mark type as beforefieldinit
+ static DebugWriter() { }
+
+ private DebugWriter() { }
+
+ public static DebugWriter Session
{
- this.mesh = mesh;
-
- this.iteration = 0;
- this.name = "debug-{0}.mesh";
+ get
+ {
+ return instance;
+ }
}
+ #endregion
+
///
/// Start a new session with given name.
///
/// Name of the session (and output files).
- public void NewSession(string name)
+ public void Start(string session)
{
this.iteration = 0;
- this.name = name + "-{0}.mesh";
+ this.session = session;
+
+ if (this.stream != null)
+ {
+ throw new Exception("A session is active. Finish before starting a new.");
+ }
+
+ this.tmpFile = Path.GetTempFileName();
+ this.stream = new StreamWriter(tmpFile);
}
-
+
///
- /// Write complete mesh to a .mesh file.
+ /// Write complete mesh to file.
///
- public void Write()
+ public void Write(Mesh mesh, bool skip = false)
{
+ this.WriteMesh(mesh, skip);
+
+ this.triangles = mesh.Triangles.Count;
+ }
+
+ ///
+ /// Finish this session.
+ ///
+ public void Finish()
+ {
+ this.Finish(session + ".mshx");
+ }
+
+ private void Finish(string path)
+ {
+ if (stream != null)
+ {
+ stream.Flush();
+ stream.Dispose();
+ stream = null;
+
+ string header = "#!N" + this.iteration + Environment.NewLine;
+
+ using (var gzFile = new FileStream(path, FileMode.Create))
+ {
+ using (var gzStream = new GZipStream(gzFile, CompressionMode.Compress, false))
+ {
+ byte[] bytes = Encoding.UTF8.GetBytes(header);
+ gzStream.Write(bytes, 0, bytes.Length);
+
+ // TODO: read with stream
+ bytes = File.ReadAllBytes(tmpFile);
+ gzStream.Write(bytes, 0, bytes.Length);
+ }
+ }
+
+ File.Delete(this.tmpFile);
+ }
+ }
+
+ private void WriteGeometry(InputGeometry geometry)
+ {
+ stream.WriteLine("#!G{0}", this.iteration++);
+ }
+
+ private void WriteMesh(Mesh mesh, bool skip)
+ {
+ // Mesh may have changed, but we choose to skip
+ if (triangles == mesh.triangles.Count && skip)
+ {
+ return;
+ }
+
+ // Header line
+ stream.WriteLine("#!M{0}", this.iteration++);
+
Vertex p1, p2, p3;
- string file = String.Format(name, iteration++);
-
- using (StreamWriter writer = new StreamWriter(file))
+ if (VerticesChanged(mesh))
{
+ HashVertices(mesh);
+
// Number of vertices.
- writer.WriteLine("{0}", mesh.vertices.Count);
+ stream.WriteLine("{0}", mesh.vertices.Count);
foreach (var v in mesh.vertices.Values)
{
// Vertex number, x and y coordinates and marker.
- writer.WriteLine("{0} {1} {2} {3}", v.hash, v.x.ToString(nfi), v.y.ToString(nfi), v.mark);
+ stream.WriteLine("{0} {1} {2} {3}", v.hash, v.x.ToString(nfi), v.y.ToString(nfi), v.mark);
}
+ }
+ else
+ {
+ stream.WriteLine("0");
+ }
- // Number of segments.
- writer.WriteLine("{0}", mesh.subsegs.Count);
+ // Number of segments.
+ stream.WriteLine("{0}", mesh.subsegs.Count);
- Osub subseg = default(Osub);
- subseg.orient = 0;
+ Osub subseg = default(Osub);
+ subseg.orient = 0;
- foreach (var item in mesh.subsegs.Values)
+ foreach (var item in mesh.subsegs.Values)
+ {
+ if (item.hash <= 0)
{
- if (item.hash <= 0)
- {
- continue;
- }
-
- subseg.seg = item;
-
- p1 = subseg.Org();
- p2 = subseg.Dest();
-
- // Segment number, indices of its two endpoints, and marker.
- writer.WriteLine("{0} {1} {2} {3}", subseg.seg.hash, p1.hash, p2.hash, subseg.seg.boundary);
+ continue;
}
- Otri tri = default(Otri), trisym = default(Otri);
+ subseg.seg = item;
+
+ p1 = subseg.Org();
+ p2 = subseg.Dest();
+
+ // Segment number, indices of its two endpoints, and marker.
+ stream.WriteLine("{0} {1} {2} {3}", subseg.seg.hash, p1.hash, p2.hash, subseg.seg.boundary);
+ }
+
+ Otri tri = default(Otri), trisym = default(Otri);
+ tri.orient = 0;
+
+ int n1, n2, n3, h1, h2, h3;
+
+ // Number of triangles.
+ stream.WriteLine("{0}", mesh.triangles.Count);
+
+ foreach (var item in mesh.triangles.Values)
+ {
+ tri.triangle = item;
+
+ p1 = tri.Org();
+ p2 = tri.Dest();
+ p3 = tri.Apex();
+
+ h1 = (p1 == null) ? -1 : p1.hash;
+ h2 = (p2 == null) ? -1 : p2.hash;
+ h3 = (p3 == null) ? -1 : p3.hash;
+
+ // Triangle number, indices for three vertices.
+ stream.Write("{0} {1} {2} {3}", tri.triangle.hash, h1, h2, h3);
+
+ tri.orient = 1;
+ tri.Sym(ref trisym);
+ n1 = trisym.triangle.hash;
+
+ tri.orient = 2;
+ tri.Sym(ref trisym);
+ n2 = trisym.triangle.hash;
+
tri.orient = 0;
+ tri.Sym(ref trisym);
+ n3 = trisym.triangle.hash;
- int n1, n2, n3, hash3;
+ // Neighboring triangle numbers.
+ stream.WriteLine(" {0} {1} {2}", n1, n2, n3);
+ }
+ }
- // Number of triangles.
- writer.WriteLine("{0}", mesh.triangles.Count);
+ private bool VerticesChanged(Mesh mesh)
+ {
+ if (vertices == null || mesh.Vertices.Count != vertices.Length)
+ {
+ return true;
+ }
- foreach (var item in mesh.triangles.Values)
+ int i = 0;
+ foreach (var v in mesh.Vertices)
+ {
+ if (v.id != vertices[i++])
{
- if (item.hash <= 0)
- {
- continue;
- }
-
- tri.triangle = item;
-
- p1 = tri.Org();
- p2 = tri.Dest();
- p3 = tri.Apex();
-
- if (p3 == null)
- {
- if (p1 == null || p2 == null)
- {
- continue;
- }
-
- hash3 = -1;
- }
- else
- {
- hash3 = p3.hash;
- }
-
- if (p1 == null || p2 == null)
- {
- continue;
- }
-
- // Triangle number, indices for three vertices.
- writer.Write("{0} {1} {2} {3}", tri.triangle.hash, p1.hash, p2.hash, hash3);
-
- tri.orient = 1;
- tri.Sym(ref trisym);
- n1 = trisym.triangle.hash;
-
- tri.orient = 2;
- tri.Sym(ref trisym);
- n2 = trisym.triangle.hash;
-
- tri.orient = 0;
- tri.Sym(ref trisym);
- n3 = trisym.triangle.hash;
-
- // Neighboring triangle numbers.
- writer.WriteLine(" {0} {1} {2}", n1, n2, n3);
+ return true;
}
}
+
+ return false;
+ }
+
+ private void HashVertices(Mesh mesh)
+ {
+ if (vertices == null || mesh.Vertices.Count != vertices.Length)
+ {
+ vertices = new int[mesh.Vertices.Count];
+ }
+
+ int i = 0;
+ foreach (var v in mesh.Vertices)
+ {
+ vertices[i++] = v.id;
+ }
}
}
}
diff --git a/Triangle.NET/Triangle/Mesh.cs b/Triangle.NET/Triangle/Mesh.cs
index 5b8e00a..aaa353f 100644
--- a/Triangle.NET/Triangle/Mesh.cs
+++ b/Triangle.NET/Triangle/Mesh.cs
@@ -28,7 +28,6 @@ namespace TriangleNet
ILog logger;
Quality quality;
- Sampler sampler;
// Stack that maintains a list of recently flipped triangles.
Stack flipstack;
@@ -73,9 +72,7 @@ namespace TriangleNet
// that isn't really connected to a subsegment at that location.
internal static Segment dummysub;
- // Pointer to a recently visited triangle. Improves point location if
- // proximate vertices are inserted sequentially.
- internal Otri recenttri;
+ internal TriangleLocator locator;
// Controls the behavior of the mesh instance.
internal Behavior behavior;
@@ -174,7 +171,7 @@ namespace TriangleNet
}
#endregion
-
+
///
/// Initializes a new instance of the class.
///
@@ -205,7 +202,7 @@ namespace TriangleNet
quality = new Quality(this);
- sampler = new Sampler();
+ locator = new TriangleLocator(this);
Primitives.ExactInit();
@@ -614,7 +611,7 @@ namespace TriangleNet
Reset();
- sampler.Reset();
+ locator.Reset();
}
///
@@ -624,7 +621,6 @@ namespace TriangleNet
{
numbering = NodeNumbering.None;
- recenttri.triangle = null; // No triangle has been visited yet.
undeads = 0; // No eliminated input vertices yet.
checksegments = false; // There are no segments in the triangulation yet.
checkquality = false; // The quality triangulation stage has not begun.
@@ -890,13 +886,13 @@ namespace TriangleNet
horiz.orient = 0;
horiz.SymSelf();
// Search for a triangle containing 'newvertex'.
- intersect = Locate(newvertex, ref horiz);
+ intersect = locator.Locate(newvertex, ref horiz);
}
else
{
// Start searching from the triangle provided by the caller.
searchtri.Copy(ref horiz);
- intersect = PreciseLocate(newvertex, ref horiz, true);
+ intersect = locator.PreciseLocate(newvertex, ref horiz, true);
}
}
else
@@ -912,7 +908,7 @@ namespace TriangleNet
// There's already a vertex there. Return in 'searchtri' a triangle
// whose origin is the existing vertex.
horiz.Copy(ref searchtri);
- horiz.Copy(ref recenttri);
+ locator.Update(ref horiz);
return InsertVertexResult.Duplicate;
}
if ((intersect == LocateResult.OnEdge) || (intersect == LocateResult.Outside))
@@ -949,7 +945,7 @@ namespace TriangleNet
// Return a handle whose primary edge contains the vertex,
// which has not been inserted.
horiz.Copy(ref searchtri);
- horiz.Copy(ref recenttri);
+ locator.Update(ref horiz);
return InsertVertexResult.Violating;
}
}
@@ -1360,7 +1356,11 @@ namespace TriangleNet
{
// We're done. Return a triangle whose origin is the new vertex.
horiz.Lnext(ref searchtri);
+
+ Otri recenttri = default(Otri);
horiz.Lnext(ref recenttri);
+ locator.Update(ref recenttri);
+
return success;
}
// Finish finding the next edge around the newly inserted vertex.
@@ -1982,307 +1982,6 @@ namespace TriangleNet
#endregion
- #region Location
-
- ///
- /// Find a triangle or edge containing a given point.
- ///
- /// The point to locate.
- /// The triangle to start the search at.
- /// If 'stopatsubsegment' is set, the search
- /// will stop if it tries to walk through a subsegment, and will return OUTSIDE.
- /// Location information.
- ///
- /// Begins its search from 'searchtri'. It is important that 'searchtri'
- /// be a handle with the property that 'searchpoint' is strictly to the left
- /// of the edge denoted by 'searchtri', or is collinear with that edge and
- /// does not intersect that edge. (In particular, 'searchpoint' should not
- /// be the origin or destination of that edge.)
- ///
- /// These conditions are imposed because preciselocate() is normally used in
- /// one of two situations:
- ///
- /// (1) To try to find the location to insert a new point. Normally, we
- /// know an edge that the point is strictly to the left of. In the
- /// incremental Delaunay algorithm, that edge is a bounding box edge.
- /// In Ruppert's Delaunay refinement algorithm for quality meshing,
- /// that edge is the shortest edge of the triangle whose circumcenter
- /// is being inserted.
- ///
- /// (2) To try to find an existing point. In this case, any edge on the
- /// convex hull is a good starting edge. You must screen out the
- /// possibility that the vertex sought is an endpoint of the starting
- /// edge before you call preciselocate().
- ///
- /// On completion, 'searchtri' is a triangle that contains 'searchpoint'.
- ///
- /// This implementation differs from that given by Guibas and Stolfi. It
- /// walks from triangle to triangle, crossing an edge only if 'searchpoint'
- /// is on the other side of the line containing that edge. After entering
- /// a triangle, there are two edges by which one can leave that triangle.
- /// If both edges are valid ('searchpoint' is on the other side of both
- /// edges), one of the two is chosen by drawing a line perpendicular to
- /// the entry edge (whose endpoints are 'forg' and 'fdest') passing through
- /// 'fapex'. Depending on which side of this perpendicular 'searchpoint'
- /// falls on, an exit edge is chosen.
- ///
- /// This implementation is empirically faster than the Guibas and Stolfi
- /// point location routine (which I originally used), which tends to spiral
- /// in toward its target.
- ///
- /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
- /// is a handle whose origin is the existing vertex.
- ///
- /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
- /// handle whose primary edge is the edge on which the point lies.
- ///
- /// Returns INTRIANGLE if the point lies strictly within a triangle.
- /// 'searchtri' is a handle on the triangle that contains the point.
- ///
- /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
- /// handle whose primary edge the point is to the right of. This might
- /// occur when the circumcenter of a triangle falls just slightly outside
- /// the mesh due to floating-point roundoff error. It also occurs when
- /// seeking a hole or region point that a foolish user has placed outside
- /// the mesh.
- ///
- /// WARNING: This routine is designed for convex triangulations, and will
- /// not generally work after the holes and concavities have been carved.
- /// However, it can still be used to find the circumcenter of a triangle, as
- /// long as the search is begun from the triangle in question.
- internal LocateResult PreciseLocate(Point searchpoint, ref Otri searchtri,
- bool stopatsubsegment)
- {
- Otri backtracktri = default(Otri);
- Osub checkedge = default(Osub);
- Vertex forg, fdest, fapex;
- double orgorient, destorient;
- bool moveleft;
-
- // Where are we?
- forg = searchtri.Org();
- fdest = searchtri.Dest();
- fapex = searchtri.Apex();
- while (true)
- {
- // Check whether the apex is the point we seek.
- if ((fapex.x == searchpoint.X) && (fapex.y == searchpoint.Y))
- {
- searchtri.LprevSelf();
- return LocateResult.OnVertex;
- }
- // Does the point lie on the other side of the line defined by the
- // triangle edge opposite the triangle's destination?
- destorient = Primitives.CounterClockwise(forg, fapex, searchpoint);
- // Does the point lie on the other side of the line defined by the
- // triangle edge opposite the triangle's origin?
- orgorient = Primitives.CounterClockwise(fapex, fdest, searchpoint);
- if (destorient > 0.0)
- {
- if (orgorient > 0.0)
- {
- // Move left if the inner product of (fapex - searchpoint) and
- // (fdest - forg) is positive. This is equivalent to drawing
- // a line perpendicular to the line (forg, fdest) and passing
- // through 'fapex', and determining which side of this line
- // 'searchpoint' falls on.
- moveleft = (fapex.x - searchpoint.X) * (fdest.x - forg.x) +
- (fapex.y - searchpoint.Y) * (fdest.y - forg.y) > 0.0;
- }
- else
- {
- moveleft = true;
- }
- }
- else
- {
- if (orgorient > 0.0)
- {
- moveleft = false;
- }
- else
- {
- // The point we seek must be on the boundary of or inside this
- // triangle.
- if (destorient == 0.0)
- {
- searchtri.LprevSelf();
- return LocateResult.OnEdge;
- }
- if (orgorient == 0.0)
- {
- searchtri.LnextSelf();
- return LocateResult.OnEdge;
- }
- return LocateResult.InTriangle;
- }
- }
-
- // Move to another triangle. Leave a trace 'backtracktri' in case
- // floating-point roundoff or some such bogey causes us to walk
- // off a boundary of the triangulation.
- if (moveleft)
- {
- searchtri.Lprev(ref backtracktri);
- fdest = fapex;
- }
- else
- {
- searchtri.Lnext(ref backtracktri);
- forg = fapex;
- }
- backtracktri.Sym(ref searchtri);
-
- if (checksegments && stopatsubsegment)
- {
- // Check for walking through a subsegment.
- backtracktri.SegPivot(ref checkedge);
- if (checkedge.seg != dummysub)
- {
- // Go back to the last triangle.
- backtracktri.Copy(ref searchtri);
- return LocateResult.Outside;
- }
- }
- // Check for walking right out of the triangulation.
- if (searchtri.triangle == dummytri)
- {
- // Go back to the last triangle.
- backtracktri.Copy(ref searchtri);
- return LocateResult.Outside;
- }
-
- fapex = searchtri.Apex();
- }
- }
-
- ///
- /// Find a triangle or edge containing a given point.
- ///
- /// The point to locate.
- /// The triangle to start the search at.
- /// Location information.
- ///
- /// Searching begins from one of: the input 'searchtri', a recently
- /// encountered triangle 'recenttri', or from a triangle chosen from a
- /// random sample. The choice is made by determining which triangle's
- /// origin is closest to the point we are searching for. Normally,
- /// 'searchtri' should be a handle on the convex hull of the triangulation.
- ///
- /// Details on the random sampling method can be found in the Mucke, Saias,
- /// and Zhu paper cited in the header of this code.
- ///
- /// On completion, 'searchtri' is a triangle that contains 'searchpoint'.
- ///
- /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
- /// is a handle whose origin is the existing vertex.
- ///
- /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
- /// handle whose primary edge is the edge on which the point lies.
- ///
- /// Returns INTRIANGLE if the point lies strictly within a triangle.
- /// 'searchtri' is a handle on the triangle that contains the point.
- ///
- /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
- /// handle whose primary edge the point is to the right of. This might
- /// occur when the circumcenter of a triangle falls just slightly outside
- /// the mesh due to floating-point roundoff error. It also occurs when
- /// seeking a hole or region point that a foolish user has placed outside
- /// the mesh.
- ///
- /// WARNING: This routine is designed for convex triangulations, and will
- /// not generally work after the holes and concavities have been carved.
- ///
- internal LocateResult Locate(Point searchpoint, ref Otri searchtri)
- {
- Otri sampletri = default(Otri);
- Vertex torg, tdest;
- double searchdist, dist;
- double ahead;
-
- // Record the distance from the suggested starting triangle to the
- // point we seek.
- torg = searchtri.Org();
- searchdist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) +
- (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y);
-
- // If a recently encountered triangle has been recorded and has not been
- // deallocated, test it as a good starting point.
- if (recenttri.triangle != null)
- {
- if (!Otri.IsDead(recenttri.triangle))
- {
- torg = recenttri.Org();
- if ((torg.x == searchpoint.X) && (torg.y == searchpoint.Y))
- {
- recenttri.Copy(ref searchtri);
- return LocateResult.OnVertex;
- }
- dist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) +
- (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y);
- if (dist < searchdist)
- {
- recenttri.Copy(ref searchtri);
- searchdist = dist;
- }
- }
- }
-
- // TODO: Improve sampling.
- sampler.Update(this);
- int[] samples = sampler.GetSamples(this);
-
- foreach (var key in samples)
- {
- sampletri.triangle = this.triangles[key];
- if (!Otri.IsDead(sampletri.triangle))
- {
- torg = sampletri.Org();
- dist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) +
- (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y);
- if (dist < searchdist)
- {
- sampletri.Copy(ref searchtri);
- searchdist = dist;
- }
- }
- }
-
- // Where are we?
- torg = searchtri.Org();
- tdest = searchtri.Dest();
- // Check the starting triangle's vertices.
- if ((torg.x == searchpoint.X) && (torg.y == searchpoint.Y))
- {
- return LocateResult.OnVertex;
- }
- if ((tdest.x == searchpoint.X) && (tdest.y == searchpoint.Y))
- {
- searchtri.LnextSelf();
- return LocateResult.OnVertex;
- }
- // Orient 'searchtri' to fit the preconditions of calling preciselocate().
- ahead = Primitives.CounterClockwise(torg, tdest, searchpoint);
- if (ahead < 0.0)
- {
- // Turn around so that 'searchpoint' is to the left of the
- // edge specified by 'searchtri'.
- searchtri.SymSelf();
- }
- else if (ahead == 0.0)
- {
- // Check if 'searchpoint' is between 'torg' and 'tdest'.
- if (((torg.x < searchpoint.X) == (searchpoint.X < tdest.x)) &&
- ((torg.y < searchpoint.Y) == (searchpoint.Y < tdest.y)))
- {
- return LocateResult.OnEdge;
- }
- }
- return PreciseLocate(searchpoint, ref searchtri, false);
- }
-
- #endregion
-
#region Segment insertion
///
@@ -2850,14 +2549,14 @@ namespace TriangleNet
searchtri1.orient = 0;
searchtri1.SymSelf();
// Search for the segment's first endpoint by point location.
- if (Locate(endpoint1, ref searchtri1) != LocateResult.OnVertex)
+ if (locator.Locate(endpoint1, ref searchtri1) != LocateResult.OnVertex)
{
logger.Error("Unable to locate PSLG vertex in triangulation.", "Mesh.InsertSegment().1");
throw new Exception("Unable to locate PSLG vertex in triangulation.");
}
}
// Remember this triangle to improve subsequent point location.
- searchtri1.Copy(ref recenttri);
+ locator.Update(ref searchtri1);
// Scout the beginnings of a path from the first endpoint
// toward the second.
@@ -2884,14 +2583,14 @@ namespace TriangleNet
searchtri2.orient = 0;
searchtri2.SymSelf();
// Search for the segment's second endpoint by point location.
- if (Locate(endpoint2, ref searchtri2) != LocateResult.OnVertex)
+ if (locator.Locate(endpoint2, ref searchtri2) != LocateResult.OnVertex)
{
logger.Error("Unable to locate PSLG vertex in triangulation.", "Mesh.InsertSegment().2");
throw new Exception("Unable to locate PSLG vertex in triangulation.");
}
}
// Remember this triangle to improve subsequent point location.
- searchtri2.Copy(ref recenttri);
+ locator.Update(ref searchtri2);
// Scout the beginnings of a path from the second endpoint
// toward the first.
if (ScoutSegment(ref searchtri2, endpoint1, newmark))
diff --git a/Triangle.NET/Triangle/NewLocation.cs b/Triangle.NET/Triangle/NewLocation.cs
index 4018ba0..25ccd86 100644
--- a/Triangle.NET/Triangle/NewLocation.cs
+++ b/Triangle.NET/Triangle/NewLocation.cs
@@ -4079,7 +4079,7 @@ namespace TriangleNet
// edge specified by 'searchtri'.
searchtri.SymSelf();
searchtri.Copy(ref horiz);
- intersect = mesh.PreciseLocate(newvertex, ref horiz, false);
+ intersect = mesh.locator.PreciseLocate(newvertex, ref horiz, false);
}
else if (ahead == 0.0)
{
@@ -4095,7 +4095,7 @@ namespace TriangleNet
else
{
searchtri.Copy(ref horiz);
- intersect = mesh.PreciseLocate(newvertex, ref horiz, false);
+ intersect = mesh.locator.PreciseLocate(newvertex, ref horiz, false);
}
}
if (intersect == LocateResult.OnVertex || intersect == LocateResult.Outside)
diff --git a/Triangle.NET/Triangle/Quality.cs b/Triangle.NET/Triangle/Quality.cs
index 5884f43..bc41819 100644
--- a/Triangle.NET/Triangle/Quality.cs
+++ b/Triangle.NET/Triangle/Quality.cs
@@ -123,6 +123,17 @@ namespace TriangleNet
}
}
+ // Check for unconnected vertices
+ mesh.MakeVertexMap();
+ foreach (var v in mesh.vertices.Values)
+ {
+ if (v.tri.triangle == null)
+ {
+ logger.Warning("Vertex (ID " + v.id + ") not connected to mesh (duplicate input vertex?)",
+ "Quality.CheckMesh()");
+ }
+ }
+
if (horrors == 0) // && Behavior.Verbose
{
logger.Info("Mesh topology appears to be consistent.");
diff --git a/Triangle.NET/Triangle/Tools/BoundedVoronoi.cs b/Triangle.NET/Triangle/Tools/BoundedVoronoi.cs
index 5b357e0..91c0156 100644
--- a/Triangle.NET/Triangle/Tools/BoundedVoronoi.cs
+++ b/Triangle.NET/Triangle/Tools/BoundedVoronoi.cs
@@ -413,13 +413,17 @@ namespace TriangleNet.Tools
f.Onext(ref f_next);
}
- // Add vertex on border
- p = new Point(vertex.x, vertex.y);
- p.id = n + segIndex;
- points[n + segIndex] = p;
- segIndex++;
+ if (f_prev.triangle == Mesh.dummytri)
+ {
+ // For vertices on the domain boundaray, add the vertex. For
+ // internal boundaries don't add it.
+ p = new Point(vertex.x, vertex.y);
+ p.id = n + segIndex;
+ points[n + segIndex] = p;
+ segIndex++;
- vpoints.Add(p);
+ vpoints.Add(p);
+ }
// Add midpoint of start triangles' edge.
torg = f.Org();
diff --git a/Triangle.NET/Triangle/Tools/Voronoi.cs b/Triangle.NET/Triangle/Tools/Voronoi.cs
index c1e6919..6f0679f 100644
--- a/Triangle.NET/Triangle/Tools/Voronoi.cs
+++ b/Triangle.NET/Triangle/Tools/Voronoi.cs
@@ -5,9 +5,10 @@
//
// -----------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
using TriangleNet.Data;
using TriangleNet.Geometry;
-using System.Collections.Generic;
namespace TriangleNet.Tools
{
@@ -21,9 +22,13 @@ namespace TriangleNet.Tools
Point[] points;
List regions;
+ // Stores the endpoints of rays of infinite Voronoi cells
Dictionary rayPoints;
int rayIndex;
+ // Bounding box of the triangles circumcenters.
+ BoundingBox bounds;
+
///
/// Initializes a new instance of the class.
///
@@ -74,11 +79,14 @@ namespace TriangleNet.Tools
this.points = new Point[mesh.triangles.Count + mesh.hullsize];
this.regions = new List(mesh.vertices.Count);
- ComputeCircumCenters();
-
rayPoints = new Dictionary();
rayIndex = 0;
+ bounds = new BoundingBox();
+
+ // Compute triangles circumcenters and setup bounding box
+ ComputeCircumCenters();
+
// Loop over the mesh vertices (Voronoi generators).
foreach (var item in mesh.vertices.Values)
{
@@ -104,11 +112,16 @@ namespace TriangleNet.Tools
pt.id = item.id;
points[item.id] = pt;
+
+ bounds.Update(pt.x, pt.y);
}
+
+ double ds = Math.Max(bounds.Width, bounds.Height);
+ bounds.Scale(ds, ds);
}
///
- ///
+ /// Construct Voronoi region for given vertex.
///
///
/// The circumcenter indices which make up the cell.
@@ -132,6 +145,7 @@ namespace TriangleNet.Tools
f_init.Copy(ref f);
f_init.Onext(ref f_next);
+ // Check if f_init lies on the boundary of the triangulation.
if (f_next.triangle == Mesh.dummytri)
{
f_init.Oprev(ref f_prev);
@@ -216,9 +230,6 @@ namespace TriangleNet.Tools
f.SegPivot(ref sub);
sid = sub.seg.hash;
- // Last valid f lies at the boundary. Add the circumcenter.
- vpoints.Add(points[f.triangle.id]);
-
if (rayPoints.ContainsKey(sid))
{
vpoints.Add(rayPoints[sid]);
@@ -254,16 +265,11 @@ namespace TriangleNet.Tools
double t1, x1, y1, t2, x2, y2;
- // Bounding box (50% enlarged)
- var box = mesh.Bounds;
-
- double dw = box.Width * 0.5f;
- double dh = box.Height * 0.5f;
-
- double minX = box.Xmin - dw;
- double maxX = box.Xmax + dw;
- double minY = box.Ymin - dh;
- double maxY = box.Ymax + dh;
+ // Bounding box
+ double minX = bounds.Xmin;
+ double maxX = bounds.Xmax;
+ double minY = bounds.Ymin;
+ double maxY = bounds.Ymax;
// Check if point is inside the bounds
if (x < minX || x > maxX || y < minY || y > maxY)
diff --git a/Triangle.NET/Triangle/Triangle.csproj b/Triangle.NET/Triangle/Triangle.csproj
index 0031228..8bbca83 100644
--- a/Triangle.NET/Triangle/Triangle.csproj
+++ b/Triangle.NET/Triangle/Triangle.csproj
@@ -93,6 +93,7 @@
+
diff --git a/Triangle.NET/Triangle/TriangleLocator.cs b/Triangle.NET/Triangle/TriangleLocator.cs
new file mode 100644
index 0000000..1409195
--- /dev/null
+++ b/Triangle.NET/Triangle/TriangleLocator.cs
@@ -0,0 +1,339 @@
+// -----------------------------------------------------------------------
+//
+// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
+// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
+//
+// -----------------------------------------------------------------------
+
+namespace TriangleNet
+{
+ using TriangleNet.Data;
+ using TriangleNet.Geometry;
+
+ ///
+ /// TODO: Update summary.
+ ///
+ class TriangleLocator
+ {
+ Sampler sampler;
+ Mesh mesh;
+
+ // Pointer to a recently visited triangle. Improves point location if
+ // proximate vertices are inserted sequentially.
+ internal Otri recenttri;
+
+ public TriangleLocator(Mesh mesh)
+ {
+ this.mesh = mesh;
+
+ sampler = new Sampler();
+ }
+
+ public void Update(ref Otri otri)
+ {
+ otri.Copy(ref recenttri);
+ }
+
+ public void Reset()
+ {
+ recenttri.triangle = null; // No triangle has been visited yet.
+ }
+
+ ///
+ /// Find a triangle or edge containing a given point.
+ ///
+ /// The point to locate.
+ /// The triangle to start the search at.
+ /// If 'stopatsubsegment' is set, the search
+ /// will stop if it tries to walk through a subsegment, and will return OUTSIDE.
+ /// Location information.
+ ///
+ /// Begins its search from 'searchtri'. It is important that 'searchtri'
+ /// be a handle with the property that 'searchpoint' is strictly to the left
+ /// of the edge denoted by 'searchtri', or is collinear with that edge and
+ /// does not intersect that edge. (In particular, 'searchpoint' should not
+ /// be the origin or destination of that edge.)
+ ///
+ /// These conditions are imposed because preciselocate() is normally used in
+ /// one of two situations:
+ ///
+ /// (1) To try to find the location to insert a new point. Normally, we
+ /// know an edge that the point is strictly to the left of. In the
+ /// incremental Delaunay algorithm, that edge is a bounding box edge.
+ /// In Ruppert's Delaunay refinement algorithm for quality meshing,
+ /// that edge is the shortest edge of the triangle whose circumcenter
+ /// is being inserted.
+ ///
+ /// (2) To try to find an existing point. In this case, any edge on the
+ /// convex hull is a good starting edge. You must screen out the
+ /// possibility that the vertex sought is an endpoint of the starting
+ /// edge before you call preciselocate().
+ ///
+ /// On completion, 'searchtri' is a triangle that contains 'searchpoint'.
+ ///
+ /// This implementation differs from that given by Guibas and Stolfi. It
+ /// walks from triangle to triangle, crossing an edge only if 'searchpoint'
+ /// is on the other side of the line containing that edge. After entering
+ /// a triangle, there are two edges by which one can leave that triangle.
+ /// If both edges are valid ('searchpoint' is on the other side of both
+ /// edges), one of the two is chosen by drawing a line perpendicular to
+ /// the entry edge (whose endpoints are 'forg' and 'fdest') passing through
+ /// 'fapex'. Depending on which side of this perpendicular 'searchpoint'
+ /// falls on, an exit edge is chosen.
+ ///
+ /// This implementation is empirically faster than the Guibas and Stolfi
+ /// point location routine (which I originally used), which tends to spiral
+ /// in toward its target.
+ ///
+ /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
+ /// is a handle whose origin is the existing vertex.
+ ///
+ /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
+ /// handle whose primary edge is the edge on which the point lies.
+ ///
+ /// Returns INTRIANGLE if the point lies strictly within a triangle.
+ /// 'searchtri' is a handle on the triangle that contains the point.
+ ///
+ /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
+ /// handle whose primary edge the point is to the right of. This might
+ /// occur when the circumcenter of a triangle falls just slightly outside
+ /// the mesh due to floating-point roundoff error. It also occurs when
+ /// seeking a hole or region point that a foolish user has placed outside
+ /// the mesh.
+ ///
+ /// WARNING: This routine is designed for convex triangulations, and will
+ /// not generally work after the holes and concavities have been carved.
+ /// However, it can still be used to find the circumcenter of a triangle, as
+ /// long as the search is begun from the triangle in question.
+ public LocateResult PreciseLocate(Point searchpoint, ref Otri searchtri,
+ bool stopatsubsegment)
+ {
+ Otri backtracktri = default(Otri);
+ Osub checkedge = default(Osub);
+ Vertex forg, fdest, fapex;
+ double orgorient, destorient;
+ bool moveleft;
+
+ // Where are we?
+ forg = searchtri.Org();
+ fdest = searchtri.Dest();
+ fapex = searchtri.Apex();
+ while (true)
+ {
+ // Check whether the apex is the point we seek.
+ if ((fapex.x == searchpoint.X) && (fapex.y == searchpoint.Y))
+ {
+ searchtri.LprevSelf();
+ return LocateResult.OnVertex;
+ }
+ // Does the point lie on the other side of the line defined by the
+ // triangle edge opposite the triangle's destination?
+ destorient = Primitives.CounterClockwise(forg, fapex, searchpoint);
+ // Does the point lie on the other side of the line defined by the
+ // triangle edge opposite the triangle's origin?
+ orgorient = Primitives.CounterClockwise(fapex, fdest, searchpoint);
+ if (destorient > 0.0)
+ {
+ if (orgorient > 0.0)
+ {
+ // Move left if the inner product of (fapex - searchpoint) and
+ // (fdest - forg) is positive. This is equivalent to drawing
+ // a line perpendicular to the line (forg, fdest) and passing
+ // through 'fapex', and determining which side of this line
+ // 'searchpoint' falls on.
+ moveleft = (fapex.x - searchpoint.X) * (fdest.x - forg.x) +
+ (fapex.y - searchpoint.Y) * (fdest.y - forg.y) > 0.0;
+ }
+ else
+ {
+ moveleft = true;
+ }
+ }
+ else
+ {
+ if (orgorient > 0.0)
+ {
+ moveleft = false;
+ }
+ else
+ {
+ // The point we seek must be on the boundary of or inside this
+ // triangle.
+ if (destorient == 0.0)
+ {
+ searchtri.LprevSelf();
+ return LocateResult.OnEdge;
+ }
+ if (orgorient == 0.0)
+ {
+ searchtri.LnextSelf();
+ return LocateResult.OnEdge;
+ }
+ return LocateResult.InTriangle;
+ }
+ }
+
+ // Move to another triangle. Leave a trace 'backtracktri' in case
+ // floating-point roundoff or some such bogey causes us to walk
+ // off a boundary of the triangulation.
+ if (moveleft)
+ {
+ searchtri.Lprev(ref backtracktri);
+ fdest = fapex;
+ }
+ else
+ {
+ searchtri.Lnext(ref backtracktri);
+ forg = fapex;
+ }
+ backtracktri.Sym(ref searchtri);
+
+ if (mesh.checksegments && stopatsubsegment)
+ {
+ // Check for walking through a subsegment.
+ backtracktri.SegPivot(ref checkedge);
+ if (checkedge.seg != Mesh.dummysub)
+ {
+ // Go back to the last triangle.
+ backtracktri.Copy(ref searchtri);
+ return LocateResult.Outside;
+ }
+ }
+ // Check for walking right out of the triangulation.
+ if (searchtri.triangle == Mesh.dummytri)
+ {
+ // Go back to the last triangle.
+ backtracktri.Copy(ref searchtri);
+ return LocateResult.Outside;
+ }
+
+ fapex = searchtri.Apex();
+ }
+ }
+
+ ///
+ /// Find a triangle or edge containing a given point.
+ ///
+ /// The point to locate.
+ /// The triangle to start the search at.
+ /// Location information.
+ ///
+ /// Searching begins from one of: the input 'searchtri', a recently
+ /// encountered triangle 'recenttri', or from a triangle chosen from a
+ /// random sample. The choice is made by determining which triangle's
+ /// origin is closest to the point we are searching for. Normally,
+ /// 'searchtri' should be a handle on the convex hull of the triangulation.
+ ///
+ /// Details on the random sampling method can be found in the Mucke, Saias,
+ /// and Zhu paper cited in the header of this code.
+ ///
+ /// On completion, 'searchtri' is a triangle that contains 'searchpoint'.
+ ///
+ /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
+ /// is a handle whose origin is the existing vertex.
+ ///
+ /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
+ /// handle whose primary edge is the edge on which the point lies.
+ ///
+ /// Returns INTRIANGLE if the point lies strictly within a triangle.
+ /// 'searchtri' is a handle on the triangle that contains the point.
+ ///
+ /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
+ /// handle whose primary edge the point is to the right of. This might
+ /// occur when the circumcenter of a triangle falls just slightly outside
+ /// the mesh due to floating-point roundoff error. It also occurs when
+ /// seeking a hole or region point that a foolish user has placed outside
+ /// the mesh.
+ ///
+ /// WARNING: This routine is designed for convex triangulations, and will
+ /// not generally work after the holes and concavities have been carved.
+ ///
+ public LocateResult Locate(Point searchpoint, ref Otri searchtri)
+ {
+ Otri sampletri = default(Otri);
+ Vertex torg, tdest;
+ double searchdist, dist;
+ double ahead;
+
+ // Record the distance from the suggested starting triangle to the
+ // point we seek.
+ torg = searchtri.Org();
+ searchdist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) +
+ (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y);
+
+ // If a recently encountered triangle has been recorded and has not been
+ // deallocated, test it as a good starting point.
+ if (recenttri.triangle != null)
+ {
+ if (!Otri.IsDead(recenttri.triangle))
+ {
+ torg = recenttri.Org();
+ if ((torg.x == searchpoint.X) && (torg.y == searchpoint.Y))
+ {
+ recenttri.Copy(ref searchtri);
+ return LocateResult.OnVertex;
+ }
+ dist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) +
+ (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y);
+ if (dist < searchdist)
+ {
+ recenttri.Copy(ref searchtri);
+ searchdist = dist;
+ }
+ }
+ }
+
+ // TODO: Improve sampling.
+ sampler.Update(mesh);
+ int[] samples = sampler.GetSamples(mesh);
+
+ foreach (var key in samples)
+ {
+ sampletri.triangle = mesh.triangles[key];
+ if (!Otri.IsDead(sampletri.triangle))
+ {
+ torg = sampletri.Org();
+ dist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) +
+ (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y);
+ if (dist < searchdist)
+ {
+ sampletri.Copy(ref searchtri);
+ searchdist = dist;
+ }
+ }
+ }
+
+ // Where are we?
+ torg = searchtri.Org();
+ tdest = searchtri.Dest();
+ // Check the starting triangle's vertices.
+ if ((torg.x == searchpoint.X) && (torg.y == searchpoint.Y))
+ {
+ return LocateResult.OnVertex;
+ }
+ if ((tdest.x == searchpoint.X) && (tdest.y == searchpoint.Y))
+ {
+ searchtri.LnextSelf();
+ return LocateResult.OnVertex;
+ }
+ // Orient 'searchtri' to fit the preconditions of calling preciselocate().
+ ahead = Primitives.CounterClockwise(torg, tdest, searchpoint);
+ if (ahead < 0.0)
+ {
+ // Turn around so that 'searchpoint' is to the left of the
+ // edge specified by 'searchtri'.
+ searchtri.SymSelf();
+ }
+ else if (ahead == 0.0)
+ {
+ // Check if 'searchpoint' is between 'torg' and 'tdest'.
+ if (((torg.x < searchpoint.X) == (searchpoint.X < tdest.x)) &&
+ ((torg.y < searchpoint.Y) == (searchpoint.Y < tdest.y)))
+ {
+ return LocateResult.OnEdge;
+ }
+ }
+ return PreciseLocate(searchpoint, ref searchtri, false);
+ }
+ }
+}