Add examples project.

This commit is contained in:
wo80
2022-02-15 17:45:36 +01:00
parent 940ae18061
commit ea6d39e1da
16 changed files with 730 additions and 27 deletions
@@ -0,0 +1,31 @@
namespace TriangleNet.Examples
{
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Meshing.Algorithm;
using TriangleNet.Rendering.Text;
/// <summary>
/// Simple point set triangulation.
/// </summary>
public class Example1
{
public static void Run(bool print = false)
{
// Generate points.
var points = Generate.RandomPoints(50, new Rectangle(0, 0, 100, 100));
// Choose triangulator: Incremental, SweepLine or Dwyer.
var triangulator = new Dwyer();
// Generate a default mesher.
var mesher = new GenericMesher(triangulator);
// Generate mesh.
var mesh = mesher.Triangulate(points);
if (print) SvgImage.Save(mesh, "example-1.png", 500);
}
}
}
@@ -0,0 +1,46 @@
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 void 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.png", 500);
}
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;
}
}
}
@@ -0,0 +1,52 @@
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.
/// </summary>
public class Example3
{
public static void Run(bool print = false)
{
// Generate mesh.
var mesh = CreateMesh();
if (print) SvgImage.Save(mesh, "example-3.png", 500);
}
public static IMesh CreateMesh()
{
// Generate the input geometry.
var poly = Example2.CreatePolygon();
// Since we want to do CVT smoothing, ensure that the mesh
// is conforming Delaunay.
var options = new ConstraintOptions() { ConformingDelaunay = true };
// 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
};
// Generate mesh using the polygons Triangulate extension method.
var mesh = poly.Triangulate(options, quality);
var smoother = new SimpleSmoother();
// Smooth mesh.
smoother.Smooth(mesh, 25);
return mesh;
}
}
}
@@ -0,0 +1,69 @@
namespace TriangleNet.Examples
{
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Rendering.Text;
using TriangleNet.Smoothing;
/// <summary>
/// Refine only a part of a polygon mesh be using region pointers and an area constraint.
/// </summary>
public class Example4
{
public static void 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));
// 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.png", 500);
}
public static IPolygon CreatePolygon()
{
// Generate three concentric circles.
var poly = new Polygon();
// Center point.
var center = new Point(0, 0);
// Inner contour (hole).
poly.Add(Generate.Circle(1.0, center, 0.1, 1), center);
// Internal contour.
poly.Add(Generate.Circle(2.0, center, 0.1, 2));
// Outer contour.
poly.Add(Generate.Circle(3.0, center, 0.3, 3));
// Note that the outer contour has a larger segment size!
return poly;
}
}
}
@@ -0,0 +1,96 @@
namespace TriangleNet.Examples
{
using System.Collections.Generic;
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Meshing.Iterators;
using TriangleNet.Rendering.Text;
/// <summary>
/// Two ways finding boundary triangles.
/// </summary>
public static class Example5
{
public static void Run(bool print = false)
{
var mesh = Example3.CreateMesh();
FindBoundary1(mesh);
if (print) SvgImage.Save(mesh, "example-5-1.png", 500, true, false);
FindBoundary2(mesh);
if (print) SvgImage.Save(mesh, "example-5-2.png", 500, true, false);
}
/// <summary>
/// Find boundary triangles using segments.
/// </summary>
private static void FindBoundary1(IMesh mesh, bool neigbours = true)
{
mesh.Renumber();
var cache = new List<Vertex>(mesh.Segments.Count + 1);
var circulator = new VertexCirculator((Mesh)mesh);
foreach (var s in mesh.Segments)
{
int label = s.Label;
for (int i = 0; i < 2; i++)
{
var vertex = s.GetVertex(i);
// Check the vertex ID to see if it was processed already.
if (vertex.ID >= 0)
{
var star = circulator.EnumerateTriangles(vertex);
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;
}
}
}
}
}
}
@@ -0,0 +1,51 @@
namespace TriangleNet.Examples
{
using System.Linq;
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.Meshing.Iterators;
using TriangleNet.Tools;
using TriangleNet.Topology;
/// <summary>
/// Boolean operation on mesh regions (intersection, difference, xor).
/// </summary>
public static class Example6
{
public static void Run() //FindRegions()
{
// Generate the input geometry.
var polygon = new Polygon(8, true);
// Two intersecting rectangles.
var A = Generate.Rectangle(0.0, 4.0, 4.0, 0.0, 1);
var B = Generate.Rectangle(1.0, 5.0, 3.0, 1.0, 2);
polygon.Add(A);
polygon.Add(B);
// Generate mesh.
var mesh = (Mesh)polygon.Triangulate();
// 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;
var iterator = new RegionIterator(mesh);
iterator.Process(seed, t => t.Label ^= 1, 1);
iterator.Process(seed, t => t.Label ^= 2, 2);
// At this point, all triangles will have label 1, 2 or 3 (= 1 xor 2).
// The intersection of A and B.
var intersection = mesh.Triangles.Where(t => t.Label == 3);
// The difference A \ B.
var difference = mesh.Triangles.Where(t => t.Label == 1);
// The xor of A and B.
var xor = mesh.Triangles.Where(t => t.Label == 1 || t.Label == 2);
}
}
}
@@ -0,0 +1,58 @@
namespace TriangleNet.Examples
{
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Rendering.Text;
/// <summary>
/// Using a user test function to define a maximum edge length constraint.
/// </summary>
public static class Example7
{
const double MAX_EDGE_LENGTH = 0.2;
public static void Run(bool print = false)
{
var poly = new Polygon();
// Generate the input geometry.
poly.Add(Generate.Rectangle(0.0, 1.0, 1.0, 0.0));
// Set minimum angle quality option, ignoring holes.
var quality = new QualityOptions()
{
UserTest = MaxEdgeLength
};
// Generate mesh using the polygons Triangulate extension method.
var mesh = poly.Triangulate(quality);
if (print) SvgImage.Save(mesh, "example-7.svg", 500);
}
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;
}
}
}
@@ -0,0 +1,82 @@
namespace TriangleNet.Examples
{
using System;
using System.Collections.Generic;
using TriangleNet;
using TriangleNet.Meshing.Iterators;
using TriangleNet.Tools;
/// <summary>
/// Compute the adjacency matrix of the mesh vertices.
/// </summary>
public class Example8
{
public static void Run()
{
var mesh = (Mesh)Example3.CreateMesh();
FindAdjacencyMatrix(mesh);
}
private static void 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)
{
var star = circulator.EnumerateVertices(vertex);
ap.Add(k);
// 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))
{
Console.WriteLine("Something's wrong in here ...");
}
}
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])
{
return false;
}
}
return true;
}
}
}
@@ -0,0 +1,84 @@
namespace TriangleNet.Examples
{
using System;
using System.Collections.Generic;
using System.Linq;
using TriangleNet.Geometry;
/// <summary>
/// Troubleshooting: finding degenerate boundary triangles.
/// </summary>
public class Example9
{
public static void Run()
{
var pts = new List<Vertex>
{
// The 4 corners of the square.
new Vertex(1.5, 1.0),
new Vertex(1.5, -1.0),
new Vertex(-1.5, -1.0),
new Vertex(-1.5, 1.0),
// 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)
};
var r = new Random(78403);
// 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 (list.Any())
{
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));
}
}
// Random rotation.
poly = Rotate(pts, Math.PI * r.NextDouble());
}
}
/// <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 xr = Math.Cos(radians) * x - Math.Sin(radians) * y;
double yr = Math.Sin(radians) * x + Math.Cos(radians) * y;
poly.Points.Add(new Vertex(xr, yr) { ID = id++ });
}
return poly;
}
}
}
+75
View File
@@ -0,0 +1,75 @@
namespace TriangleNet
{
using System;
using System.Collections.Generic;
using TriangleNet.Geometry;
static class Generate
{
private const int RANDOM_SEED = 63841;
public static List<Vertex> RandomPoints(int n, Rectangle bounds,
int seed = RANDOM_SEED)
{
var points = new List<Vertex>(n);
var xmin = bounds.Left;
var ymin = bounds.Bottom;
var width = bounds.Width;
var height = bounds.Height;
var random = new Random(seed);
for (int i = 0; i < n; i++)
{
double x = random.NextDouble();
double y = random.NextDouble();
points.Add(new Vertex(xmin + x * width, ymin + y * height));
}
return points;
}
public static Contour Rectangle(double left, double top,
double right, double bottom, int mark = 0)
{
var points = new List<Vertex>(4);
points.Add(new Vertex(left, top, mark));
points.Add(new Vertex(right, top, mark));
points.Add(new Vertex(right, bottom, mark));
points.Add(new Vertex(left, bottom, mark));
return new Contour(points, mark, true);
}
/// <summary>
/// Create a circular contour.
/// </summary>
/// <param name="r">The radius.</param>
/// <param name="center">The center point.</param>
/// <param name="h">The desired segment length.</param>
/// <param name="label">The boundary label.</param>
/// <returns>A circular contour.</returns>
public static Contour Circle(double r, Point center, double h, int label = 0)
{
int n = (int)(2 * Math.PI * r / h);
var points = new List<Vertex>(n);
double x, y, dphi = 2 * Math.PI / n;
for (int i = 0; i < n; i++)
{
x = center.X + r * Math.Cos(i * dphi);
y = center.Y + r * Math.Sin(i * dphi);
points.Add(new Vertex(x, y, label));
}
return new Contour(points, label, true);
}
}
}
+21
View File
@@ -0,0 +1,21 @@
namespace TriangleNet
{
using TriangleNet.Examples;
class Program
{
static void Main(string[] args)
{
Example1.Run();
Example2.Run();
Example3.Run();
Example4.Run();
Example5.Run();
Example8.Run();
Example6.Run();
Example7.Run();
Example9.Run();
}
}
}
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>TriangleNet</RootNamespace>
<OutDir>$(SolutionDir)bin\$(Configuration)</OutDir>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Triangle.Rendering\Text\FormattingStreamWriter.cs" Link="Rendering\FormattingStreamWriter.cs" />
<Compile Include="..\Triangle.Rendering\Text\SvgImage.cs" Link="Rendering\SvgImage.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Triangle\Triangle.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Rendering\" />
</ItemGroup>
</Project>