From acc1353f7b43cdbac3720b7c406fc35a5f8c774d Mon Sep 17 00:00:00 2001 From: "SND\\wo80_cp" Date: Wed, 16 Jul 2014 12:57:39 +0000 Subject: [PATCH] Added Triangle mesh to DCEL conversion git-svn-id: https://triangle.svn.codeplex.com/svn@75154 0e2699bc-83d4-4a8f-98e7-55e24ab8c7a5 --- Triangle.NET/TestApp/IO/Formats/JsonFile.cs | 4 +- Triangle.NET/Triangle/IO/TriangleFormat.cs | 4 +- Triangle.NET/Triangle/Meshing/Converter.cs | 175 ++++++++++++++++-- .../Triangle/Meshing/GenericMesher.cs | 4 +- .../Triangle/Smoothing/SimpleSmoother.cs | 9 +- .../Triangle/Topology/DCEL/DcelMesh.cs | 84 ++++++++- Triangle.NET/Triangle/Topology/DCEL/Face.cs | 20 ++ .../Triangle/Topology/DCEL/HalfEdge.cs | 12 +- Triangle.NET/Triangle/Triangle.csproj | 5 +- .../Triangle/Voronoi/BoundedVoronoi.cs | 9 + 10 files changed, 293 insertions(+), 33 deletions(-) diff --git a/Triangle.NET/TestApp/IO/Formats/JsonFile.cs b/Triangle.NET/TestApp/IO/Formats/JsonFile.cs index b75f50e..3891e7e 100644 --- a/Triangle.NET/TestApp/IO/Formats/JsonFile.cs +++ b/Triangle.NET/TestApp/IO/Formats/JsonFile.cs @@ -96,9 +96,7 @@ namespace MeshExplorer.IO.Formats } } - var converter = new Converter(); - - return converter.ToMesh(geometry, triangles); + return Converter.ToMesh(geometry, triangles); } public void Write(IMesh mesh, string filename) diff --git a/Triangle.NET/Triangle/IO/TriangleFormat.cs b/Triangle.NET/Triangle/IO/TriangleFormat.cs index 87ed979..b1bda4a 100644 --- a/Triangle.NET/Triangle/IO/TriangleFormat.cs +++ b/Triangle.NET/Triangle/IO/TriangleFormat.cs @@ -42,9 +42,7 @@ namespace TriangleNet.IO if (geometry != null && triangles != null) { - var converter = new Converter(); - - return converter.ToMesh(geometry, triangles.ToArray()); + return Converter.ToMesh(geometry, triangles.ToArray()); } } diff --git a/Triangle.NET/Triangle/Meshing/Converter.cs b/Triangle.NET/Triangle/Meshing/Converter.cs index 5d0029b..8fda7b6 100644 --- a/Triangle.NET/Triangle/Meshing/Converter.cs +++ b/Triangle.NET/Triangle/Meshing/Converter.cs @@ -10,18 +10,24 @@ namespace TriangleNet.Meshing using System; using System.Collections.Generic; using System.Linq; - using TriangleNet.Topology; using TriangleNet.Geometry; + using TriangleNet.Topology; + using TriangleNet.Topology.DCEL; + + using HVertex = TriangleNet.Topology.DCEL.Vertex; + using TVertex = TriangleNet.Geometry.Vertex; /// /// The Converter class provides methods for mesh reconstruction. /// - public class Converter + public static class Converter { + #region Triangle mesh conversion + /// /// Reconstruct a triangulation from its raw data representation. /// - public Mesh ToMesh(Polygon polygon, IList triangles) + public static Mesh ToMesh(Polygon polygon, IList triangles) { return ToMesh(polygon, triangles.ToArray()); } @@ -29,14 +35,14 @@ namespace TriangleNet.Meshing /// /// Reconstruct a triangulation from its raw data representation. /// - public Mesh ToMesh(Polygon polygon, ITriangle[] triangles) + public static Mesh ToMesh(Polygon polygon, ITriangle[] triangles) { Otri tri = default(Otri); Osub subseg = default(Osub); int i = 0; int elements = triangles == null ? 0 : triangles.Length; - int numberofsegments = polygon.Segments.Count; + int segments = polygon.Segments.Count; var mesh = new Mesh(); @@ -53,17 +59,17 @@ namespace TriangleNet.Meshing } // Create the triangles. - for (i = 0; i < mesh.inelements; i++) + for (i = 0; i < elements; i++) { mesh.MakeTriangle(ref tri); } if (mesh.behavior.Poly) { - mesh.insegments = numberofsegments; + mesh.insegments = segments; // Create the subsegments. - for (i = 0; i < mesh.insegments; i++) + for (i = 0; i < segments; i++) { mesh.MakeSegment(ref subseg); } @@ -87,8 +93,8 @@ namespace TriangleNet.Meshing Otri checktri = default(Otri); Otri checkleft = default(Otri); Otri nexttri; - Vertex tdest, tapex; - Vertex checkdest, checkapex; + TVertex tdest, tapex; + TVertex checkdest, checkapex; int[] corner = new int[3]; int aroundvertex; int i; @@ -203,12 +209,12 @@ namespace TriangleNet.Meshing { Otri checktri = default(Otri); Otri nexttri; // Triangle - Vertex checkdest; + TVertex checkdest; Otri checkneighbor = default(Otri); Osub subseg = default(Osub); Otri prevlink; // Triangle - Vertex shorg; - Vertex segmentorg, segmentdest; + TVertex shorg; + TVertex segmentorg, segmentdest; int[] end = new int[2]; bool notfound; //bool segmentmarkers = false; @@ -337,5 +343,148 @@ namespace TriangleNet.Meshing mesh.hullsize = hullsize; mesh.edges = (3 * mesh.triangles.Count + hullsize) / 2; } + + #endregion + + #region DCEL conversion + + public static DcelMesh ToDCEL(Mesh mesh) + { + var dcel = new DcelMesh(); + + var vertices = new HVertex[mesh.vertices.Count]; + var faces = new Face[mesh.triangles.Count]; + + dcel.HalfEdges.Capacity = 2 * mesh.edges; + + mesh.Renumber(); + + HVertex vertex; + + foreach (var v in mesh.vertices.Values) + { + vertex = new HVertex(v.x, v.y); + vertex.id = v.id; + vertex.mark = v.mark; + + vertices[v.id] = vertex; + } + + // Maps a triangle to its 3 edges (used to set next pointers). + var map = new List[mesh.triangles.Count]; + + Face face; + + foreach (var t in mesh.triangles.Values) + { + face = new Face(null); + face.id = t.id; + + faces[t.id] = face; + + map[t.id] = new List(3); + } + + Otri tri = default(Otri), neighbor = default(Otri); + TriangleNet.Geometry.Vertex org, dest; + + int id, nid, count = mesh.triangles.Count; + + HalfEdge edge, twin, next; + + var edges = dcel.HalfEdges; + + // Count half-edges (edge ids). + int k = 0; + + // Maps a vertex to its leaving boundary edge. + var boundary = new Dictionary(); + + foreach (var t in mesh.triangles.Values) + { + id = t.id; + + tri.triangle = t; + + for (int i = 0; i < 3; i++) + { + tri.orient = i; + tri.Sym(ref neighbor); + + nid = neighbor.triangle.id; + + if (id < nid || nid < 0) + { + face = faces[id]; + + // Get the endpoints of the current triangle edge. + org = tri.Org(); + dest = tri.Dest(); + + // Create half-edges. + edge = new HalfEdge(vertices[org.id], face); + twin = new HalfEdge(vertices[dest.id], nid < 0 ? Face.Empty : faces[nid]); + + map[id].Add(edge); + + if (nid >= 0) + { + map[nid].Add(twin); + } + else + { + boundary.Add(dest.id, twin); + } + + // Set leaving edges. + edge.origin.leaving = edge; + twin.origin.leaving = twin; + + // Set twin edges. + edge.twin = twin; + twin.twin = edge; + + edge.id = k++; + twin.id = k++; + + edges.Add(edge); + edges.Add(twin); + } + } + } + + // Set next pointers for each triangle face. + foreach (var t in map) + { + edge = t[0]; + next = t[1]; + + if (edge.twin.origin.id == next.origin.id) + { + edge.next = next; + next.next = t[2]; + t[2].next = edge; + } + else + { + edge.next = t[2]; + next.next = edge; + t[2].next = next; + } + } + + // Resolve boundary edges. + foreach (var e in boundary.Values) + { + e.next = boundary[e.twin.origin.id]; + } + + dcel.Vertices.AddRange(vertices); + dcel.Faces.AddRange(faces); + + return dcel; + } + + #endregion } } diff --git a/Triangle.NET/Triangle/Meshing/GenericMesher.cs b/Triangle.NET/Triangle/Meshing/GenericMesher.cs index 759b851..12a8fa6 100644 --- a/Triangle.NET/Triangle/Meshing/GenericMesher.cs +++ b/Triangle.NET/Triangle/Meshing/GenericMesher.cs @@ -177,9 +177,7 @@ namespace TriangleNet.Meshing } } - var converter = new Converter(); - - return converter.ToMesh(polygon, triangles); + return Converter.ToMesh(polygon, triangles); } } } diff --git a/Triangle.NET/Triangle/Smoothing/SimpleSmoother.cs b/Triangle.NET/Triangle/Smoothing/SimpleSmoother.cs index 644c450..bab21d0 100644 --- a/Triangle.NET/Triangle/Smoothing/SimpleSmoother.cs +++ b/Triangle.NET/Triangle/Smoothing/SimpleSmoother.cs @@ -22,6 +22,9 @@ namespace TriangleNet.Smoothing { ConstraintOptions options; + /// + /// Initializes a new instance of the class. + /// public SimpleSmoother() { this.options = new ConstraintOptions() { ConformingDelaunay = true }; @@ -39,7 +42,7 @@ namespace TriangleNet.Smoothing // The smoother should respect the mesh segment splitting behavior. this.options.SegmentSplitting = smoothedMesh.behavior.NoBisect; - // Take a few smoothing rounds. + // Take a few smoothing rounds (Lloyd's algorithm). for (int i = 0; i < limit; i++) { Step(smoothedMesh); @@ -58,7 +61,7 @@ namespace TriangleNet.Smoothing /// private void Step(Mesh mesh) { - BoundedVoronoiLegacy voronoi = new BoundedVoronoiLegacy(mesh, false); + var voronoi = new BoundedVoronoiLegacy(mesh, false); var cells = voronoi.Regions; @@ -101,7 +104,7 @@ namespace TriangleNet.Smoothing //area = atmp / 2; } - + /// /// Rebuild the input geometry. /// diff --git a/Triangle.NET/Triangle/Topology/DCEL/DcelMesh.cs b/Triangle.NET/Triangle/Topology/DCEL/DcelMesh.cs index 1276766..e5d9d3a 100644 --- a/Triangle.NET/Triangle/Topology/DCEL/DcelMesh.cs +++ b/Triangle.NET/Triangle/Topology/DCEL/DcelMesh.cs @@ -1,4 +1,9 @@ - +// ----------------------------------------------------------------------- +// +// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ +// +// ----------------------------------------------------------------------- + namespace TriangleNet.Topology.DCEL { using System.Collections.Generic; @@ -10,11 +15,18 @@ namespace TriangleNet.Topology.DCEL protected List edges; protected List faces; + /// + /// Initializes a new instance of the class. + /// public DcelMesh() : this(true) { } + /// + /// Initializes a new instance of the class. + /// + /// If false, lists will not be initialized. protected DcelMesh(bool initialize) { if (initialize) @@ -57,6 +69,74 @@ namespace TriangleNet.Topology.DCEL get { return EnumerateEdges(); } } + /// + /// Check if the DCEL ist consistend. + /// + /// If true, faces are assumed to be closed (i.e. all edges must have + /// a valid next pointer). + /// + public bool IsConsistent(bool closed = true) + { + int horrors = 0; + + // Check faces + foreach (var face in faces) + { + if (face.edge == null) + { + horrors++; + } + } + + // Check half-edges + foreach (var edge in edges) + { + var twin = edge.twin; + + if (edge.origin == null) + { + horrors++; + } + + if (twin == null) + { + horrors++; + } + else if (twin.twin != null && edge.id != twin.twin.id) + { + horrors++; + } + + if (closed) + { + if (edge.next == null) + { + horrors++; + } + else if (twin != null && edge.next.origin.id != twin.origin.id) + { + horrors++; + } + } + + if (edge.face == null) + { + horrors++; + } + } + + // Check vertices + foreach (var vertex in vertices) + { + if (vertex.leaving == null) + { + horrors++; + } + } + + return horrors == 0; + } + /// /// Search for half-edge without twin and add a twin. Connect twins to form connected /// boundary contours. @@ -74,7 +154,7 @@ namespace TriangleNet.Topology.DCEL { if (edge.twin == null) { - var twin = edge.twin = new HalfEdge(edge.next.origin); + var twin = edge.twin = new HalfEdge(edge.next.origin, Face.Empty); twin.twin = edge; map.Add(twin.origin.id, twin); diff --git a/Triangle.NET/Triangle/Topology/DCEL/Face.cs b/Triangle.NET/Triangle/Topology/DCEL/Face.cs index b2b0102..e3f6d47 100644 --- a/Triangle.NET/Triangle/Topology/DCEL/Face.cs +++ b/Triangle.NET/Triangle/Topology/DCEL/Face.cs @@ -13,6 +13,18 @@ namespace TriangleNet.Topology.DCEL /// public class Face { + #region Static initialization of "Outer Space" face + + public static readonly Face Empty; + + static Face() + { + Empty = new Face(null); + Empty.id = -1; + } + + #endregion + internal int id; internal Point generator; @@ -20,6 +32,14 @@ namespace TriangleNet.Topology.DCEL internal HalfEdge edge; internal bool bounded; + /// + /// Gets the face id. + /// + public int ID + { + get { return id; } + } + /// /// Gets or sets a half-edge connected to the face. /// diff --git a/Triangle.NET/Triangle/Topology/DCEL/HalfEdge.cs b/Triangle.NET/Triangle/Topology/DCEL/HalfEdge.cs index 0d85b77..3f72422 100644 --- a/Triangle.NET/Triangle/Topology/DCEL/HalfEdge.cs +++ b/Triangle.NET/Triangle/Topology/DCEL/HalfEdge.cs @@ -15,6 +15,14 @@ namespace TriangleNet.Topology.DCEL internal HalfEdge twin; internal HalfEdge next; + /// + /// Gets the half-edge id. + /// + public int ID + { + get { return id; } + } + /// /// Gets or sets the origin of the half-edge. /// @@ -70,8 +78,8 @@ namespace TriangleNet.Topology.DCEL this.origin = origin; this.face = face; - // IMPORTANT: do not remove the (this.face == null) check! - if (face != null && this.face.edge == null) + // IMPORTANT: do not remove the (face.edge == null) check! + if (face != null && face.edge == null) { face.edge = this; } diff --git a/Triangle.NET/Triangle/Triangle.csproj b/Triangle.NET/Triangle/Triangle.csproj index 2e1f77c..22f3c46 100644 --- a/Triangle.NET/Triangle/Triangle.csproj +++ b/Triangle.NET/Triangle/Triangle.csproj @@ -116,10 +116,7 @@ - - - - +