Fixed issue #9346
+ some minor fixes/changes git-svn-id: https://triangle.svn.codeplex.com/svn@67039 0e2699bc-83d4-4a8f-98e7-55e24ab8c7a5
This commit is contained in:
@@ -97,7 +97,7 @@ namespace TestApp
|
||||
|
||||
meshRenderer1.SetData(mesh, false);
|
||||
|
||||
statistic.Update(mesh);
|
||||
statistic.Update(mesh, 10);
|
||||
|
||||
if (formStats != null && !formStats.IsDisposed)
|
||||
{
|
||||
@@ -121,7 +121,7 @@ namespace TestApp
|
||||
{
|
||||
if (Directory.Exists(@"..\..\..\Data\"))
|
||||
{
|
||||
dlgDirectory = @"..\..\..\Data\";
|
||||
dlgDirectory = Path.GetFullPath(@"..\..\..\Data\");
|
||||
|
||||
//Examples.Example1();
|
||||
//Examples.Example2();
|
||||
@@ -129,7 +129,7 @@ namespace TestApp
|
||||
}
|
||||
else if (Directory.Exists(@"Data\"))
|
||||
{
|
||||
dlgDirectory = @"Data\";
|
||||
dlgDirectory = Path.GetFullPath(@"Data\");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -217,7 +217,7 @@ namespace TestApp
|
||||
|
||||
meshRenderer1.SetData(mesh, false);
|
||||
|
||||
statistic.Update(mesh);
|
||||
statistic.Update(mesh, 10);
|
||||
|
||||
if (formStats != null && !formStats.IsDisposed)
|
||||
{
|
||||
|
||||
@@ -849,7 +849,9 @@ namespace TriangleNet.Algorithm
|
||||
{
|
||||
if (Behavior.Verbose)
|
||||
{
|
||||
SimpleLogger.Instance.Warning("A duplicate vertex appeared and was ignored.", "DivConquer.DivconqDelaunay()");
|
||||
SimpleLog.Instance.Warning(
|
||||
String.Format("A duplicate vertex appeared and was ignored (ID {0}).", sortarray[j].Hash),
|
||||
"DivConquer.DivconqDelaunay()");
|
||||
}
|
||||
sortarray[j].type = VertexType.UndeadVertex;
|
||||
m.undeads++;
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace TriangleNet.Algorithm
|
||||
{
|
||||
if (Behavior.Verbose)
|
||||
{
|
||||
SimpleLogger.Instance.Warning("A duplicate vertex appeared and was ignored.",
|
||||
SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.",
|
||||
"Incremental.IncrementalDelaunay()");
|
||||
}
|
||||
v.type = VertexType.UndeadVertex;
|
||||
|
||||
@@ -583,7 +583,7 @@ namespace TriangleNet.Algorithm
|
||||
{
|
||||
if (heapsize == 0)
|
||||
{
|
||||
SimpleLogger.Instance.Error("Input vertices are all identical.", "SweepLine.SweepLineDelaunay()");
|
||||
SimpleLog.Instance.Error("Input vertices are all identical.", "SweepLine.SweepLineDelaunay()");
|
||||
throw new Exception("Input vertices are all identical.");
|
||||
}
|
||||
secondvertex = eventheap[0].vertexEvent;
|
||||
@@ -594,7 +594,7 @@ namespace TriangleNet.Algorithm
|
||||
{
|
||||
if (Behavior.Verbose)
|
||||
{
|
||||
SimpleLogger.Instance.Warning("A duplicate vertex appeared and was ignored.",
|
||||
SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.",
|
||||
"SweepLine.SweepLineDelaunay().1");
|
||||
}
|
||||
secondvertex.type = VertexType.UndeadVertex;
|
||||
@@ -650,7 +650,7 @@ namespace TriangleNet.Algorithm
|
||||
{
|
||||
if (Behavior.Verbose)
|
||||
{
|
||||
SimpleLogger.Instance.Warning("A duplicate vertex appeared and was ignored.",
|
||||
SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.",
|
||||
"SweepLine.SweepLineDelaunay().2");
|
||||
}
|
||||
nextvertex.type = VertexType.UndeadVertex;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace TriangleNet
|
||||
{
|
||||
static readonly double SQRT2 = 1.4142135623730950488016887242096980785696718753769480732;
|
||||
|
||||
public List<BadTriangle> badtriangles;
|
||||
public int Count { get { return this.count; } }
|
||||
|
||||
// Variables that maintain the bad triangle queues. The queues are
|
||||
// ordered from 4095 (highest priority) to 0 (lowest priority).
|
||||
@@ -27,15 +27,19 @@ namespace TriangleNet
|
||||
int[] nextnonemptyq;//[4096];
|
||||
int firstnonemptyq;
|
||||
|
||||
int count;
|
||||
|
||||
public BadTriQueue()
|
||||
{
|
||||
badtriangles = new List<BadTriangle>();
|
||||
//badtriangles = new List<BadTriangle>();
|
||||
|
||||
queuefront = new BadTriangle[4096];
|
||||
queuetail = new BadTriangle[4096];
|
||||
nextnonemptyq = new int[4096];
|
||||
|
||||
firstnonemptyq = -1;
|
||||
|
||||
count = 0;
|
||||
}
|
||||
|
||||
#region Queue
|
||||
@@ -57,6 +61,8 @@ namespace TriangleNet
|
||||
int posexponent;
|
||||
int i;
|
||||
|
||||
this.count++;
|
||||
|
||||
// Determine the appropriate queue to put the bad triangle into.
|
||||
// Recall that the key is the square of its shortest edge length.
|
||||
if (badtri.key >= 1.0)
|
||||
@@ -166,7 +172,7 @@ namespace TriangleNet
|
||||
Vertex dest = enqtri.Dest();
|
||||
Vertex apex = enqtri.Apex();
|
||||
|
||||
badtriangles.Add(newbad);
|
||||
//badtriangles.Add(newbad);
|
||||
|
||||
Enqueue(newbad);
|
||||
}
|
||||
@@ -184,6 +190,9 @@ namespace TriangleNet
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
this.count--;
|
||||
|
||||
// Find the first triangle of the highest-priority queue.
|
||||
result = queuefront[firstnonemptyq];
|
||||
// Remove the triangle from the queue.
|
||||
|
||||
@@ -51,12 +51,12 @@ namespace TriangleNet
|
||||
RegionAttrib = false;
|
||||
Convex = false;
|
||||
Jettison = false;
|
||||
UseBoundaryMarkers = false;
|
||||
UseBoundaryMarkers = true;
|
||||
NoHoles = false;
|
||||
NoExact = false;
|
||||
ConformDel = false;
|
||||
Algorithm = TriangulationAlgorithm.Dwyer;
|
||||
Verbose = false;
|
||||
Verbose = true;
|
||||
UseSegments = true;
|
||||
|
||||
NoBisect = 0;
|
||||
|
||||
@@ -27,13 +27,13 @@ namespace TriangleNet.Data
|
||||
internal int Hash;
|
||||
|
||||
// The ID is only used for mesh output.
|
||||
public int ID;
|
||||
//public int ID;
|
||||
|
||||
public Osub[] subsegs;
|
||||
public Vertex[] vertices;
|
||||
public Otri[] triangles;
|
||||
public int boundary;
|
||||
public int segment;
|
||||
//public int segment;
|
||||
|
||||
public Subseg()
|
||||
{
|
||||
|
||||
@@ -50,28 +50,6 @@ namespace TriangleNet.Data
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(Vertex a, Vertex b)
|
||||
{
|
||||
// If vertex is null return false.
|
||||
if ((object)a == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Vertex a, Vertex b)
|
||||
{
|
||||
// If vertex is null return false.
|
||||
if ((object)a == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return !a.Equals(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified coordinate of the vertex.
|
||||
/// </summary>
|
||||
@@ -110,31 +88,53 @@ namespace TriangleNet.Data
|
||||
hashSeed = value;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.Hash;
|
||||
}
|
||||
#region Operator overloading / overriding Equals
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
Vertex v = obj as Vertex;
|
||||
// Compare "Guidelines for Overriding Equals() and Operator =="
|
||||
// http://msdn.microsoft.com/en-us/library/ms173147.aspx
|
||||
|
||||
if (v == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.Equals(v);
|
||||
}
|
||||
|
||||
public bool Equals(Vertex v)
|
||||
public static bool operator ==(Vertex a, Vertex b)
|
||||
{
|
||||
// If both are null, or both are same instance, return true.
|
||||
if (object.ReferenceEquals(this, v))
|
||||
if (Object.ReferenceEquals(a, b))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If one is null, but not both, return false.
|
||||
if (((object)a == null) || ((object)b == null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Vertex a, Vertex b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
// If parameter is null return false.
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Vertex v = obj as Vertex;
|
||||
|
||||
if ((object)v == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.pt.Equals(v.pt);
|
||||
}
|
||||
|
||||
public bool Equals(Vertex v)
|
||||
{
|
||||
// If vertex is null return false.
|
||||
if ((object)v == null)
|
||||
{
|
||||
@@ -145,6 +145,12 @@ namespace TriangleNet.Data
|
||||
return this.pt.Equals(v.pt);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return this.Hash;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -20,12 +20,6 @@ namespace TriangleNet.IO
|
||||
/// </summary>
|
||||
public static class DataReader
|
||||
{
|
||||
class StackTri
|
||||
{
|
||||
public Otri tri = default(Otri);
|
||||
public StackTri next;
|
||||
}
|
||||
|
||||
#region Library
|
||||
|
||||
/// <summary>
|
||||
@@ -134,7 +128,7 @@ namespace TriangleNet.IO
|
||||
corner[j] = input.Triangles[i][j];
|
||||
if ((corner[j] < 0) || (corner[j] >= mesh.invertices))
|
||||
{
|
||||
SimpleLogger.Instance.Error("Triangle has an invalid vertex index.", "MeshReader.Reconstruct()");
|
||||
SimpleLog.Instance.Error("Triangle has an invalid vertex index.", "MeshReader.Reconstruct()");
|
||||
throw new Exception("Triangle has an invalid vertex index.");
|
||||
}
|
||||
}
|
||||
@@ -242,7 +236,7 @@ namespace TriangleNet.IO
|
||||
{
|
||||
if ((end[j] < 0) || (end[j] >= mesh.invertices))
|
||||
{
|
||||
SimpleLogger.Instance.Error("Segment has an invalid vertex index.", "MeshReader.Reconstruct()");
|
||||
SimpleLog.Instance.Error("Segment has an invalid vertex index.", "MeshReader.Reconstruct()");
|
||||
throw new Exception("Segment has an invalid vertex index.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +170,20 @@ namespace TriangleNet.IO
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
n = mesh.holes.Count;
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
data.Holes = new double[n][];
|
||||
|
||||
i = 0;
|
||||
foreach (var hole in mesh.holes)
|
||||
{
|
||||
data.Holes[i] = new double[] { hole.X, hole.Y };
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -281,7 +295,7 @@ namespace TriangleNet.IO
|
||||
/// <summary>
|
||||
/// Gets the Voronoi diagram as raw output data.
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
/// <param name="mesh"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// The Voronoi diagram is the geometric dual of the Delaunay triangulation.
|
||||
@@ -292,7 +306,7 @@ namespace TriangleNet.IO
|
||||
/// WARNING: In order to assign numbers to the Voronoi vertices, this
|
||||
/// procedure messes up the subsegments or the extra nodes of every
|
||||
/// element. Hence, you should call this procedure last.</remarks>
|
||||
public static VoronoiData WriteVoronoi(Mesh m)
|
||||
public static VoronoiData WriteVoronoi(Mesh mesh)
|
||||
{
|
||||
VoronoiData data = new VoronoiData();
|
||||
|
||||
@@ -302,15 +316,29 @@ namespace TriangleNet.IO
|
||||
double xi = 0, eta = 0;
|
||||
int p1, p2;
|
||||
|
||||
int i = 0;
|
||||
|
||||
// Copy input points (actually not part of the voronoi diagram)
|
||||
data.InputPoints = new double[mesh.vertices.Count][];
|
||||
|
||||
foreach (var item in mesh.vertices.Values)
|
||||
{
|
||||
if (item.type != VertexType.UndeadVertex)
|
||||
{
|
||||
data.InputPoints[i] = new double[] { item.pt.X, item.pt.Y };
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate memory for Voronoi vertices.
|
||||
data.PointList = new double[m.triangles.Count][];
|
||||
data.Points = new double[mesh.triangles.Count][];
|
||||
|
||||
int index = 0;
|
||||
|
||||
tri.orient = 0;
|
||||
|
||||
int i = 0;
|
||||
foreach (var item in m.triangles.Values)
|
||||
i = 0;
|
||||
foreach (var item in mesh.triangles.Values)
|
||||
{
|
||||
tri.triangle = item;
|
||||
torg = tri.Org();
|
||||
@@ -319,17 +347,17 @@ namespace TriangleNet.IO
|
||||
circumcenter = Primitives.FindCircumcenter(torg.pt, tdest.pt, tapex.pt, ref xi, ref eta, false);
|
||||
|
||||
// X and y coordinates.
|
||||
data.PointList[i] = new double[] { circumcenter.X, circumcenter.Y };
|
||||
data.Points[i] = new double[] { circumcenter.X, circumcenter.Y };
|
||||
|
||||
// Update element id
|
||||
tri.triangle.ID = i++;
|
||||
}
|
||||
|
||||
// Allocate memory for output Voronoi edges.
|
||||
data.EdgeList = new int[m.edges][];
|
||||
data.Edges = new int[mesh.edges][];
|
||||
|
||||
// Allocate memory for output Voronoi norms.
|
||||
data.NormList = new double[m.edges][];
|
||||
data.Directions = new double[mesh.edges][];
|
||||
|
||||
index = 0;
|
||||
// To loop over the set of edges, loop over all triangles, and look at
|
||||
@@ -338,7 +366,7 @@ namespace TriangleNet.IO
|
||||
// adjacent triangle, operate on the edge only if the current triangle
|
||||
// has a smaller pointer than its neighbor. This way, each edge is
|
||||
// considered only once.
|
||||
foreach (var item in m.triangles.Values)
|
||||
foreach (var item in mesh.triangles.Values)
|
||||
{
|
||||
tri.triangle = item;
|
||||
|
||||
@@ -356,8 +384,8 @@ namespace TriangleNet.IO
|
||||
tdest = tri.Dest();
|
||||
|
||||
// Copy an infinite ray. Index of one endpoint, and -1.
|
||||
data.EdgeList[index] = new int[] { p1, -1};
|
||||
data.NormList[index] = new double[] { tdest[1] - torg[1], torg[0] - tdest[0] };
|
||||
data.Edges[index] = new int[] { p1, -1};
|
||||
data.Directions[index] = new double[] { tdest[1] - torg[1], torg[0] - tdest[0] };
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -365,8 +393,8 @@ namespace TriangleNet.IO
|
||||
p2 = trisym.triangle.ID;
|
||||
// Finite edge. Write indices of two endpoints.
|
||||
|
||||
data.EdgeList[index] = new int[] { p1, p2 };
|
||||
data.NormList[index] = new double[] { 0, 0 };
|
||||
data.Edges[index] = new int[] { p1, p2 };
|
||||
data.Directions[index] = new double[] { 0, 0 };
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
@@ -393,7 +393,7 @@ namespace TriangleNet.IO
|
||||
{
|
||||
if (Behavior.Verbose)
|
||||
{
|
||||
SimpleLogger.Instance.Warning("Invalid first endpoint of segment.",
|
||||
SimpleLog.Instance.Warning("Invalid first endpoint of segment.",
|
||||
"MeshReader.ReadPolyfile()");
|
||||
}
|
||||
}
|
||||
@@ -401,7 +401,7 @@ namespace TriangleNet.IO
|
||||
{
|
||||
if (Behavior.Verbose)
|
||||
{
|
||||
SimpleLogger.Instance.Warning("Invalid second endpoint of segment.",
|
||||
SimpleLog.Instance.Warning("Invalid second endpoint of segment.",
|
||||
"MeshReader.ReadPolyfile()");
|
||||
}
|
||||
}
|
||||
@@ -489,6 +489,15 @@ namespace TriangleNet.IO
|
||||
return data;
|
||||
}
|
||||
|
||||
public static MeshData ReadEleFile(string elefilename)
|
||||
{
|
||||
MeshData data = new MeshData();
|
||||
|
||||
ReadEleFile(elefilename, data, false);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the elements from an .ele file.
|
||||
/// </summary>
|
||||
@@ -590,7 +599,7 @@ namespace TriangleNet.IO
|
||||
|
||||
if (int.Parse(line[0]) != intriangles)
|
||||
{
|
||||
SimpleLogger.Instance.Warning("Number of area constraints doesn't match number of triangles.",
|
||||
SimpleLog.Instance.Warning("Number of area constraints doesn't match number of triangles.",
|
||||
"ReadAreaFile()");
|
||||
return;
|
||||
}
|
||||
@@ -614,5 +623,100 @@ namespace TriangleNet.IO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static MeshData ReadEdgeFile(string edgeFile)
|
||||
{
|
||||
// Read poly file
|
||||
MeshData data = new MeshData();
|
||||
|
||||
startIndex = 0;
|
||||
|
||||
string[] line;
|
||||
|
||||
using (StreamReader reader = new StreamReader(edgeFile))
|
||||
{
|
||||
// Read the edges from a .edge file.
|
||||
|
||||
// Read number of segments and number of boundary markers.
|
||||
if (!TryReadLine(reader, out line))
|
||||
{
|
||||
throw new Exception("Can't read input file (segments).");
|
||||
}
|
||||
|
||||
int inedges = int.Parse(line[0]);
|
||||
|
||||
int edgemarkers = 0;
|
||||
if (line.Length > 1)
|
||||
{
|
||||
edgemarkers = int.Parse(line[1]);
|
||||
}
|
||||
|
||||
if (inedges > 0)
|
||||
{
|
||||
data.Edges = new int[inedges][];
|
||||
}
|
||||
|
||||
if (edgemarkers > 0)
|
||||
{
|
||||
data.EdgeMarkers = new int[inedges];
|
||||
}
|
||||
|
||||
int end1, end2;
|
||||
// Read and insert the segments.
|
||||
for (int i = 0; i < inedges; i++)
|
||||
{
|
||||
if (!TryReadLine(reader, out line))
|
||||
{
|
||||
throw new Exception("Can't read input file (segments).");
|
||||
}
|
||||
|
||||
if (line.Length < 3)
|
||||
{
|
||||
throw new Exception("Segment has no endpoints.");
|
||||
}
|
||||
|
||||
// TODO: startIndex ok?
|
||||
end1 = int.Parse(line[1]) - startIndex;
|
||||
end2 = int.Parse(line[2]) - startIndex;
|
||||
|
||||
if (edgemarkers > 0)
|
||||
{
|
||||
if (line.Length > 3)
|
||||
{
|
||||
data.SegmentMarkers[i] = int.Parse(line[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.SegmentMarkers[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
data.Segments[i] = new int[] { end1, end2 };
|
||||
|
||||
//if ((end1 < 0) || (end1 >= invertices))
|
||||
//{
|
||||
// if (Behavior.Verbose)
|
||||
// {
|
||||
// SimpleLogger.Instance.Warning("Invalid first endpoint of segment.",
|
||||
// "MeshReader.ReadPolyfile()");
|
||||
// }
|
||||
//}
|
||||
//else if ((end2 < 0) || (end2 >= invertices))
|
||||
//{
|
||||
// if (Behavior.Verbose)
|
||||
// {
|
||||
// SimpleLogger.Instance.Warning("Invalid second endpoint of segment.",
|
||||
// "MeshReader.ReadPolyfile()");
|
||||
// }
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// data.Segments[i] = new int[] { end1, end2 };
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,15 @@
|
||||
namespace TriangleNet.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the mesh data in- and output.
|
||||
/// Stores the voronoi data (output only).
|
||||
/// </summary>
|
||||
public class VoronoiData
|
||||
{
|
||||
public double[][] PointList; // In / out
|
||||
//public int NumberOfPoints; // In / out
|
||||
|
||||
public int[][] EdgeList; // Out only
|
||||
//public int NumberOfEdges; // Out only
|
||||
public double[][] InputPoints;
|
||||
public double[][] Points;
|
||||
public int[][] Edges;
|
||||
|
||||
// Stores the direction for infinite voronoi edges
|
||||
public double[][] NormList;
|
||||
public double[][] Directions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,17 +11,27 @@ namespace TriangleNet.Log
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
public enum LogLevel
|
||||
{
|
||||
Info = 0,
|
||||
Warning = 1,
|
||||
Error = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TODO: Update summary.
|
||||
/// A basic log interface.
|
||||
/// </summary>
|
||||
public interface ILog<T>
|
||||
public interface ILog<T> where T : ILogItem
|
||||
{
|
||||
void Add(T item);
|
||||
void Clear();
|
||||
|
||||
void Info(string message);
|
||||
void Trace(string message, string location);
|
||||
void Error(string message, string location);
|
||||
void Warning(string message, string location);
|
||||
void Error(string message, string info);
|
||||
void Warning(string message, string info);
|
||||
|
||||
IList<T> Data { get; }
|
||||
|
||||
LogLevel Level { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="ILogger.cs" company="">
|
||||
// Triangle.NET code by Christian Woltering, http://home.edo.tu-dortmund.de/~woltering/triangle/
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace TriangleNet.Log
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// A basic log item interface.
|
||||
/// </summary>
|
||||
public interface ILogItem
|
||||
{
|
||||
DateTime Time { get; }
|
||||
LogLevel Level { get; }
|
||||
string Message { get; }
|
||||
string Info { get; }
|
||||
}
|
||||
}
|
||||
+21
-14
@@ -17,21 +17,23 @@ namespace TriangleNet.Log
|
||||
/// <remarks>Using singleton pattern as proposed by Jon Skeet.
|
||||
/// http://csharpindepth.com/Articles/General/Singleton.aspx
|
||||
/// </remarks>
|
||||
public sealed class SimpleLogger : ILog<string>
|
||||
public sealed class SimpleLog : ILog<SimpleLogItem>
|
||||
{
|
||||
private List<string> log = new List<string>();
|
||||
private List<SimpleLogItem> log = new List<SimpleLogItem>();
|
||||
|
||||
private LogLevel level = LogLevel.Info;
|
||||
|
||||
#region Singleton pattern
|
||||
|
||||
private static readonly SimpleLogger instance = new SimpleLogger();
|
||||
private static readonly SimpleLog instance = new SimpleLog();
|
||||
|
||||
// Explicit static constructor to tell C# compiler
|
||||
// not to mark type as beforefieldinit
|
||||
static SimpleLogger() { }
|
||||
static SimpleLog() { }
|
||||
|
||||
private SimpleLogger() { }
|
||||
private SimpleLog() { }
|
||||
|
||||
public static ILog<string> Instance
|
||||
public static ILog<SimpleLogItem> Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -41,34 +43,39 @@ namespace TriangleNet.Log
|
||||
|
||||
#endregion
|
||||
|
||||
public void Add(string item)
|
||||
public void Add(SimpleLogItem item)
|
||||
{
|
||||
log.Add(item);
|
||||
}
|
||||
|
||||
public void Info(string message)
|
||||
public void Clear()
|
||||
{
|
||||
log.Add(message);
|
||||
log.Clear();
|
||||
}
|
||||
|
||||
public void Trace(string message, string location)
|
||||
public void Info(string message)
|
||||
{
|
||||
log.Add(message);
|
||||
log.Add(new SimpleLogItem(LogLevel.Info, message));
|
||||
}
|
||||
|
||||
public void Warning(string message, string location)
|
||||
{
|
||||
log.Add(message);
|
||||
log.Add(new SimpleLogItem(LogLevel.Warning, message, location));
|
||||
}
|
||||
|
||||
public void Error(string message, string location)
|
||||
{
|
||||
log.Add(message);
|
||||
log.Add(new SimpleLogItem(LogLevel.Error, message, location));
|
||||
}
|
||||
|
||||
public IList<string> Data
|
||||
public IList<SimpleLogItem> Data
|
||||
{
|
||||
get { return log; }
|
||||
}
|
||||
|
||||
public LogLevel Level
|
||||
{
|
||||
get { return level; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="SimpleLogItem.cs" company="">
|
||||
// TODO: Update copyright text.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace TriangleNet.Log
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// TODO: Update summary.
|
||||
/// </summary>
|
||||
public class SimpleLogItem : ILogItem
|
||||
{
|
||||
DateTime time;
|
||||
LogLevel level;
|
||||
string message;
|
||||
string info;
|
||||
|
||||
public DateTime Time
|
||||
{
|
||||
get { return time; }
|
||||
}
|
||||
|
||||
public LogLevel Level
|
||||
{
|
||||
get { return level; }
|
||||
}
|
||||
|
||||
public string Message
|
||||
{
|
||||
get { return message; }
|
||||
}
|
||||
|
||||
public string Info
|
||||
{
|
||||
get { return info; }
|
||||
}
|
||||
|
||||
public SimpleLogItem(LogLevel level, string message)
|
||||
: this(level, message, "")
|
||||
{ }
|
||||
|
||||
public SimpleLogItem(LogLevel level, string message, string info)
|
||||
{
|
||||
this.time = DateTime.Now;
|
||||
this.level = level;
|
||||
this.message = message;
|
||||
this.info = info;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ namespace TriangleNet
|
||||
using TriangleNet.Log;
|
||||
using TriangleNet.IO;
|
||||
using TriangleNet.Algorithm;
|
||||
using TriangleNet.Smoothing;
|
||||
|
||||
/// <summary>
|
||||
/// Mesh data structure.
|
||||
@@ -22,7 +23,7 @@ namespace TriangleNet
|
||||
{
|
||||
#region Variables
|
||||
|
||||
ILog<string> logger;
|
||||
ILog<SimpleLogItem> logger;
|
||||
|
||||
Quality quality;
|
||||
Sampler sampler;
|
||||
@@ -49,11 +50,11 @@ namespace TriangleNet
|
||||
internal int inelements; // Number of input triangles.
|
||||
internal int insegments; // Number of input segments.
|
||||
internal int undeads; // Number of input vertices that don't appear in the mesh.
|
||||
internal long edges; // Number of output edges.
|
||||
internal int edges; // Number of output edges.
|
||||
internal int mesh_dim; // Dimension (ought to be 2).
|
||||
internal int nextras; // Number of attributes per vertex.
|
||||
internal int eextras; // Number of attributes per triangle.
|
||||
internal long hullsize; // Number of edges in convex hull.
|
||||
internal int hullsize; // Number of edges in convex hull.
|
||||
internal int steinerleft; // Number of Steiner points not yet used.
|
||||
internal bool checksegments; // Are there segments in the triangulation yet?
|
||||
internal bool checkquality; // Has quality triangulation begun yet?
|
||||
@@ -84,7 +85,7 @@ namespace TriangleNet
|
||||
|
||||
public Mesh()
|
||||
{
|
||||
logger = SimpleLogger.Instance;
|
||||
logger = SimpleLog.Instance;
|
||||
|
||||
Behavior.Init();
|
||||
|
||||
@@ -266,8 +267,6 @@ namespace TriangleNet
|
||||
quality.EnforceQuality(); // Enforce angle and area constraints.
|
||||
}
|
||||
|
||||
quality.CheckMesh();
|
||||
|
||||
// Calculate the number of edges.
|
||||
edges = (3 * triangles.Count + hullsize) / 2;
|
||||
}
|
||||
@@ -357,8 +356,8 @@ namespace TriangleNet
|
||||
}
|
||||
|
||||
// TODO
|
||||
holes.Clear();
|
||||
regions.Clear();
|
||||
//holes.Clear();
|
||||
//regions.Clear();
|
||||
|
||||
if (triangles.Count > 0)
|
||||
{
|
||||
@@ -370,6 +369,15 @@ namespace TriangleNet
|
||||
edges = (3 * triangles.Count + hullsize) / 2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smooth the current mesh.
|
||||
/// </summary>
|
||||
public void Smooth()
|
||||
{
|
||||
//ISmoother smoother = new CvdSmoother(this);
|
||||
//smoother.Smooth();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check mesh consistency and (constrained) Delaunay property.
|
||||
/// </summary>
|
||||
@@ -626,6 +634,9 @@ namespace TriangleNet
|
||||
triangles.Clear();
|
||||
subsegs.Clear();
|
||||
|
||||
holes.Clear();
|
||||
regions.Clear();
|
||||
|
||||
Triangle.ResetHashSeed(0);
|
||||
Vertex.ResetHashSeed(0);
|
||||
Subseg.ResetHashSeed(0);
|
||||
@@ -633,6 +644,13 @@ namespace TriangleNet
|
||||
viri.Clear();
|
||||
flipstackers.Clear();
|
||||
|
||||
hullsize = 0;
|
||||
xmin = 0;
|
||||
xmax = 0;
|
||||
ymin = 0;
|
||||
ymax = 0;
|
||||
edges = 0;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
@@ -2300,7 +2318,7 @@ namespace TriangleNet
|
||||
|
||||
// TODO: Improve sampling.
|
||||
sampler.Update(this);
|
||||
int[] samples = sampler.GetSamples();
|
||||
int[] samples = sampler.GetSamples(this);
|
||||
|
||||
foreach (var key in samples)
|
||||
{
|
||||
@@ -2473,7 +2491,6 @@ namespace TriangleNet
|
||||
double tx, ty;
|
||||
double etx, ety;
|
||||
double split, denom;
|
||||
int i;
|
||||
|
||||
// Find the other three segment endpoints.
|
||||
endpoint1 = splittri.Apex();
|
||||
@@ -2990,15 +3007,15 @@ namespace TriangleNet
|
||||
void InsertSegment(Vertex endpoint1, Vertex endpoint2, int newmark)
|
||||
{
|
||||
Otri searchtri1 = default(Otri), searchtri2 = default(Otri);
|
||||
Vertex checkvertex;
|
||||
Vertex checkvertex = null;
|
||||
|
||||
// Find a triangle whose origin is the segment's first endpoint.
|
||||
checkvertex = null;
|
||||
searchtri1 = endpoint1.tri;
|
||||
if (searchtri1.triangle != null)
|
||||
{
|
||||
checkvertex = searchtri1.Org();
|
||||
}
|
||||
|
||||
if (checkvertex != endpoint1)
|
||||
{
|
||||
// Find a boundary triangle to search from.
|
||||
|
||||
@@ -22,11 +22,11 @@ namespace TriangleNet
|
||||
Mesh mesh;
|
||||
Func<Vertex, Vertex, Vertex, double, bool> userTest;
|
||||
|
||||
ILog<string> logger;
|
||||
ILog<SimpleLogItem> logger;
|
||||
|
||||
public Quality(Mesh m)
|
||||
{
|
||||
logger = SimpleLogger.Instance;
|
||||
logger = SimpleLog.Instance;
|
||||
|
||||
badsubsegs = new Queue<BadSubseg>();
|
||||
queue = new BadTriQueue();
|
||||
@@ -957,7 +957,7 @@ namespace TriangleNet
|
||||
TallyFaces();
|
||||
|
||||
mesh.checkquality = true;
|
||||
while ((queue.badtriangles.Count > 0) && (mesh.steinerleft != 0))
|
||||
while ((queue.Count > 0) && (mesh.steinerleft != 0))
|
||||
{
|
||||
// Fix one bad triangle by inserting a vertex at its circumcenter.
|
||||
badtri = queue.Dequeue();
|
||||
@@ -974,7 +974,7 @@ namespace TriangleNet
|
||||
else
|
||||
{
|
||||
// Return the bad triangle to the pool.
|
||||
queue.badtriangles.Remove(badtri);
|
||||
//queue.badtriangles.Remove(badtri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,11 +35,20 @@ namespace TriangleNet
|
||||
/// </summary>
|
||||
/// <param name="mesh">Current mesh.</param>
|
||||
public void Update(Mesh mesh)
|
||||
{
|
||||
this.Update(mesh, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update sampling parameters if mesh changed.
|
||||
/// </summary>
|
||||
/// <param name="mesh">Current mesh.</param>
|
||||
public void Update(Mesh mesh, bool forceUpdate)
|
||||
{
|
||||
int count = mesh.triangles.Count;
|
||||
|
||||
// TODO: Is checking the triangle count a good way to monitor mesh changes?
|
||||
if (triangleCount != count)
|
||||
if (triangleCount != count || forceUpdate)
|
||||
{
|
||||
triangleCount = count;
|
||||
|
||||
@@ -61,20 +70,33 @@ namespace TriangleNet
|
||||
/// Get a random sample set of triangle keys.
|
||||
/// </summary>
|
||||
/// <returns>Array of triangle keys.</returns>
|
||||
public int[] GetSamples()
|
||||
public int[] GetSamples(Mesh mesh)
|
||||
{
|
||||
int[] randSamples = new int[samples];
|
||||
// TODO: Using currKeys to check key availability?
|
||||
List<int> randSamples = new List<int>(samples);
|
||||
|
||||
int range = triangleCount / samples;
|
||||
int key;
|
||||
|
||||
for (int i = 0; i < samples; i++)
|
||||
{
|
||||
// Yeah, rand should be equally distributed, but just to make
|
||||
// sure, use a range variable...
|
||||
randSamples[i] = keys[rand.Next(i * range, (i + 1) * range - 1)];
|
||||
key = rand.Next(i * range, (i + 1) * range - 1);
|
||||
|
||||
if (!mesh.triangles.Keys.Contains(keys[key]))
|
||||
{
|
||||
// Keys collection isn't up to date anymore!
|
||||
this.Update(mesh, true);
|
||||
i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
randSamples.Add(keys[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return randSamples;
|
||||
return randSamples.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// -----------------------------------------------------------------------
|
||||
// <copyright file="ISmoother.cs" company="">
|
||||
// TODO: Update copyright text.
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace TriangleNet.Smoothing
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// TODO: Update summary.
|
||||
/// </summary>
|
||||
public interface ISmoother
|
||||
{
|
||||
void Smooth();
|
||||
}
|
||||
}
|
||||
+196
-322
@@ -49,7 +49,7 @@ namespace TriangleNet
|
||||
public static long CircleTopCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Number of vertex relocation.
|
||||
/// Number of vertex relocations.
|
||||
/// </summary>
|
||||
public static long RelocationCount = 0;
|
||||
|
||||
@@ -59,191 +59,199 @@ namespace TriangleNet
|
||||
|
||||
double minEdge = 0;
|
||||
/// <summary>
|
||||
/// Shortest edge
|
||||
/// Gets the shortest edge.
|
||||
/// </summary>
|
||||
public double ShortestEdge { get { return minEdge; } }
|
||||
|
||||
double maxEdge = 0;
|
||||
/// <summary>
|
||||
/// Longest edge
|
||||
/// Gets the longest edge.
|
||||
/// </summary>
|
||||
public double LongestEdge { get { return maxEdge; } }
|
||||
|
||||
//
|
||||
double minAspect = 0;
|
||||
/// <summary>
|
||||
/// Shortest altitude
|
||||
/// Gets the shortest altitude.
|
||||
/// </summary>
|
||||
public double ShortestAltitude { get { return minAspect; } }
|
||||
|
||||
double maxAspect = 0;
|
||||
/// <summary>
|
||||
/// Largest aspect ratio
|
||||
/// Gets the largest aspect ratio.
|
||||
/// </summary>
|
||||
public double LargestAspectRatio { get { return maxAspect; } }
|
||||
|
||||
double minArea = 0;
|
||||
/// <summary>
|
||||
/// Smallest area
|
||||
/// Gets the smallest area.
|
||||
/// </summary>
|
||||
public double SmallestArea { get { return minArea; } }
|
||||
|
||||
double maxArea = 0;
|
||||
/// <summary>
|
||||
/// Largest area
|
||||
/// Gets the largest area.
|
||||
/// </summary>
|
||||
public double LargestArea { get { return maxArea; } }
|
||||
|
||||
double minAngle = 0;
|
||||
/// <summary>
|
||||
/// Smallest angle
|
||||
/// Gets the smallest angle.
|
||||
/// </summary>
|
||||
public double SmallestAngle { get { return minAngle; } }
|
||||
|
||||
double maxAngle = 0;
|
||||
/// <summary>
|
||||
/// Largest angle
|
||||
/// Gets the largest angle.
|
||||
/// </summary>
|
||||
public double LargestAngle { get { return maxAngle; } }
|
||||
|
||||
int inVetrices = 0;
|
||||
/// <summary>
|
||||
/// Input vertices
|
||||
/// Gets the number of input vertices.
|
||||
/// </summary>
|
||||
public int InputVertices { get { return inVetrices; } }
|
||||
|
||||
int inTriangles = 0;
|
||||
/// <summary>
|
||||
/// Input triangles
|
||||
/// Gets the number of input triangles.
|
||||
/// </summary>
|
||||
public int InputTriangles { get { return inTriangles; } }
|
||||
|
||||
int inSegments = 0;
|
||||
/// <summary>
|
||||
/// Input segments
|
||||
/// Gets the number of input segments.
|
||||
/// </summary>
|
||||
public int InputSegments { get { return inSegments; } }
|
||||
|
||||
int inHoles = 0;
|
||||
/// <summary>
|
||||
/// Input holes
|
||||
/// Gets the number of input holes.
|
||||
/// </summary>
|
||||
public int InputHoles { get { return inHoles; } }
|
||||
|
||||
int outVertices = 0;
|
||||
/// <summary>
|
||||
/// Mesh vertices
|
||||
/// Gets the number of mesh vertices.
|
||||
/// </summary>
|
||||
public int Vertices { get { return outVertices; } }
|
||||
|
||||
int outTriangles = 0;
|
||||
/// <summary>
|
||||
/// Mesh triangles
|
||||
/// Gets the number of mesh triangles.
|
||||
/// </summary>
|
||||
public int Triangles { get { return outTriangles; } }
|
||||
|
||||
int outEdges = 0;
|
||||
/// <summary>
|
||||
/// Mesh edges
|
||||
/// Gets the number of mesh edges.
|
||||
/// </summary>
|
||||
public int Edges { get { return outEdges; } }
|
||||
|
||||
int boundaryEdges = 0;
|
||||
/// <summary>
|
||||
/// Exterior boundary edges
|
||||
/// Gets the number of exterior boundary edges.
|
||||
/// </summary>
|
||||
public int BoundaryEdges { get { return boundaryEdges; } }
|
||||
|
||||
int intBoundaryEdges = 0;
|
||||
/// <summary>
|
||||
/// Interior boundary edges
|
||||
/// Gets the number of interior boundary edges.
|
||||
/// </summary>
|
||||
public int InteriorBoundaryEdges { get { return intBoundaryEdges; } }
|
||||
|
||||
int constrainedEdges = 0;
|
||||
/// <summary>
|
||||
/// Constrained edges
|
||||
/// Gets the number of constrained edges.
|
||||
/// </summary>
|
||||
public int ConstrainedEdges { get { return constrainedEdges; } }
|
||||
|
||||
int[] angleTable;
|
||||
/// <summary>
|
||||
/// Angle histogram
|
||||
/// Gets the angle histogram.
|
||||
/// </summary>
|
||||
public int[] AngleHistogram { get { return angleTable; } }
|
||||
|
||||
int[] minAngles;
|
||||
/// <summary>
|
||||
/// Gets the min angles histogram.
|
||||
/// </summary>
|
||||
public int[] MinAngleHistogram { get { return minAngles; } }
|
||||
|
||||
int[] maxAngles;
|
||||
/// <summary>
|
||||
/// Gets the max angles histogram.
|
||||
/// </summary>
|
||||
public int[] MaxAngleHistogram { get { return maxAngles; } }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// detailedHistogram()
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
void detailedHistogram(Mesh m)
|
||||
#region Private methods
|
||||
|
||||
private void GetAspectHistogram(Mesh mesh)
|
||||
{
|
||||
int[] aspecttable;
|
||||
double[] ratiotable;
|
||||
|
||||
aspecttable = new int[16];
|
||||
ratiotable = new double[] {
|
||||
1.5, 2.0, 2.5, 3.0, 4.0, 6.0, 10.0, 15.0, 25.0, 50.0,
|
||||
100.0, 300.0, 1000.0, 10000.0, 100000.0, 0.0 };
|
||||
|
||||
|
||||
Otri tri = default(Otri);
|
||||
Vertex[] p = new Vertex[3];
|
||||
double[] cosSquareTable = new double[8];
|
||||
double[] dx = new double[3], dy = new double[3];
|
||||
double[] edgelength = new double[3];
|
||||
double dotproduct;
|
||||
double cosSquare;
|
||||
double triarea;
|
||||
double trilongest2;
|
||||
double triminaltitude2;
|
||||
double triaspect2;
|
||||
|
||||
int i, ii, j, k;
|
||||
double[] cossquaretableHist = new double[89];
|
||||
double radconstHist;
|
||||
int onedegree;
|
||||
int[] angletableHist = new int[180];
|
||||
int aspectindex;
|
||||
int i, j, k;
|
||||
|
||||
radconstHist = Math.PI / 180.0;
|
||||
for (i = 0; i < 89; i++)
|
||||
tri.orient = 0;
|
||||
foreach (var t in mesh.triangles.Values)
|
||||
{
|
||||
cossquaretableHist[i] = Math.Cos(radconstHist * (i + 1));
|
||||
cossquaretableHist[i] = cossquaretableHist[i] * cossquaretableHist[i];
|
||||
}
|
||||
for (i = 0; i < 180; i++)
|
||||
{
|
||||
angletableHist[i] = 0;
|
||||
}
|
||||
|
||||
foreach (var tri in m.triangles.Values)
|
||||
{
|
||||
p[0] = tri.vertices[0];
|
||||
p[1] = tri.vertices[1];
|
||||
p[2] = tri.vertices[2];
|
||||
tri.triangle = t;
|
||||
p[0] = tri.Org();
|
||||
p[1] = tri.Dest();
|
||||
p[2] = tri.Apex();
|
||||
trilongest2 = 0.0;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
j = plus1Mod3[i];
|
||||
k = minus1Mod3[i];
|
||||
dx[i] = p[j][0] - p[k][0];
|
||||
dy[i] = p[j][1] - p[k][1];
|
||||
dx[i] = p[j].pt.X - p[k].pt.X;
|
||||
dy[i] = p[j].pt.Y - p[k].pt.Y;
|
||||
edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i];
|
||||
if (edgelength[i] > trilongest2)
|
||||
{
|
||||
trilongest2 = edgelength[i];
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 3; i++)
|
||||
|
||||
//triarea = Primitives.CounterClockwise(p[0], p[1], p[2]);
|
||||
triarea = Math.Abs((p[2].pt.X - p[0].pt.X) * (p[1].pt.Y - p[0].pt.Y) -
|
||||
(p[1].pt.X - p[0].pt.X) * (p[2].pt.Y - p[0].pt.Y)) / 2.0;
|
||||
|
||||
triminaltitude2 = triarea * triarea / trilongest2;
|
||||
|
||||
triaspect2 = trilongest2 / triminaltitude2;
|
||||
|
||||
aspectindex = 0;
|
||||
while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex]) && (aspectindex < 15))
|
||||
{
|
||||
j = plus1Mod3[i];
|
||||
k = minus1Mod3[i];
|
||||
dotproduct = dx[j] * dx[k] + dy[j] * dy[k];
|
||||
cosSquare = dotproduct * dotproduct / (edgelength[j] * edgelength[k]);
|
||||
onedegree = 89;
|
||||
for (ii = 88; ii >= 0; ii--)
|
||||
{
|
||||
if (cosSquare > cossquaretableHist[ii])
|
||||
{
|
||||
onedegree = ii;
|
||||
}
|
||||
}
|
||||
if (dotproduct <= 0.0)
|
||||
{
|
||||
angletableHist[onedegree]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
angletableHist[179 - onedegree]++;
|
||||
}
|
||||
aspectindex++;
|
||||
}
|
||||
aspecttable[aspectindex]++;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
static readonly int[] plus1Mod3 = { 1, 2, 0 };
|
||||
static readonly int[] minus1Mod3 = { 2, 0, 1 };
|
||||
|
||||
@@ -251,7 +259,7 @@ namespace TriangleNet
|
||||
/// Update statistics about the quality of the mesh.
|
||||
/// </summary>
|
||||
/// <param name="mesh"></param>
|
||||
public void Update(Mesh mesh)
|
||||
public void Update(Mesh mesh, int sampleDegrees)
|
||||
{
|
||||
inVetrices = mesh.invertices;
|
||||
inTriangles = mesh.inelements;
|
||||
@@ -267,31 +275,37 @@ namespace TriangleNet
|
||||
Point2[] p = new Point2[3];
|
||||
|
||||
int k1, k2;
|
||||
int tendegree;
|
||||
int degreeStep;
|
||||
|
||||
double[] cosSquareTable = new double[8];
|
||||
//sampleDegrees = 36; // sample every 5 degrees
|
||||
//sampleDegrees = 45; // sample every 4 degrees
|
||||
sampleDegrees = 60; // sample every 3 degrees
|
||||
|
||||
double[] cosSquareTable = new double[sampleDegrees / 2 - 1];
|
||||
double[] dx = new double[3];
|
||||
double[] dy = new double[3];
|
||||
double[] edgelength = new double[3];
|
||||
double dotproduct;
|
||||
double cossquare;
|
||||
double triarea;
|
||||
double trilongest2;
|
||||
double triminaltitude2;
|
||||
double triaspect2;
|
||||
double[] edgeLength = new double[3];
|
||||
double dotProduct;
|
||||
double cosSquare;
|
||||
double triArea;
|
||||
double triLongest2;
|
||||
double triMinAltitude2;
|
||||
double triAspect2;
|
||||
|
||||
double radconst = Math.PI / 18.0;
|
||||
double radconst = Math.PI / sampleDegrees;
|
||||
double degconst = 180.0 / Math.PI;
|
||||
|
||||
// New angle table
|
||||
angleTable = new int[18];
|
||||
angleTable = new int[sampleDegrees];
|
||||
minAngles = new int[sampleDegrees];
|
||||
maxAngles = new int[sampleDegrees];
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
for (int i = 0; i < sampleDegrees / 2 - 1; i++)
|
||||
{
|
||||
cosSquareTable[i] = Math.Cos(radconst * (i + 1));
|
||||
cosSquareTable[i] = cosSquareTable[i] * cosSquareTable[i];
|
||||
}
|
||||
for (int i = 0; i < 18; i++)
|
||||
for (int i = 0; i < sampleDegrees; i++)
|
||||
{
|
||||
angleTable[i] = 0;
|
||||
}
|
||||
@@ -307,14 +321,20 @@ namespace TriangleNet
|
||||
maxAngle = 2.0;
|
||||
|
||||
bool acuteBiggest = true;
|
||||
bool acuteBiggestTri = true;
|
||||
|
||||
double triMinAngle, triMaxAngle = 1;
|
||||
|
||||
foreach (var tri in mesh.triangles.Values)
|
||||
{
|
||||
triMinAngle = 0; // Min angle: 0 < a < 60 degress
|
||||
triMaxAngle = 1; // Max angle: 60 < a < 180 degress
|
||||
|
||||
p[0] = tri.vertices[0].pt;
|
||||
p[1] = tri.vertices[1].pt;
|
||||
p[2] = tri.vertices[2].pt;
|
||||
|
||||
trilongest2 = 0.0;
|
||||
triLongest2 = 0.0;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
@@ -324,44 +344,48 @@ namespace TriangleNet
|
||||
dx[i] = p[k1].X - p[k2].X;
|
||||
dy[i] = p[k1].Y - p[k2].Y;
|
||||
|
||||
edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i];
|
||||
edgeLength[i] = dx[i] * dx[i] + dy[i] * dy[i];
|
||||
|
||||
if (edgelength[i] > trilongest2)
|
||||
if (edgeLength[i] > triLongest2)
|
||||
{
|
||||
trilongest2 = edgelength[i];
|
||||
triLongest2 = edgeLength[i];
|
||||
}
|
||||
|
||||
if (edgelength[i] > maxEdge)
|
||||
if (edgeLength[i] > maxEdge)
|
||||
{
|
||||
maxEdge = edgelength[i];
|
||||
maxEdge = edgeLength[i];
|
||||
}
|
||||
|
||||
if (edgelength[i] < minEdge)
|
||||
if (edgeLength[i] < minEdge)
|
||||
{
|
||||
minEdge = edgelength[i];
|
||||
minEdge = edgeLength[i];
|
||||
}
|
||||
}
|
||||
|
||||
//triarea = Primitives.CounterClockwise(p[0], p[1], p[2]);
|
||||
triarea = Math.Abs((p[2].X - p[0].X) * (p[1].Y - p[0].Y) -
|
||||
triArea = Math.Abs((p[2].X - p[0].X) * (p[1].Y - p[0].Y) -
|
||||
(p[1].X - p[0].X) * (p[2].Y - p[0].Y));
|
||||
if (triarea < minArea)
|
||||
|
||||
if (triArea < minArea)
|
||||
{
|
||||
minArea = triarea;
|
||||
minArea = triArea;
|
||||
}
|
||||
if (triarea > maxArea)
|
||||
|
||||
if (triArea > maxArea)
|
||||
{
|
||||
maxArea = triarea;
|
||||
maxArea = triArea;
|
||||
}
|
||||
triminaltitude2 = triarea * triarea / trilongest2;
|
||||
if (triminaltitude2 < minAspect)
|
||||
|
||||
triMinAltitude2 = triArea * triArea / triLongest2;
|
||||
if (triMinAltitude2 < minAspect)
|
||||
{
|
||||
minAspect = triminaltitude2;
|
||||
minAspect = triMinAltitude2;
|
||||
}
|
||||
triaspect2 = trilongest2 / triminaltitude2;
|
||||
if (triaspect2 > maxAspect)
|
||||
|
||||
triAspect2 = triLongest2 / triMinAltitude2;
|
||||
if (triAspect2 > maxAspect)
|
||||
{
|
||||
maxAspect = triaspect2;
|
||||
maxAspect = triAspect2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
@@ -369,39 +393,91 @@ namespace TriangleNet
|
||||
k1 = plus1Mod3[i];
|
||||
k2 = minus1Mod3[i];
|
||||
|
||||
dotproduct = dx[k1] * dx[k2] + dy[k1] * dy[k2];
|
||||
cossquare = dotproduct * dotproduct / (edgelength[k1] * edgelength[k2]);
|
||||
tendegree = 8;
|
||||
dotProduct = dx[k1] * dx[k2] + dy[k1] * dy[k2];
|
||||
cosSquare = dotProduct * dotProduct / (edgeLength[k1] * edgeLength[k2]);
|
||||
degreeStep = sampleDegrees / 2 - 1;
|
||||
|
||||
for (int j = 7; j >= 0; j--)
|
||||
for (int j = degreeStep - 1; j >= 0; j--)
|
||||
{
|
||||
if (cossquare > cosSquareTable[j])
|
||||
if (cosSquare > cosSquareTable[j])
|
||||
{
|
||||
tendegree = j;
|
||||
degreeStep = j;
|
||||
}
|
||||
}
|
||||
if (dotproduct <= 0.0)
|
||||
|
||||
if (dotProduct <= 0.0)
|
||||
{
|
||||
angleTable[tendegree]++;
|
||||
if (cossquare > minAngle)
|
||||
angleTable[degreeStep]++;
|
||||
if (cosSquare > minAngle)
|
||||
{
|
||||
minAngle = cossquare;
|
||||
minAngle = cosSquare;
|
||||
}
|
||||
if (acuteBiggest && (cossquare < maxAngle))
|
||||
if (acuteBiggest && (cosSquare < maxAngle))
|
||||
{
|
||||
maxAngle = cossquare;
|
||||
maxAngle = cosSquare;
|
||||
}
|
||||
|
||||
// Update min/max angle per triangle
|
||||
if (cosSquare > triMinAngle)
|
||||
{
|
||||
triMinAngle = cosSquare;
|
||||
}
|
||||
if (acuteBiggestTri && (cosSquare < triMaxAngle))
|
||||
{
|
||||
triMaxAngle = cosSquare;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
angleTable[17 - tendegree]++;
|
||||
if (acuteBiggest || (cossquare > maxAngle))
|
||||
angleTable[sampleDegrees - degreeStep - 1]++;
|
||||
if (acuteBiggest || (cosSquare > maxAngle))
|
||||
{
|
||||
maxAngle = cossquare;
|
||||
maxAngle = cosSquare;
|
||||
acuteBiggest = false;
|
||||
}
|
||||
|
||||
// Update max angle for (possibly non-acute) triangle
|
||||
if (acuteBiggestTri || (cosSquare > triMaxAngle))
|
||||
{
|
||||
triMaxAngle = cosSquare;
|
||||
acuteBiggestTri = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update min angle histogram
|
||||
degreeStep = sampleDegrees / 2 - 1;
|
||||
|
||||
for (int j = degreeStep - 1; j >= 0; j--)
|
||||
{
|
||||
if (triMinAngle > cosSquareTable[j])
|
||||
{
|
||||
degreeStep = j;
|
||||
}
|
||||
}
|
||||
minAngles[degreeStep]++;
|
||||
|
||||
// Update max angle histogram
|
||||
degreeStep = sampleDegrees / 2 - 1;
|
||||
|
||||
for (int j = degreeStep - 1; j >= 0; j--)
|
||||
{
|
||||
if (triMaxAngle > cosSquareTable[j])
|
||||
{
|
||||
degreeStep = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (acuteBiggestTri)
|
||||
{
|
||||
maxAngles[degreeStep]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxAngles[sampleDegrees - degreeStep - 1]++;
|
||||
}
|
||||
|
||||
acuteBiggestTri = true;
|
||||
}
|
||||
|
||||
minEdge = Math.Sqrt(minEdge);
|
||||
@@ -418,6 +494,7 @@ namespace TriangleNet
|
||||
{
|
||||
minAngle = degconst * Math.Acos(Math.Sqrt(minAngle));
|
||||
}
|
||||
|
||||
if (maxAngle >= 1.0)
|
||||
{
|
||||
maxAngle = 180.0;
|
||||
@@ -434,208 +511,5 @@ namespace TriangleNet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Original code with aspect ratio
|
||||
|
||||
int[] aspecttable;
|
||||
double[] ratiotable;
|
||||
|
||||
public Statistic()
|
||||
{
|
||||
angletable = new int[18];
|
||||
aspecttable = new int[16];
|
||||
ratiotable = new double[] {
|
||||
1.5, 2.0, 2.5, 3.0, 4.0, 6.0, 10.0, 15.0, 25.0, 50.0,
|
||||
100.0, 300.0, 1000.0, 10000.0, 100000.0, 0.0 };
|
||||
}
|
||||
|
||||
public void Update(Mesh mesh)
|
||||
{
|
||||
inVetrices = mesh.invertices;
|
||||
inTriangles = mesh.inelements;
|
||||
inSegments = mesh.insegments;
|
||||
inHoles = mesh.holes;
|
||||
outVertices = mesh.vertices.Count - mesh.undeads;
|
||||
outTriangles = mesh.triangles.Count;
|
||||
outEdges = (int)mesh.edges;
|
||||
boundaryEdges = (int)mesh.hullsize;
|
||||
intBoundaryEdges = mesh.subsegs.Count - (int)mesh.hullsize;
|
||||
constrainedEdges = mesh.subsegs.Count;
|
||||
|
||||
Otri triangleloop = default(Otri);
|
||||
Vertex[] p = new Vertex[3];
|
||||
double[] cossquaretable = new double[8];
|
||||
double[] dx = new double[3], dy = new double[3];
|
||||
double[] edgelength = new double[3];
|
||||
double dotproduct;
|
||||
double cossquare;
|
||||
double triarea;
|
||||
double trilongest2;
|
||||
double triminaltitude2;
|
||||
double triaspect2;
|
||||
double radconst, degconst;
|
||||
|
||||
int aspectindex;
|
||||
int tendegree;
|
||||
bool acutebiggest;
|
||||
int i, ii, j, k;
|
||||
|
||||
radconst = Math.PI / 18.0;
|
||||
degconst = 180.0 / Math.PI;
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
cossquaretable[i] = Math.Cos(radconst * (double)(i + 1));
|
||||
cossquaretable[i] = cossquaretable[i] * cossquaretable[i];
|
||||
}
|
||||
for (i = 0; i < 18; i++)
|
||||
{
|
||||
angletable[i] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
aspecttable[i] = 0;
|
||||
}
|
||||
|
||||
worstaspect = 0.0;
|
||||
minaltitude = mesh.xmax - mesh.xmin + mesh.ymax - mesh.ymin;
|
||||
minaltitude = minaltitude * minaltitude;
|
||||
shortest = minaltitude;
|
||||
longest = 0.0;
|
||||
smallestarea = minaltitude;
|
||||
biggestarea = 0.0;
|
||||
worstaspect = 0.0;
|
||||
smallestangle = 0.0;
|
||||
biggestangle = 2.0;
|
||||
acutebiggest = true;
|
||||
|
||||
triangleloop.orient = 0;
|
||||
foreach (var t in mesh.triangles)
|
||||
{
|
||||
triangleloop.triangle = t;
|
||||
p[0] = triangleloop.Org();
|
||||
p[1] = triangleloop.Dest();
|
||||
p[2] = triangleloop.Apex();
|
||||
trilongest2 = 0.0;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
j = plus1Mod3[i];
|
||||
k = minus1Mod3[i];
|
||||
dx[i] = p[j].pt.X - p[k].pt.X;
|
||||
dy[i] = p[j].pt.Y - p[k].pt.Y;
|
||||
edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i];
|
||||
if (edgelength[i] > trilongest2)
|
||||
{
|
||||
trilongest2 = edgelength[i];
|
||||
}
|
||||
if (edgelength[i] > longest)
|
||||
{
|
||||
longest = edgelength[i];
|
||||
}
|
||||
if (edgelength[i] < shortest)
|
||||
{
|
||||
shortest = edgelength[i];
|
||||
}
|
||||
}
|
||||
|
||||
//triarea = Primitives.CounterClockwise(p[0], p[1], p[2]);
|
||||
triarea = Math.Abs((p[2].pt.X - p[0].pt.X) * (p[1].pt.Y - p[0].pt.Y) -
|
||||
(p[1].pt.X - p[0].pt.X) * (p[2].pt.Y - p[0].pt.Y)) / 2.0;
|
||||
if (triarea < smallestarea)
|
||||
{
|
||||
smallestarea = triarea;
|
||||
}
|
||||
if (triarea > biggestarea)
|
||||
{
|
||||
biggestarea = triarea;
|
||||
}
|
||||
triminaltitude2 = triarea * triarea / trilongest2;
|
||||
if (triminaltitude2 < minaltitude)
|
||||
{
|
||||
minaltitude = triminaltitude2;
|
||||
}
|
||||
triaspect2 = trilongest2 / triminaltitude2;
|
||||
if (triaspect2 > worstaspect)
|
||||
{
|
||||
worstaspect = triaspect2;
|
||||
}
|
||||
aspectindex = 0;
|
||||
while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex]) && (aspectindex < 15))
|
||||
{
|
||||
aspectindex++;
|
||||
}
|
||||
aspecttable[aspectindex]++;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
j = plus1Mod3[i];
|
||||
k = minus1Mod3[i];
|
||||
dotproduct = dx[j] * dx[k] + dy[j] * dy[k];
|
||||
cossquare = dotproduct * dotproduct / (edgelength[j] * edgelength[k]);
|
||||
tendegree = 8;
|
||||
for (ii = 7; ii >= 0; ii--)
|
||||
{
|
||||
if (cossquare > cossquaretable[ii])
|
||||
{
|
||||
tendegree = ii;
|
||||
}
|
||||
}
|
||||
if (dotproduct <= 0.0)
|
||||
{
|
||||
angletable[tendegree]++;
|
||||
if (cossquare > smallestangle)
|
||||
{
|
||||
smallestangle = cossquare;
|
||||
}
|
||||
if (acutebiggest && (cossquare < biggestangle))
|
||||
{
|
||||
biggestangle = cossquare;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
angletable[17 - tendegree]++;
|
||||
if (acutebiggest || (cossquare > biggestangle))
|
||||
{
|
||||
biggestangle = cossquare;
|
||||
acutebiggest = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shortest = Math.Sqrt(shortest);
|
||||
longest = Math.Sqrt(longest);
|
||||
minaltitude = Math.Sqrt(minaltitude);
|
||||
worstaspect = Math.Sqrt(worstaspect);
|
||||
smallestarea *= 0.5;
|
||||
biggestarea *= 0.5;
|
||||
if (smallestangle >= 1.0)
|
||||
{
|
||||
smallestangle = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
smallestangle = degconst * Math.Acos(Math.Sqrt(smallestangle));
|
||||
}
|
||||
if (biggestangle >= 1.0)
|
||||
{
|
||||
biggestangle = 180.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (acutebiggest)
|
||||
{
|
||||
biggestangle = degconst * Math.Acos(Math.Sqrt(biggestangle));
|
||||
}
|
||||
else
|
||||
{
|
||||
biggestangle = 180.0 - degconst * Math.Acos(Math.Sqrt(biggestangle));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,9 @@
|
||||
<Compile Include="IO\DataWriter.cs" />
|
||||
<Compile Include="IO\FileReader.cs" />
|
||||
<Compile Include="Log\ILog.cs" />
|
||||
<Compile Include="Log\SimpleLogger.cs" />
|
||||
<Compile Include="Log\ILogItem.cs" />
|
||||
<Compile Include="Log\SimpleLog.cs" />
|
||||
<Compile Include="Log\SimpleLogItem.cs" />
|
||||
<Compile Include="NewLocation.cs" />
|
||||
<Compile Include="Quality.cs" />
|
||||
<Compile Include="Enums.cs" />
|
||||
@@ -73,6 +75,7 @@
|
||||
<Compile Include="Primitives.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Sampler.cs" />
|
||||
<Compile Include="Smoothing\ISmoother.cs" />
|
||||
<Compile Include="Statistic.cs" />
|
||||
<Compile Include="Algorithm\SweepLine.cs" />
|
||||
<Compile Include="IO\MeshData.cs" />
|
||||
|
||||
Reference in New Issue
Block a user