MeshValidator.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // -----------------------------------------------------------------------
  2. // <copyright file="MeshValidator.cs">
  3. // Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
  4. // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
  5. // </copyright>
  6. // -----------------------------------------------------------------------
  7. namespace UnityEngine.U2D.Animation.TriangleNet
  8. {
  9. using System;
  10. using Animation.TriangleNet.Topology;
  11. using Animation.TriangleNet.Geometry;
  12. internal static class MeshValidator
  13. {
  14. private static RobustPredicates predicates = RobustPredicates.Default;
  15. /// <summary>
  16. /// Test the mesh for topological consistency.
  17. /// </summary>
  18. internal static bool IsConsistent(Mesh mesh)
  19. {
  20. Otri tri = default(Otri);
  21. Otri oppotri = default(Otri), oppooppotri = default(Otri);
  22. Vertex org, dest, apex;
  23. Vertex oppoorg, oppodest;
  24. var logger = Log.Instance;
  25. // Temporarily turn on exact arithmetic if it's off.
  26. bool saveexact = Behavior.NoExact;
  27. Behavior.NoExact = false;
  28. int horrors = 0;
  29. // Run through the list of triangles, checking each one.
  30. foreach (var t in mesh.triangles)
  31. {
  32. tri.tri = t;
  33. // Check all three edges of the triangle.
  34. for (tri.orient = 0; tri.orient < 3; tri.orient++)
  35. {
  36. org = tri.Org();
  37. dest = tri.Dest();
  38. if (tri.orient == 0)
  39. {
  40. // Only test for inversion once.
  41. // Test if the triangle is flat or inverted.
  42. apex = tri.Apex();
  43. if (predicates.CounterClockwise(org, dest, apex) <= 0.0)
  44. {
  45. if (Log.Verbose)
  46. {
  47. logger.Warning(String.Format("Triangle is flat or inverted (ID {0}).", t.id),
  48. "MeshValidator.IsConsistent()");
  49. }
  50. horrors++;
  51. }
  52. }
  53. // Find the neighboring triangle on this edge.
  54. tri.Sym(ref oppotri);
  55. if (oppotri.tri.id != Mesh.DUMMY)
  56. {
  57. // Check that the triangle's neighbor knows it's a neighbor.
  58. oppotri.Sym(ref oppooppotri);
  59. if ((tri.tri != oppooppotri.tri) || (tri.orient != oppooppotri.orient))
  60. {
  61. if (tri.tri == oppooppotri.tri && Log.Verbose)
  62. {
  63. logger.Warning("Asymmetric triangle-triangle bond: (Right triangle, wrong orientation)",
  64. "MeshValidator.IsConsistent()");
  65. }
  66. horrors++;
  67. }
  68. // Check that both triangles agree on the identities
  69. // of their shared vertices.
  70. oppoorg = oppotri.Org();
  71. oppodest = oppotri.Dest();
  72. if ((org != oppodest) || (dest != oppoorg))
  73. {
  74. if (Log.Verbose)
  75. {
  76. logger.Warning("Mismatched edge coordinates between two triangles.",
  77. "MeshValidator.IsConsistent()");
  78. }
  79. horrors++;
  80. }
  81. }
  82. }
  83. }
  84. // Check for unconnected vertices
  85. mesh.MakeVertexMap();
  86. foreach (var v in mesh.vertices.Values)
  87. {
  88. if (v.tri.tri == null && Log.Verbose)
  89. {
  90. logger.Warning("Vertex (ID " + v.id + ") not connected to mesh (duplicate input vertex?)",
  91. "MeshValidator.IsConsistent()");
  92. }
  93. }
  94. // Restore the status of exact arithmetic.
  95. Behavior.NoExact = saveexact;
  96. return (horrors == 0);
  97. }
  98. /// <summary>
  99. /// Check if the mesh is (conforming) Delaunay.
  100. /// </summary>
  101. internal static bool IsDelaunay(Mesh mesh)
  102. {
  103. return IsDelaunay(mesh, false);
  104. }
  105. /// <summary>
  106. /// Check if that the mesh is (constrained) Delaunay.
  107. /// </summary>
  108. internal static bool IsConstrainedDelaunay(Mesh mesh)
  109. {
  110. return IsDelaunay(mesh, true);
  111. }
  112. /// <summary>
  113. /// Ensure that the mesh is (constrained) Delaunay.
  114. /// </summary>
  115. private static bool IsDelaunay(Mesh mesh, bool constrained)
  116. {
  117. Otri loop = default(Otri);
  118. Otri oppotri = default(Otri);
  119. Osub opposubseg = default(Osub);
  120. Vertex org, dest, apex;
  121. Vertex oppoapex;
  122. bool shouldbedelaunay;
  123. var logger = Log.Instance;
  124. // Temporarily turn on exact arithmetic if it's off.
  125. bool saveexact = Behavior.NoExact;
  126. Behavior.NoExact = false;
  127. int horrors = 0;
  128. var inf1 = mesh.infvertex1;
  129. var inf2 = mesh.infvertex2;
  130. var inf3 = mesh.infvertex3;
  131. // Run through the list of triangles, checking each one.
  132. foreach (var tri in mesh.triangles)
  133. {
  134. loop.tri = tri;
  135. // Check all three edges of the triangle.
  136. for (loop.orient = 0; loop.orient < 3; loop.orient++)
  137. {
  138. org = loop.Org();
  139. dest = loop.Dest();
  140. apex = loop.Apex();
  141. loop.Sym(ref oppotri);
  142. oppoapex = oppotri.Apex();
  143. // Only test that the edge is locally Delaunay if there is an
  144. // adjoining triangle whose pointer is larger (to ensure that
  145. // each pair isn't tested twice).
  146. shouldbedelaunay = (loop.tri.id < oppotri.tri.id) &&
  147. !Otri.IsDead(oppotri.tri) && (oppotri.tri.id != Mesh.DUMMY) &&
  148. (org != inf1) && (org != inf2) && (org != inf3) &&
  149. (dest != inf1) && (dest != inf2) && (dest != inf3) &&
  150. (apex != inf1) && (apex != inf2) && (apex != inf3) &&
  151. (oppoapex != inf1) && (oppoapex != inf2) && (oppoapex != inf3);
  152. if (constrained && mesh.checksegments && shouldbedelaunay)
  153. {
  154. // If a subsegment separates the triangles, then the edge is
  155. // constrained, so no local Delaunay test should be done.
  156. loop.Pivot(ref opposubseg);
  157. if (opposubseg.seg.hash != Mesh.DUMMY)
  158. {
  159. shouldbedelaunay = false;
  160. }
  161. }
  162. if (shouldbedelaunay)
  163. {
  164. if (predicates.NonRegular(org, dest, apex, oppoapex) > 0.0)
  165. {
  166. if (Log.Verbose)
  167. {
  168. logger.Warning(String.Format("Non-regular pair of triangles found (IDs {0}/{1}).",
  169. loop.tri.id, oppotri.tri.id), "MeshValidator.IsDelaunay()");
  170. }
  171. horrors++;
  172. }
  173. }
  174. }
  175. }
  176. // Restore the status of exact arithmetic.
  177. Behavior.NoExact = saveexact;
  178. return (horrors == 0);
  179. }
  180. }
  181. }