Rename example files.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user