Some fixes for Voronoi code

git-svn-id: https://triangle.svn.codeplex.com/svn@73114 0e2699bc-83d4-4a8f-98e7-55e24ab8c7a5
This commit is contained in:
SND\wo80_cp
2013-06-06 14:28:00 +00:00
parent 1595d43876
commit b8972bfcd3
23 changed files with 822 additions and 780 deletions
@@ -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();
}
}
+9 -9
View File
@@ -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);
}
}
+8 -1
View File
@@ -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)
@@ -0,0 +1,87 @@
// -----------------------------------------------------------------------
// <copyright file="BaseGenerator.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace MeshExplorer.Generators
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TriangleNet.Geometry;
/// <summary>
/// TODO: Update summary.
/// </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;
}
}
}
+17 -79
View File
@@ -15,89 +15,32 @@ namespace MeshExplorer.Generators
/// <summary>
/// Generates a star contained in a box.
/// </summary>
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;
}
}
}
@@ -15,68 +15,28 @@ namespace MeshExplorer.Generators
/// <summary>
/// Generates a ring polygon.
/// </summary>
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;
}
}
}
+17 -62
View File
@@ -15,66 +15,26 @@ namespace MeshExplorer.Generators
/// <summary>
/// Simple random points generator.
/// </summary>
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;
}
}
}
@@ -15,43 +15,26 @@ namespace MeshExplorer.Generators
/// <summary>
/// Simple random points generator (points distributed in a circle).
/// </summary>
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;
}
}
}
+19 -38
View File
@@ -15,60 +15,41 @@ namespace MeshExplorer.Generators
/// <summary>
/// Generates a ring polygon.
/// </summary>
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));
+11 -42
View File
@@ -15,48 +15,22 @@ namespace MeshExplorer.Generators
/// <summary>
/// Generates a star contained in a box.
/// </summary>
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;
}
}
}
@@ -96,6 +96,7 @@
<Compile Include="FormMain.Designer.cs">
<DependentUpon>FormMain.cs</DependentUpon>
</Compile>
<Compile Include="Generators\BaseGenerator.cs" />
<Compile Include="Generators\BoxWithHole.cs" />
<Compile Include="Generators\CircleWithHole.cs" />
<Compile Include="Generators\IGenerator.cs" />
+8 -4
View File
@@ -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);
}
+2 -2
View File
@@ -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
@@ -103,6 +103,19 @@ namespace TriangleNet.Geometry
ymax = Math.Max(ymax, y);
}
/// <summary>
/// Scale bounds.
/// </summary>
/// <param name="dx">Add dx to left and right bounds.</param>
/// <param name="dy">Add dy to top and bottom bounds.</param>
public void Scale(double dx, double dy)
{
xmin -= dx;
xmax += dx;
ymin -= dy;
ymax += dy;
}
/// <summary>
/// Check if given point is inside bounding box.
/// </summary>
+1
View File
@@ -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++)
+192 -93
View File
@@ -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;
/// <summary>
/// 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
/// </remarks>
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
/// <summary>
/// Start a new session with given name.
/// </summary>
/// <param name="name">Name of the session (and output files).</param>
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);
}
/// <summary>
/// Write complete mesh to a .mesh file.
/// Write complete mesh to file.
/// </summary>
public void Write()
public void Write(Mesh mesh, bool skip = false)
{
this.WriteMesh(mesh, skip);
this.triangles = mesh.Triangles.Count;
}
/// <summary>
/// Finish this session.
/// </summary>
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;
}
}
}
}
+16 -317
View File
@@ -28,7 +28,6 @@ namespace TriangleNet
ILog<SimpleLogItem> logger;
Quality quality;
Sampler sampler;
// Stack that maintains a list of recently flipped triangles.
Stack<Otri> 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
/// <summary>
/// Initializes a new instance of the <see cref="Mesh" /> class.
/// </summary>
@@ -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();
}
/// <summary>
@@ -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
/// <summary>
/// Find a triangle or edge containing a given point.
/// </summary>
/// <param name="searchpoint">The point to locate.</param>
/// <param name="searchtri">The triangle to start the search at.</param>
/// <param name="stopatsubsegment"> If 'stopatsubsegment' is set, the search
/// will stop if it tries to walk through a subsegment, and will return OUTSIDE.</param>
/// <returns>Location information.</returns>
/// <remarks>
/// 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.</remarks>
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();
}
}
/// <summary>
/// Find a triangle or edge containing a given point.
/// </summary>
/// <param name="searchpoint">The point to locate.</param>
/// <param name="searchtri">The triangle to start the search at.</param>
/// <returns>Location information.</returns>
/// <remarks>
/// 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.
/// </remarks>
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
/// <summary>
@@ -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))
+2 -2
View File
@@ -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)
+11
View File
@@ -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.");
+10 -6
View File
@@ -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();
+23 -17
View File
@@ -5,9 +5,10 @@
// </copyright>
// -----------------------------------------------------------------------
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<VoronoiRegion> regions;
// Stores the endpoints of rays of infinite Voronoi cells
Dictionary<int, Point> rayPoints;
int rayIndex;
// Bounding box of the triangles circumcenters.
BoundingBox bounds;
/// <summary>
/// Initializes a new instance of the <see cref="Voronoi" /> class.
/// </summary>
@@ -74,11 +79,14 @@ namespace TriangleNet.Tools
this.points = new Point[mesh.triangles.Count + mesh.hullsize];
this.regions = new List<VoronoiRegion>(mesh.vertices.Count);
ComputeCircumCenters();
rayPoints = new Dictionary<int, Point>();
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);
}
/// <summary>
///
/// Construct Voronoi region for given vertex.
/// </summary>
/// <param name="vertex"></param>
/// <returns>The circumcenter indices which make up the cell.</returns>
@@ -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)
+1
View File
@@ -93,6 +93,7 @@
<Compile Include="Algorithm\SweepLine.cs" />
<Compile Include="Tools\Voronoi.cs" />
<Compile Include="Tools\VoronoiRegion.cs" />
<Compile Include="TriangleLocator.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+339
View File
@@ -0,0 +1,339 @@
// -----------------------------------------------------------------------
// <copyright file="TriangleLocator.cs" company="">
// 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/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet
{
using TriangleNet.Data;
using TriangleNet.Geometry;
/// <summary>
/// TODO: Update summary.
/// </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.
}
/// <summary>
/// Find a triangle or edge containing a given point.
/// </summary>
/// <param name="searchpoint">The point to locate.</param>
/// <param name="searchtri">The triangle to start the search at.</param>
/// <param name="stopatsubsegment"> If 'stopatsubsegment' is set, the search
/// will stop if it tries to walk through a subsegment, and will return OUTSIDE.</param>
/// <returns>Location information.</returns>
/// <remarks>
/// 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.</remarks>
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();
}
}
/// <summary>
/// Find a triangle or edge containing a given point.
/// </summary>
/// <param name="searchpoint">The point to locate.</param>
/// <param name="searchtri">The triangle to start the search at.</param>
/// <returns>Location information.</returns>
/// <remarks>
/// 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.
/// </remarks>
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);
}
}
}