Rename example files.

This commit is contained in:
wo80
2022-04-25 17:36:16 +02:00
parent 5343dcfb8c
commit 5b2d85e299
10 changed files with 495 additions and 495 deletions
+67 -111
View File
@@ -1,133 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Meshing.Algorithm;
using TriangleNet.Rendering.Text;
using TriangleNet.Tools;
namespace TriangleNet.Examples
{
using System;
using System.Collections.Generic;
using System.Linq;
using TriangleNet.Geometry;
/// <summary>
/// Scattered data interpolation without USE_Z or USE_ATTRIBS.
/// Troubleshooting: finding degenerate boundary triangles.
/// </summary>
internal class Example10
public class Example9
{
// The function we are sampling.
private static readonly Func<Point, double> F = p => Math.Sin(p.X) * Math.Cos(p.Y);
// The mesh size, for a structured grid (SIZE x SIZE) points.
private const int SIZE = 20;
public static bool Run(bool print = false)
{
// The input domain.
var r = new Rectangle(0d, 0d, 10d, 10d);
var mesh = GetScatteredDataMesh(r, out double[] data);
//var mesh = GetStructuredDataMesh(r, out double[] data);
if (print) SvgImage.Save(mesh, "example-10.svg", 500);
// The points to interpolate.
var xy = Generate.RandomPoints(50, r);
var xyData = InterpolateData((Mesh)mesh, data, xy);
double error = xy.Max(p => Math.Abs(xyData[p.ID] - F(p)));
// L2 error
//double error = Math.Sqrt(xy.Sum(p => Math.Pow(xyData[p.ID] - F(p), 2)));
// Define tolerance dependent on mesh dimensions and size.
double tolerance = 0.5 * Math.Max(r.Width, r.Height) / SIZE;
return error < tolerance;
}
private static IMesh GetStructuredDataMesh(Rectangle domain, out double[] data)
{
var mesh = GenericMesher.StructuredMesh(domain, SIZE, SIZE);
mesh.Renumber();
// Generate function values for mesh points.
data = new double[mesh.Vertices.Count];
foreach (var item in mesh.Vertices)
var pts = new List<Vertex>
{
data[item.ID] = F(item);
}
// The 4 corners of the rectangle.
new Vertex(1.5, 1.0),
new Vertex(1.5, -1.0),
new Vertex(-1.5, -1.0),
new Vertex(-1.5, 1.0),
return mesh;
}
// The edge midpoints.
new Vertex(0.0, 1.0),
new Vertex(0.0, -1.0),
new Vertex(1.5, 0.0),
new Vertex(-1.5, 0.0)
};
private static IMesh GetScatteredDataMesh(Rectangle domain, out double[] data)
{
var r = new Rectangle(domain);
var r = new Random(78403);
double h = domain.Width / SIZE;
// The original rectangle.
var poly = Rotate(pts, 0);
// Generate a rectangle boundary point set (20 points on each side).
var input = Generate.Rectangle(r, 0.5);
// Making sure we add some margin to the boundary.
h = -h / 2;
r.Resize(h, h);
int n = Math.Max(1, SIZE * SIZE - input.Points.Count);
// Add more input points (more sampling points, better interpolation).
input.Points.AddRange(Generate.RandomPoints(n, r));
var mesher = new GenericMesher(new Dwyer());
// Generate mesh.
var mesh = mesher.Triangulate(input.Points);
mesh.Renumber();
// Generate function values for mesh points.
data = new double[mesh.Vertices.Count];
foreach (var item in mesh.Vertices)
for (int i = 0; i < 10; i++)
{
data[item.ID] = F(item);
}
var mesh = poly.Triangulate();
return mesh;
}
var list = MeshValidator.GetDegenerateBoundaryTriangles(mesh);
private static double[] InterpolateData(Mesh mesh, double[] data, IEnumerable<Point> xy)
{
// The interpolated values.
var values = new double[xy.Count()];
var qtree = new TriangleQuadTree(mesh);
int i = 0;
foreach (var p in xy)
{
var tri = qtree.Query(p.X, p.Y);
// For easy access of the interpolated values.
p.ID = i;
if (tri == null)
if (print && list.Any())
{
values[i] = float.NaN;
}
else
{
values[i] = Interpolation.InterpolatePoint(tri, p, data);
Console.WriteLine("Iteration {0}: found {1} degenerate triangle(s) of {2}.",
i, list.Count(), mesh.Triangles.Count);
foreach (var t in list)
{
Console.WriteLine(" [{0} {1} {2}]",
t.GetVertexID(0),
t.GetVertexID(1),
t.GetVertexID(2));
}
}
i++;
// Random rotation.
poly = Rotate(pts, Math.PI * r.NextDouble());
}
return values;
return true;
}
/// <summary>
/// Rotate given point set around the origin.
/// </summary>
private static IPolygon Rotate(List<Vertex> points, double radians)
{
var poly = new Polygon(points.Count);
int id = 0;
foreach (var p in points)
{
double x = p.X;
double y = p.Y;
double s = Math.Sin(radians);
double c = Math.Cos(radians);
double xr = c * x - s * y;
double yr = s * x + c * y;
poly.Points.Add(new Vertex(xr, yr) { ID = id++ });
}
return poly;
}
}
}
}
+133
View File
@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Meshing.Algorithm;
using TriangleNet.Rendering.Text;
using TriangleNet.Tools;
namespace TriangleNet.Examples
{
/// <summary>
/// Scattered data interpolation without USE_Z or USE_ATTRIBS.
/// </summary>
internal class Example10
{
// The function we are sampling.
private static readonly Func<Point, double> F = p => Math.Sin(p.X) * Math.Cos(p.Y);
// The mesh size, for a structured grid (SIZE x SIZE) points.
private const int SIZE = 20;
public static bool Run(bool print = false)
{
// The input domain.
var r = new Rectangle(0d, 0d, 10d, 10d);
var mesh = GetScatteredDataMesh(r, out double[] data);
//var mesh = GetStructuredDataMesh(r, out double[] data);
if (print) SvgImage.Save(mesh, "example-10.svg", 500);
// The points to interpolate.
var xy = Generate.RandomPoints(50, r);
var xyData = InterpolateData((Mesh)mesh, data, xy);
double error = xy.Max(p => Math.Abs(xyData[p.ID] - F(p)));
// L2 error
//double error = Math.Sqrt(xy.Sum(p => Math.Pow(xyData[p.ID] - F(p), 2)));
// Define tolerance dependent on mesh dimensions and size.
double tolerance = 0.5 * Math.Max(r.Width, r.Height) / SIZE;
return error < tolerance;
}
private static IMesh GetStructuredDataMesh(Rectangle domain, out double[] data)
{
var mesh = GenericMesher.StructuredMesh(domain, SIZE, SIZE);
mesh.Renumber();
// Generate function values for mesh points.
data = new double[mesh.Vertices.Count];
foreach (var item in mesh.Vertices)
{
data[item.ID] = F(item);
}
return mesh;
}
private static IMesh GetScatteredDataMesh(Rectangle domain, out double[] data)
{
var r = new Rectangle(domain);
double h = domain.Width / SIZE;
// Generate a rectangle boundary point set (20 points on each side).
var input = Generate.Rectangle(r, 0.5);
// Making sure we add some margin to the boundary.
h = -h / 2;
r.Resize(h, h);
int n = Math.Max(1, SIZE * SIZE - input.Points.Count);
// Add more input points (more sampling points, better interpolation).
input.Points.AddRange(Generate.RandomPoints(n, r));
var mesher = new GenericMesher(new Dwyer());
// Generate mesh.
var mesh = mesher.Triangulate(input.Points);
mesh.Renumber();
// Generate function values for mesh points.
data = new double[mesh.Vertices.Count];
foreach (var item in mesh.Vertices)
{
data[item.ID] = F(item);
}
return mesh;
}
private static double[] InterpolateData(Mesh mesh, double[] data, IEnumerable<Point> xy)
{
// The interpolated values.
var values = new double[xy.Count()];
var qtree = new TriangleQuadTree(mesh);
int i = 0;
foreach (var p in xy)
{
var tri = qtree.Query(p.X, p.Y);
// For easy access of the interpolated values.
p.ID = i;
if (tri == null)
{
values[i] = float.NaN;
}
else
{
values[i] = Interpolation.InterpolatePoint(tri, p, data);
}
i++;
}
return values;
}
}
}
@@ -1,48 +0,0 @@
namespace TriangleNet.Examples
{
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Rendering.Text;
/// <summary>
/// Triangulate a polygon with hole and set minimum angle constraint.
/// </summary>
public static class Example2
{
public static bool Run(bool print = false)
{
// Generate the input geometry.
var poly = CreatePolygon();
// Set minimum angle quality option.
var quality = new QualityOptions() { MinimumAngle = 30.0 };
// Generate mesh using the polygons Triangulate extension method.
var mesh = poly.Triangulate(quality);
if (print) SvgImage.Save(mesh, "example-2.svg", 500);
return mesh.Triangles.Count > 0;
}
public static IPolygon CreatePolygon(double h = 0.2)
{
// Generate the input geometry.
var poly = new Polygon();
// Center point.
var center = new Point(0, 0);
// Inner contour (hole).
poly.Add(Generate.Circle(1.0, center, h, 1), center);
// Internal contour.
poly.Add(Generate.Circle(2.0, center, h, 2));
// Outer contour.
poly.Add(Generate.Circle(3.0, center, h, 3));
return poly;
}
}
}
+24 -30
View File
@@ -1,54 +1,48 @@
namespace TriangleNet.Examples
namespace TriangleNet.Examples
{
using System;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Rendering.Text;
using TriangleNet.Smoothing;
/// <summary>
/// Triangulate a polygon with hole with maximum area constraint, followed by mesh smoothing.
/// Triangulate a polygon with hole and set minimum angle constraint.
/// </summary>
public class Example3
public static class Example2
{
public static bool Run(bool print = false)
{
// Generate mesh.
var mesh = CreateMesh();
// Generate the input geometry.
var poly = CreatePolygon();
if (print) SvgImage.Save(mesh, "example-3.svg", 500);
// Set minimum angle quality option.
var quality = new QualityOptions() { MinimumAngle = 30.0 };
// Generate mesh using the polygons Triangulate extension method.
var mesh = poly.Triangulate(quality);
if (print) SvgImage.Save(mesh, "example-2.svg", 500);
return mesh.Triangles.Count > 0;
}
public static IMesh CreateMesh()
public static IPolygon CreatePolygon(double h = 0.2)
{
// Generate the input geometry.
var poly = Example2.CreatePolygon();
var poly = new Polygon();
// Since we want to do CVT smoothing, ensure that the mesh
// is conforming Delaunay.
var options = new ConstraintOptions() { ConformingDelaunay = true };
// Center point.
var center = new Point(0, 0);
// Set maximum area quality option (we don't need to set a minimum
// angle, since smoothing will improve the triangle shapes).
var quality = new QualityOptions()
{
// The boundary segments have a length of 0.2, so we set a
// maximum area constraint assuming equilateral triangles.
MaximumArea = (Math.Sqrt(3) / 4 * 0.2 * 0.2) * 1.45
};
// Inner contour (hole).
poly.Add(Generate.Circle(1.0, center, h, 1), center);
// Generate mesh using the polygons Triangulate extension method.
var mesh = poly.Triangulate(options, quality);
// Internal contour.
poly.Add(Generate.Circle(2.0, center, h, 2));
var smoother = new SimpleSmoother();
// Outer contour.
poly.Add(Generate.Circle(3.0, center, h, 3));
// Smooth mesh.
smoother.Smooth(mesh, 25);
return mesh;
return poly;
}
}
}
}
+26 -43
View File
@@ -1,71 +1,54 @@
namespace TriangleNet.Examples
{
using TriangleNet;
using System;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Rendering.Text;
using TriangleNet.Smoothing;
/// <summary>
/// Refine only a part of a polygon mesh by using region pointers and an area constraint.
/// Triangulate a polygon with hole with maximum area constraint, followed by mesh smoothing.
/// </summary>
public class Example4
public class Example3
{
public static bool Run(bool print = false)
{
// Generate the input geometry.
var poly = CreatePolygon();
// Define regions (first one defines the area constraint).
poly.Regions.Add(new RegionPointer(1.5, 0.0, 1, 0.01));
poly.Regions.Add(new RegionPointer(2.5, 0.0, 2));
// Generate mesh.
var mesh = CreateMesh();
// Set quality and constraint options.
var options = new ConstraintOptions()
{
ConformingDelaunay = true
};
var quality = new QualityOptions()
{
MinimumAngle = 25.0,
VariableArea = true
};
//quality.UserTest = (t, area) => t.Label == 1 && area > 0.01;
var mesh = poly.Triangulate(options, quality);
var smoother = new SimpleSmoother();
smoother.Smooth(mesh, 5);
if (print) SvgImage.Save(mesh, "example-4.svg", 500);
if (print) SvgImage.Save(mesh, "example-3.svg", 500);
return mesh.Triangles.Count > 0;
}
public static IPolygon CreatePolygon()
public static IMesh CreateMesh()
{
// Generate three concentric circles.
var poly = new Polygon();
// Generate the input geometry.
var poly = Example2.CreatePolygon();
// Center point.
var center = new Point(0, 0);
// Since we want to do CVT smoothing, ensure that the mesh
// is conforming Delaunay.
var options = new ConstraintOptions() { ConformingDelaunay = true };
// Inner contour (hole).
poly.Add(Generate.Circle(1.0, center, 0.1, 1), center);
// Set maximum area quality option (we don't need to set a minimum
// angle, since smoothing will improve the triangle shapes).
var quality = new QualityOptions()
{
// The boundary segments have a length of 0.2, so we set a
// maximum area constraint assuming equilateral triangles.
MaximumArea = (Math.Sqrt(3) / 4 * 0.2 * 0.2) * 1.45
};
// Internal contour.
poly.Add(Generate.Circle(2.0, center, 0.1, 2));
// Generate mesh using the polygons Triangulate extension method.
var mesh = poly.Triangulate(options, quality);
// Outer contour.
poly.Add(Generate.Circle(3.0, center, 0.3, 3));
var smoother = new SimpleSmoother();
// Note that the outer contour has a larger segment size!
// Smooth mesh.
smoother.Smooth(mesh, 25);
return poly;
return mesh;
}
}
}
+40 -67
View File
@@ -1,98 +1,71 @@
namespace TriangleNet.Examples
{
using System.Collections.Generic;
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Meshing.Iterators;
using TriangleNet.Rendering.Text;
using TriangleNet.Smoothing;
/// <summary>
/// Two ways finding boundary triangles.
/// Refine only a part of a polygon mesh by using region pointers and an area constraint.
/// </summary>
public static class Example5
public class Example4
{
public static bool Run(bool print = false)
{
var mesh = Example3.CreateMesh();
// Generate the input geometry.
var poly = CreatePolygon();
// Define regions (first one defines the area constraint).
poly.Regions.Add(new RegionPointer(1.5, 0.0, 1, 0.01));
poly.Regions.Add(new RegionPointer(2.5, 0.0, 2));
FindBoundary1(mesh);
// Set quality and constraint options.
var options = new ConstraintOptions()
{
ConformingDelaunay = true
};
if (print) SvgImage.Save(mesh, "example-5-1.svg", 500, true, false);
var quality = new QualityOptions()
{
MinimumAngle = 25.0,
VariableArea = true
};
FindBoundary2(mesh);
//quality.UserTest = (t, area) => t.Label == 1 && area > 0.01;
if (print) SvgImage.Save(mesh, "example-5-2.svg", 500, true, false);
var mesh = poly.Triangulate(options, quality);
var smoother = new SimpleSmoother();
smoother.Smooth(mesh, 5);
if (print) SvgImage.Save(mesh, "example-4.svg", 500);
return mesh.Triangles.Count > 0;
}
/// <summary>
/// Find boundary triangles using segments.
/// </summary>
private static void FindBoundary1(IMesh mesh, bool neigbours = true)
public static IPolygon CreatePolygon()
{
mesh.Renumber();
// Generate three concentric circles.
var poly = new Polygon();
var cache = new List<Vertex>(mesh.Segments.Count + 1);
// Center point.
var center = new Point(0, 0);
var circulator = new VertexCirculator((Mesh)mesh);
// Inner contour (hole).
poly.Add(Generate.Circle(1.0, center, 0.1, 1), center);
foreach (var s in mesh.Segments)
{
int label = s.Label;
// Internal contour.
poly.Add(Generate.Circle(2.0, center, 0.1, 2));
for (int i = 0; i < 2; i++)
{
var vertex = s.GetVertex(i);
// Outer contour.
poly.Add(Generate.Circle(3.0, center, 0.3, 3));
// Check the vertex ID to see if it was processed already.
if (vertex.ID >= 0)
{
var star = circulator.EnumerateTriangles(vertex);
// Note that the outer contour has a larger segment size!
foreach (var triangle in star)
{
triangle.Label = label;
}
// Mark the vertex as "processed".
vertex.ID = -vertex.ID;
cache.Add(vertex);
}
}
}
// Undo the vertex ID changes.
foreach (var vertex in cache)
{
vertex.ID = -vertex.ID;
}
}
/// <summary>
/// Find boundary triangles using vertices.
/// </summary>
private static void FindBoundary2(IMesh mesh)
{
var circulator = new VertexCirculator((Mesh)mesh);
foreach (var vertex in mesh.Vertices)
{
int label = vertex.Label;
if (label > 0)
{
var star = circulator.EnumerateTriangles(vertex);
foreach (var triangle in star)
{
triangle.Label = label;
}
}
}
return poly;
}
}
}
+73 -31
View File
@@ -1,56 +1,98 @@
namespace TriangleNet.Examples
namespace TriangleNet.Examples
{
using System.Linq;
using System.Collections.Generic;
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Meshing.Iterators;
using TriangleNet.Rendering.Text;
using TriangleNet.Tools;
using TriangleNet.Topology;
/// <summary>
/// Boolean operations on mesh regions (intersection, difference, xor).
/// Two ways finding boundary triangles.
/// </summary>
public static class Example6
public static class Example5
{
public static bool Run(bool print = false)
{
// Generate the input geometry.
var polygon = new Polygon(8, true);
var mesh = Example3.CreateMesh();
// Two intersecting rectangles.
var A = Generate.Rectangle(0d, 0d, 4d, 4d, label: 1);
var B = Generate.Rectangle(1d, 1d, 4d, 4d, label: 2);
FindBoundary1(mesh);
polygon.Add(A);
polygon.Add(B);
if (print) SvgImage.Save(mesh, "example-5-1.svg", 500, true, false);
// Generate mesh.
var mesh = (Mesh)polygon.Triangulate();
FindBoundary2(mesh);
if (print) SvgImage.Save(mesh, "example-6.svg", 500);
if (print) SvgImage.Save(mesh, "example-5-2.svg", 500, true, false);
// Find a seeding triangle (in this case, the point (2, 2) lies in
// both rectangles).
var seed = (new TriangleQuadTree(mesh)).Query(2.0, 2.0) as Triangle;
return mesh.Triangles.Count > 0;
}
var iterator = new RegionIterator(mesh);
/// <summary>
/// Find boundary triangles using segments.
/// </summary>
private static void FindBoundary1(IMesh mesh, bool neigbours = true)
{
mesh.Renumber();
iterator.Process(seed, t => t.Label ^= 1, 1);
iterator.Process(seed, t => t.Label ^= 2, 2);
var cache = new List<Vertex>(mesh.Segments.Count + 1);
// At this point, all triangles will have label 1, 2 or 3 (= 1 xor 2).
var circulator = new VertexCirculator((Mesh)mesh);
// The intersection of A and B.
var intersection = mesh.Triangles.Where(t => t.Label == 3);
foreach (var s in mesh.Segments)
{
int label = s.Label;
// The difference A \ B.
var difference = mesh.Triangles.Where(t => t.Label == 1);
for (int i = 0; i < 2; i++)
{
var vertex = s.GetVertex(i);
// The xor of A and B.
var xor = mesh.Triangles.Where(t => t.Label == 1 || t.Label == 2);
// Check the vertex ID to see if it was processed already.
if (vertex.ID >= 0)
{
var star = circulator.EnumerateTriangles(vertex);
return intersection.Any() && difference.Any() && xor.Any();
foreach (var triangle in star)
{
triangle.Label = label;
}
// Mark the vertex as "processed".
vertex.ID = -vertex.ID;
cache.Add(vertex);
}
}
}
// Undo the vertex ID changes.
foreach (var vertex in cache)
{
vertex.ID = -vertex.ID;
}
}
/// <summary>
/// Find boundary triangles using vertices.
/// </summary>
private static void FindBoundary2(IMesh mesh)
{
var circulator = new VertexCirculator((Mesh)mesh);
foreach (var vertex in mesh.Vertices)
{
int label = vertex.Label;
if (label > 0)
{
var star = circulator.EnumerateTriangles(vertex);
foreach (var triangle in star)
{
triangle.Label = label;
}
}
}
}
}
}
}
+31 -48
View File
@@ -1,73 +1,56 @@
namespace TriangleNet.Examples
namespace TriangleNet.Examples
{
using System;
using System.Linq;
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Meshing.Iterators;
using TriangleNet.Rendering.Text;
using TriangleNet.Tools;
using TriangleNet.Topology;
/// <summary>
/// Using a user test function to define a maximum edge length constraint.
/// Boolean operations on mesh regions (intersection, difference, xor).
/// </summary>
public static class Example7
public static class Example6
{
const double MAX_EDGE_LENGTH = 0.2;
public static bool Run(bool print = false)
{
var poly = new Polygon();
// Generate the input geometry.
poly.Add(Generate.Rectangle(0.0, 0.0, 1.0, 1.0));
var polygon = new Polygon(8, true);
// Set minimum angle quality option, ignoring holes.
var quality = new QualityOptions()
{
UserTest = MaxEdgeLength
};
// Two intersecting rectangles.
var A = Generate.Rectangle(0d, 0d, 4d, 4d, label: 1);
var B = Generate.Rectangle(1d, 1d, 4d, 4d, label: 2);
// Generate mesh using the polygons Triangulate extension method.
var mesh = (Mesh)poly.Triangulate(quality);
polygon.Add(A);
polygon.Add(B);
// Validate.
foreach (var e in EdgeIterator.EnumerateEdges(mesh))
{
double length = Math.Sqrt(DistSqr(e.GetVertex(0), e.GetVertex(1)));
// Generate mesh.
var mesh = (Mesh)polygon.Triangulate();
if (length > MAX_EDGE_LENGTH)
{
return false;
}
}
if (print) SvgImage.Save(mesh, "example-6.svg", 500);
if (print) SvgImage.Save(mesh, "example-7.svg", 500);
// Find a seeding triangle (in this case, the point (2, 2) lies in
// both rectangles).
var seed = (new TriangleQuadTree(mesh)).Query(2.0, 2.0) as Triangle;
return true;
}
var iterator = new RegionIterator(mesh);
static bool MaxEdgeLength(ITriangle tri, double area)
{
var p0 = tri.GetVertex(0);
var p1 = tri.GetVertex(1);
var p2 = tri.GetVertex(2);
iterator.Process(seed, t => t.Label ^= 1, 1);
iterator.Process(seed, t => t.Label ^= 2, 2);
var s1 = DistSqr(p0, p1);
var s2 = DistSqr(p1, p2);
var s3 = DistSqr(p2, p0);
// At this point, all triangles will have label 1, 2 or 3 (= 1 xor 2).
// Comparing against squared max leg length.
var maxlen = MAX_EDGE_LENGTH * MAX_EDGE_LENGTH;
// The intersection of A and B.
var intersection = mesh.Triangles.Where(t => t.Label == 3);
return s1 > maxlen || s2 > maxlen || s3 > maxlen;
}
// The difference A \ B.
var difference = mesh.Triangles.Where(t => t.Label == 1);
static double DistSqr(Vertex a, Vertex b)
{
var dx = a.X - b.X;
var dy = a.Y - b.Y;
// The xor of A and B.
var xor = mesh.Triangles.Where(t => t.Label == 1 || t.Label == 2);
return dx * dx + dy * dy;
return intersection.Any() && difference.Any() && xor.Any();
}
}
}
}
+47 -58
View File
@@ -2,83 +2,72 @@
namespace TriangleNet.Examples
{
using System;
using System.Collections.Generic;
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Meshing.Iterators;
using TriangleNet.Tools;
using TriangleNet.Rendering.Text;
/// <summary>
/// Compute the adjacency matrix of the mesh vertices.
/// Using a user test function to define a maximum edge length constraint.
/// </summary>
public class Example8
public static class Example7
{
public static bool Run()
const double MAX_EDGE_LENGTH = 0.2;
public static bool Run(bool print = false)
{
var mesh = (Mesh)Example3.CreateMesh();
var poly = new Polygon();
return FindAdjacencyMatrix(mesh);
}
// Generate the input geometry.
poly.Add(Generate.Rectangle(0.0, 0.0, 1.0, 1.0));
private static bool FindAdjacencyMatrix(Mesh mesh)
{
mesh.Renumber();
var ap = new List<int>(mesh.Vertices.Count); // Column pointers.
var ai = new List<int>(4 * mesh.Vertices.Count); // Row indices.
var circulator = new VertexCirculator(mesh);
int k = 0;
foreach (var vertex in mesh.Vertices)
// Set minimum angle quality option, ignoring holes.
var quality = new QualityOptions()
{
var star = circulator.EnumerateVertices(vertex);
UserTest = MaxEdgeLength
};
ap.Add(k);
// Generate mesh using the polygons Triangulate extension method.
var mesh = (Mesh)poly.Triangulate(quality);
// Each vertex is adjacent to itself.
ai.Add(vertex.ID);
k++;
foreach (var item in star)
{
ai.Add(item.ID);
k++;
}
}
ap.Add(k);
var matrix1 = new AdjacencyMatrix(ap.ToArray(), ai.ToArray());
var matrix2 = new AdjacencyMatrix(mesh);
// Column pointers should be exactly the same.
if (!CompareArray(matrix1.ColumnPointers, matrix2.ColumnPointers))
// Validate.
foreach (var e in EdgeIterator.EnumerateEdges(mesh))
{
return false;
}
double length = Math.Sqrt(DistSqr(e.GetVertex(0), e.GetVertex(1)));
return true;
}
private static bool CompareArray(int[] a, int[] b)
{
int length = a.Length;
if (b.Length != length)
{
return false;
}
for (int i = 0; i < length; i++)
{
if (a[i] != b[i])
if (length > MAX_EDGE_LENGTH)
{
return false;
}
}
if (print) SvgImage.Save(mesh, "example-7.svg", 500);
return true;
}
static bool MaxEdgeLength(ITriangle tri, double area)
{
var p0 = tri.GetVertex(0);
var p1 = tri.GetVertex(1);
var p2 = tri.GetVertex(2);
var s1 = DistSqr(p0, p1);
var s2 = DistSqr(p1, p2);
var s3 = DistSqr(p2, p0);
// Comparing against squared max leg length.
var maxlen = MAX_EDGE_LENGTH * MAX_EDGE_LENGTH;
return s1 > maxlen || s2 > maxlen || s3 > maxlen;
}
static double DistSqr(Vertex a, Vertex b)
{
var dx = a.X - b.X;
var dy = a.Y - b.Y;
return dx * dx + dy * dy;
}
}
}
+54 -59
View File
@@ -3,87 +3,82 @@ namespace TriangleNet.Examples
{
using System;
using System.Collections.Generic;
using System.Linq;
using TriangleNet.Geometry;
using TriangleNet;
using TriangleNet.Meshing.Iterators;
using TriangleNet.Tools;
/// <summary>
/// Troubleshooting: finding degenerate boundary triangles.
/// Compute the adjacency matrix of the mesh vertices.
/// </summary>
public class Example9
public class Example8
{
public static bool Run(bool print = false)
public static bool Run()
{
var pts = new List<Vertex>
var mesh = (Mesh)Example3.CreateMesh();
return FindAdjacencyMatrix(mesh);
}
private static bool FindAdjacencyMatrix(Mesh mesh)
{
mesh.Renumber();
var ap = new List<int>(mesh.Vertices.Count); // Column pointers.
var ai = new List<int>(4 * mesh.Vertices.Count); // Row indices.
var circulator = new VertexCirculator(mesh);
int k = 0;
foreach (var vertex in mesh.Vertices)
{
// The 4 corners of the rectangle.
new Vertex(1.5, 1.0),
new Vertex(1.5, -1.0),
new Vertex(-1.5, -1.0),
new Vertex(-1.5, 1.0),
var star = circulator.EnumerateVertices(vertex);
// The edge midpoints.
new Vertex(0.0, 1.0),
new Vertex(0.0, -1.0),
new Vertex(1.5, 0.0),
new Vertex(-1.5, 0.0)
};
ap.Add(k);
var r = new Random(78403);
// Each vertex is adjacent to itself.
ai.Add(vertex.ID);
k++;
// The original rectangle.
var poly = Rotate(pts, 0);
for (int i = 0; i < 10; i++)
{
var mesh = poly.Triangulate();
var list = MeshValidator.GetDegenerateBoundaryTriangles(mesh);
if (print && list.Any())
foreach (var item in star)
{
Console.WriteLine("Iteration {0}: found {1} degenerate triangle(s) of {2}.",
i, list.Count(), mesh.Triangles.Count);
foreach (var t in list)
{
Console.WriteLine(" [{0} {1} {2}]",
t.GetVertexID(0),
t.GetVertexID(1),
t.GetVertexID(2));
}
ai.Add(item.ID);
k++;
}
}
// Random rotation.
poly = Rotate(pts, Math.PI * r.NextDouble());
ap.Add(k);
var matrix1 = new AdjacencyMatrix(ap.ToArray(), ai.ToArray());
var matrix2 = new AdjacencyMatrix(mesh);
// Column pointers should be exactly the same.
if (!CompareArray(matrix1.ColumnPointers, matrix2.ColumnPointers))
{
return false;
}
return true;
}
/// <summary>
/// Rotate given point set around the origin.
/// </summary>
private static IPolygon Rotate(List<Vertex> points, double radians)
private static bool CompareArray(int[] a, int[] b)
{
var poly = new Polygon(points.Count);
int length = a.Length;
int id = 0;
foreach (var p in points)
if (b.Length != length)
{
double x = p.X;
double y = p.Y;
double s = Math.Sin(radians);
double c = Math.Cos(radians);
double xr = c * x - s * y;
double yr = s * x + c * y;
poly.Points.Add(new Vertex(xr, yr) { ID = id++ });
return false;
}
return poly;
for (int i = 0; i < length; i++)
{
if (a[i] != b[i])
{
return false;
}
}
return true;
}
}
}