From fd205fbb9c69d974fd3e52c1c4028ed1fba92d87 Mon Sep 17 00:00:00 2001 From: wo80 Date: Sat, 5 Mar 2022 21:13:52 +0100 Subject: [PATCH] Update examples (add interpolation example 10). --- src/Triangle.Examples/Examples/Example1.cs | 2 +- src/Triangle.Examples/Examples/Example10.cs | 125 ++++++++++++++++++++ src/Triangle.Examples/Examples/Example2.cs | 2 +- src/Triangle.Examples/Examples/Example3.cs | 2 +- src/Triangle.Examples/Examples/Example4.cs | 2 +- src/Triangle.Examples/Examples/Example5.cs | 2 +- src/Triangle.Examples/Examples/Example6.cs | 11 +- src/Triangle.Examples/Examples/Example7.cs | 2 +- src/Triangle.Examples/Examples/Example8.cs | 10 +- src/Triangle.Examples/Program.cs | 7 +- src/Triangle/Tools/Interpolation.cs | 111 ++++++++++------- 11 files changed, 216 insertions(+), 60 deletions(-) create mode 100644 src/Triangle.Examples/Examples/Example10.cs diff --git a/src/Triangle.Examples/Examples/Example1.cs b/src/Triangle.Examples/Examples/Example1.cs index 5419d48..82d09d5 100644 --- a/src/Triangle.Examples/Examples/Example1.cs +++ b/src/Triangle.Examples/Examples/Example1.cs @@ -27,7 +27,7 @@ namespace TriangleNet.Examples if (print) SvgImage.Save(mesh, "example-1.svg", 500); - return true; + return mesh.Triangles.Count > 0; } } } diff --git a/src/Triangle.Examples/Examples/Example10.cs b/src/Triangle.Examples/Examples/Example10.cs new file mode 100644 index 0000000..ff13379 --- /dev/null +++ b/src/Triangle.Examples/Examples/Example10.cs @@ -0,0 +1,125 @@ +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 +{ + /// + /// Scattered data interpolation without USE_Z or USE_ATTRIBS. + /// + internal class Example10 + { + // The function we are sampling. + private static readonly Func F = p => Math.Sin(p.X) * Math.Cos(p.Y); + + 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))); + + return error < 0.5; + } + + private static IMesh GetStructuredDataMesh(Rectangle domain, out double[] data) + { + var mesh = GenericMesher.StructuredMesh(domain, 20, 20); + + 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 / 20; + + // 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); + + // Add more input points (more sampling points, better interpolation). + input.Points.AddRange(Generate.RandomPoints(350, 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 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; + } + } +} \ No newline at end of file diff --git a/src/Triangle.Examples/Examples/Example2.cs b/src/Triangle.Examples/Examples/Example2.cs index e3dd6f1..c497ccf 100644 --- a/src/Triangle.Examples/Examples/Example2.cs +++ b/src/Triangle.Examples/Examples/Example2.cs @@ -22,7 +22,7 @@ if (print) SvgImage.Save(mesh, "example-2.svg", 500); - return true; + return mesh.Triangles.Count > 0; } public static IPolygon CreatePolygon(double h = 0.2) diff --git a/src/Triangle.Examples/Examples/Example3.cs b/src/Triangle.Examples/Examples/Example3.cs index 74fd070..7e0182d 100644 --- a/src/Triangle.Examples/Examples/Example3.cs +++ b/src/Triangle.Examples/Examples/Example3.cs @@ -19,7 +19,7 @@ namespace TriangleNet.Examples if (print) SvgImage.Save(mesh, "example-3.svg", 500); - return true; + return mesh.Triangles.Count > 0; } public static IMesh CreateMesh() diff --git a/src/Triangle.Examples/Examples/Example4.cs b/src/Triangle.Examples/Examples/Example4.cs index cc1fff6..d3c122a 100644 --- a/src/Triangle.Examples/Examples/Example4.cs +++ b/src/Triangle.Examples/Examples/Example4.cs @@ -43,7 +43,7 @@ namespace TriangleNet.Examples if (print) SvgImage.Save(mesh, "example-4.svg", 500); - return true; + return mesh.Triangles.Count > 0; } public static IPolygon CreatePolygon() diff --git a/src/Triangle.Examples/Examples/Example5.cs b/src/Triangle.Examples/Examples/Example5.cs index ac9cbd2..17540af 100644 --- a/src/Triangle.Examples/Examples/Example5.cs +++ b/src/Triangle.Examples/Examples/Example5.cs @@ -25,7 +25,7 @@ namespace TriangleNet.Examples if (print) SvgImage.Save(mesh, "example-5-2.svg", 500, true, false); - return true; + return mesh.Triangles.Count > 0; } /// diff --git a/src/Triangle.Examples/Examples/Example6.cs b/src/Triangle.Examples/Examples/Example6.cs index 739fafa..0ed1b2c 100644 --- a/src/Triangle.Examples/Examples/Example6.cs +++ b/src/Triangle.Examples/Examples/Example6.cs @@ -4,6 +4,7 @@ using TriangleNet; using TriangleNet.Geometry; using TriangleNet.Meshing.Iterators; + using TriangleNet.Rendering.Text; using TriangleNet.Tools; using TriangleNet.Topology; @@ -12,14 +13,14 @@ /// public static class Example6 { - public static bool Run() + public static bool Run(bool print = true) { // Generate the input geometry. var polygon = new Polygon(8, true); // Two intersecting rectangles. - var A = Generate.Rectangle(0.0, 0.0, 4.0, 4.0, 1); - var B = Generate.Rectangle(1.0, 1.0, 4.0, 4.0, 2); + var A = Generate.Rectangle(0d, 0d, 4d, 4d, label: 1); + var B = Generate.Rectangle(1d, 1d, 4d, 4d, label: 2); polygon.Add(A); polygon.Add(B); @@ -27,6 +28,8 @@ // Generate mesh. var mesh = (Mesh)polygon.Triangulate(); + if (print) SvgImage.Save(mesh, "example-6.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; @@ -47,7 +50,7 @@ // The xor of A and B. var xor = mesh.Triangles.Where(t => t.Label == 1 || t.Label == 2); - return true; + return intersection.Any() && difference.Any() && xor.Any(); } } } \ No newline at end of file diff --git a/src/Triangle.Examples/Examples/Example7.cs b/src/Triangle.Examples/Examples/Example7.cs index 06bedc6..cdc2f8f 100644 --- a/src/Triangle.Examples/Examples/Example7.cs +++ b/src/Triangle.Examples/Examples/Example7.cs @@ -37,7 +37,7 @@ namespace TriangleNet.Examples if (length > MAX_EDGE_LENGTH) { - Console.WriteLine("Something's wrong in here ..."); + return false; } } diff --git a/src/Triangle.Examples/Examples/Example8.cs b/src/Triangle.Examples/Examples/Example8.cs index 8d131ba..f0a16a5 100644 --- a/src/Triangle.Examples/Examples/Example8.cs +++ b/src/Triangle.Examples/Examples/Example8.cs @@ -16,12 +16,10 @@ namespace TriangleNet.Examples { var mesh = (Mesh)Example3.CreateMesh(); - FindAdjacencyMatrix(mesh); - - return true; + return FindAdjacencyMatrix(mesh); } - private static void FindAdjacencyMatrix(Mesh mesh) + private static bool FindAdjacencyMatrix(Mesh mesh) { mesh.Renumber(); @@ -57,8 +55,10 @@ namespace TriangleNet.Examples // Column pointers should be exactly the same. if (!CompareArray(matrix1.ColumnPointers, matrix2.ColumnPointers)) { - Console.WriteLine("Something's wrong in here ..."); + return false; } + + return true; } private static bool CompareArray(int[] a, int[] b) diff --git a/src/Triangle.Examples/Program.cs b/src/Triangle.Examples/Program.cs index e0ffef4..05269a1 100644 --- a/src/Triangle.Examples/Program.cs +++ b/src/Triangle.Examples/Program.cs @@ -13,10 +13,11 @@ namespace TriangleNet Check("Example 3", Example3.Run()); Check("Example 4", Example4.Run()); Check("Example 5", Example5.Run()); - Check("Example 6", Example8.Run()); - Check("Example 7", Example6.Run()); - Check("Example 8", Example7.Run()); + Check("Example 6", Example6.Run()); + Check("Example 7", Example7.Run()); + Check("Example 8", Example8.Run()); Check("Example 9", Example9.Run()); + Check("Example 10", Example10.Run()); } static void Check(string item, bool success) diff --git a/src/Triangle/Tools/Interpolation.cs b/src/Triangle/Tools/Interpolation.cs index 857275c..227e020 100644 --- a/src/Triangle/Tools/Interpolation.cs +++ b/src/Triangle/Tools/Interpolation.cs @@ -1,10 +1,54 @@ - +// ----------------------------------------------------------------------- +// +// Triangle Copyright (c) 1993, 1995, 1997, 1998, 2002, 2005 Jonathan Richard Shewchuk +// Triangle.NET code by Christian Woltering +// +// ----------------------------------------------------------------------- + namespace TriangleNet.Tools { using TriangleNet.Geometry; public static class Interpolation { + /// + /// Linear interpolation of a point. + /// + /// The triangle containing the point + /// The point to interpolate. + /// The vertex data (z values). + /// The linear interpolation value. + /// + /// IMPORTANT: this method assumes the mesh vertex ids correspond to the data array indices. + /// + public static double InterpolatePoint(ITriangle tri, Point p, double[] data) + { + var org = tri.GetVertex(0); + var dest = tri.GetVertex(1); + var apex = tri.GetVertex(2); + + double xdo = dest.x - org.x; + double ydo = dest.y - org.y; + double xao = apex.x - org.x; + double yao = apex.y - org.y; + + double denominator = 0.5 / (xdo * yao - xao * ydo); + + double dx = p.x - org.x; + double dy = p.y - org.y; + + // To interpolate z value for the given point inserted, define a + // coordinate system with a xi-axis, directed from the triangle's + // origin to its destination, and an eta-axis, directed from its + // origin to its apex. + double xi = (yao * dx - xao * dy) * (2.0 * denominator); + double eta = (xdo * dy - ydo * dx) * (2.0 * denominator); + + double orgz = data[org.id]; + + return orgz + xi * (data[dest.id] - orgz) + eta * (data[apex.id] - orgz); + } + #if USE_ATTRIBS /// /// Linear interpolation of vertex attributes. @@ -17,35 +61,26 @@ namespace TriangleNet.Tools /// public static void InterpolateAttributes(Vertex vertex, ITriangle triangle, int n) { - Vertex org = triangle.GetVertex(0); - Vertex dest = triangle.GetVertex(1); - Vertex apex = triangle.GetVertex(2); + var org = triangle.GetVertex(0); + var dest = triangle.GetVertex(1); + var apex = triangle.GetVertex(2); - double xdo, ydo, xao, yao; - double denominator; - double dx, dy; - double xi, eta; + double xdo = dest.x - org.x; + double ydo = dest.y - org.y; + double xao = apex.x - org.x; + double yao = apex.y - org.y; - // Compute the circumcenter of the triangle. - xdo = dest.x - org.x; - ydo = dest.y - org.y; - xao = apex.x - org.x; - yao = apex.y - org.y; + double denominator = 0.5 / (xdo * yao - xao * ydo); - denominator = 0.5 / (xdo * yao - xao * ydo); - - //dx = (yao * dodist - ydo * aodist) * denominator; - //dy = (xdo * aodist - xao * dodist) * denominator; - - dx = vertex.x - org.x; - dy = vertex.y - org.y; + double dx = vertex.x - org.x; + double dy = vertex.y - org.y; // To interpolate vertex attributes for the new vertex, define a // coordinate system with a xi-axis directed from the triangle's // origin to its destination, and an eta-axis, directed from its // origin to its apex. - xi = (yao * dx - xao * dy) * (2.0 * denominator); - eta = (xdo * dy - ydo * dx) * (2.0 * denominator); + double xi = (yao * dx - xao * dy) * (2.0 * denominator); + double eta = (xdo * dy - ydo * dx) * (2.0 * denominator); for (int i = 0; i < n; i++) { @@ -68,35 +103,27 @@ namespace TriangleNet.Tools /// public static void InterpolateZ(Point p, ITriangle triangle) { - Vertex org = triangle.GetVertex(0); - Vertex dest = triangle.GetVertex(1); - Vertex apex = triangle.GetVertex(2); - - double xdo, ydo, xao, yao; - double denominator; - double dx, dy; - double xi, eta; + var org = triangle.GetVertex(0); + var dest = triangle.GetVertex(1); + var apex = triangle.GetVertex(2); // Compute the circumcenter of the triangle. - xdo = dest.x - org.x; - ydo = dest.y - org.y; - xao = apex.x - org.x; - yao = apex.y - org.y; + double xdo = dest.x - org.x; + double ydo = dest.y - org.y; + double xao = apex.x - org.x; + double yao = apex.y - org.y; - denominator = 0.5 / (xdo * yao - xao * ydo); + double denominator = 0.5 / (xdo * yao - xao * ydo); - //dx = (yao * dodist - ydo * aodist) * denominator; - //dy = (xdo * aodist - xao * dodist) * denominator; - - dx = p.x - org.x; - dy = p.y - org.y; + double dx = p.x - org.x; + double dy = p.y - org.y; // To interpolate z value for the given point inserted, define a // coordinate system with a xi-axis, directed from the triangle's // origin to its destination, and an eta-axis, directed from its // origin to its apex. - xi = (yao * dx - xao * dy) * (2.0 * denominator); - eta = (xdo * dy - ydo * dx) * (2.0 * denominator); + double xi = (yao * dx - xao * dy) * (2.0 * denominator); + double eta = (xdo * dy - ydo * dx) * (2.0 * denominator); p.z = org.z + xi * (dest.z - org.z) + eta * (apex.z - org.z); }