// ----------------------------------------------------------------------- // // Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/ // // ----------------------------------------------------------------------- namespace UnityEngine.U2D.Animation.TriangleNet { using System; using Animation.TriangleNet.Topology; using Animation.TriangleNet.Geometry; internal static class MeshValidator { private static RobustPredicates predicates = RobustPredicates.Default; /// /// Test the mesh for topological consistency. /// internal static bool IsConsistent(Mesh mesh) { Otri tri = default(Otri); Otri oppotri = default(Otri), oppooppotri = default(Otri); Vertex org, dest, apex; Vertex oppoorg, oppodest; var logger = Log.Instance; // Temporarily turn on exact arithmetic if it's off. bool saveexact = Behavior.NoExact; Behavior.NoExact = false; int horrors = 0; // Run through the list of triangles, checking each one. foreach (var t in mesh.triangles) { tri.tri = t; // Check all three edges of the triangle. for (tri.orient = 0; tri.orient < 3; tri.orient++) { org = tri.Org(); dest = tri.Dest(); if (tri.orient == 0) { // Only test for inversion once. // Test if the triangle is flat or inverted. apex = tri.Apex(); if (predicates.CounterClockwise(org, dest, apex) <= 0.0) { if (Log.Verbose) { logger.Warning(String.Format("Triangle is flat or inverted (ID {0}).", t.id), "MeshValidator.IsConsistent()"); } horrors++; } } // Find the neighboring triangle on this edge. tri.Sym(ref oppotri); if (oppotri.tri.id != Mesh.DUMMY) { // Check that the triangle's neighbor knows it's a neighbor. oppotri.Sym(ref oppooppotri); if ((tri.tri != oppooppotri.tri) || (tri.orient != oppooppotri.orient)) { if (tri.tri == oppooppotri.tri && Log.Verbose) { logger.Warning("Asymmetric triangle-triangle bond: (Right triangle, wrong orientation)", "MeshValidator.IsConsistent()"); } horrors++; } // Check that both triangles agree on the identities // of their shared vertices. oppoorg = oppotri.Org(); oppodest = oppotri.Dest(); if ((org != oppodest) || (dest != oppoorg)) { if (Log.Verbose) { logger.Warning("Mismatched edge coordinates between two triangles.", "MeshValidator.IsConsistent()"); } horrors++; } } } } // Check for unconnected vertices mesh.MakeVertexMap(); foreach (var v in mesh.vertices.Values) { if (v.tri.tri == null && Log.Verbose) { logger.Warning("Vertex (ID " + v.id + ") not connected to mesh (duplicate input vertex?)", "MeshValidator.IsConsistent()"); } } // Restore the status of exact arithmetic. Behavior.NoExact = saveexact; return (horrors == 0); } /// /// Check if the mesh is (conforming) Delaunay. /// internal static bool IsDelaunay(Mesh mesh) { return IsDelaunay(mesh, false); } /// /// Check if that the mesh is (constrained) Delaunay. /// internal static bool IsConstrainedDelaunay(Mesh mesh) { return IsDelaunay(mesh, true); } /// /// Ensure that the mesh is (constrained) Delaunay. /// private static bool IsDelaunay(Mesh mesh, bool constrained) { Otri loop = default(Otri); Otri oppotri = default(Otri); Osub opposubseg = default(Osub); Vertex org, dest, apex; Vertex oppoapex; bool shouldbedelaunay; var logger = Log.Instance; // Temporarily turn on exact arithmetic if it's off. bool saveexact = Behavior.NoExact; Behavior.NoExact = false; int horrors = 0; var inf1 = mesh.infvertex1; var inf2 = mesh.infvertex2; var inf3 = mesh.infvertex3; // Run through the list of triangles, checking each one. foreach (var tri in mesh.triangles) { loop.tri = tri; // Check all three edges of the triangle. for (loop.orient = 0; loop.orient < 3; loop.orient++) { org = loop.Org(); dest = loop.Dest(); apex = loop.Apex(); loop.Sym(ref oppotri); oppoapex = oppotri.Apex(); // Only test that the edge is locally Delaunay if there is an // adjoining triangle whose pointer is larger (to ensure that // each pair isn't tested twice). shouldbedelaunay = (loop.tri.id < oppotri.tri.id) && !Otri.IsDead(oppotri.tri) && (oppotri.tri.id != Mesh.DUMMY) && (org != inf1) && (org != inf2) && (org != inf3) && (dest != inf1) && (dest != inf2) && (dest != inf3) && (apex != inf1) && (apex != inf2) && (apex != inf3) && (oppoapex != inf1) && (oppoapex != inf2) && (oppoapex != inf3); if (constrained && mesh.checksegments && shouldbedelaunay) { // If a subsegment separates the triangles, then the edge is // constrained, so no local Delaunay test should be done. loop.Pivot(ref opposubseg); if (opposubseg.seg.hash != Mesh.DUMMY) { shouldbedelaunay = false; } } if (shouldbedelaunay) { if (predicates.NonRegular(org, dest, apex, oppoapex) > 0.0) { if (Log.Verbose) { logger.Warning(String.Format("Non-regular pair of triangles found (IDs {0}/{1}).", loop.tri.id, oppotri.tri.id), "MeshValidator.IsDelaunay()"); } horrors++; } } } } // Restore the status of exact arithmetic. Behavior.NoExact = saveexact; return (horrors == 0); } } }