PolygonValidator.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // -----------------------------------------------------------------------
  2. // <copyright file="PolygonValidator.cs">
  3. // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
  4. // </copyright>
  5. // -----------------------------------------------------------------------
  6. namespace UnityEngine.U2D.Animation.TriangleNet
  7. .Tools
  8. {
  9. using System;
  10. using System.Collections.Generic;
  11. using Animation.TriangleNet.Geometry;
  12. internal static class PolygonValidator
  13. {
  14. /// <summary>
  15. /// Test the polygon for consistency.
  16. /// </summary>
  17. internal static bool IsConsistent(IPolygon poly)
  18. {
  19. var logger = Log.Instance;
  20. var points = poly.Points;
  21. int horrors = 0;
  22. int i = 0;
  23. int count = points.Count;
  24. if (count < 3)
  25. {
  26. logger.Warning("Polygon must have at least 3 vertices.", "PolygonValidator.IsConsistent()");
  27. return false;
  28. }
  29. foreach (var p in points)
  30. {
  31. if (p == null)
  32. {
  33. horrors++;
  34. logger.Warning(String.Format("Point {0} is null.", i), "PolygonValidator.IsConsistent()");
  35. }
  36. else if (double.IsNaN(p.x) || double.IsNaN(p.y))
  37. {
  38. horrors++;
  39. logger.Warning(String.Format("Point {0} has invalid coordinates.", i), "PolygonValidator.IsConsistent()");
  40. }
  41. else if (double.IsInfinity(p.x) || double.IsInfinity(p.y))
  42. {
  43. horrors++;
  44. logger.Warning(String.Format("Point {0} has invalid coordinates.", i), "PolygonValidator.IsConsistent()");
  45. }
  46. i++;
  47. }
  48. i = 0;
  49. foreach (var seg in poly.Segments)
  50. {
  51. if (seg == null)
  52. {
  53. horrors++;
  54. logger.Warning(String.Format("Segment {0} is null.", i), "PolygonValidator.IsConsistent()");
  55. // Always abort if a NULL-segment is found.
  56. return false;
  57. }
  58. var p = seg.GetVertex(0);
  59. var q = seg.GetVertex(1);
  60. if ((p.x == q.x) && (p.y == q.y))
  61. {
  62. horrors++;
  63. logger.Warning(String.Format("Endpoints of segment {0} are coincident (IDs {1} / {2}).", i, p.id, q.id),
  64. "PolygonValidator.IsConsistent()");
  65. }
  66. i++;
  67. }
  68. if (points[0].id == points[1].id)
  69. {
  70. horrors += CheckVertexIDs(poly, count);
  71. }
  72. else
  73. {
  74. horrors += CheckDuplicateIDs(poly);
  75. }
  76. return horrors == 0;
  77. }
  78. /// <summary>
  79. /// Test the polygon for duplicate vertices.
  80. /// </summary>
  81. internal static bool HasDuplicateVertices(IPolygon poly)
  82. {
  83. var logger = Log.Instance;
  84. int horrors = 0;
  85. var points = poly.Points.ToArray();
  86. VertexSorter.Sort(points);
  87. for (int i = 1; i < points.Length; i++)
  88. {
  89. if (points[i - 1] == points[i])
  90. {
  91. horrors++;
  92. logger.Warning(String.Format("Found duplicate point {0}.", points[i]),
  93. "PolygonValidator.HasDuplicateVertices()");
  94. }
  95. }
  96. return horrors > 0;
  97. }
  98. /// <summary>
  99. /// Test the polygon for 360 degree angles.
  100. /// </summary>
  101. /// <param name="poly">The polygon.</param>
  102. /// <param name="threshold">The angle threshold.</param>
  103. internal static bool HasBadAngles(IPolygon poly, double threshold = 2e-12)
  104. {
  105. var logger = Log.Instance;
  106. int horrors = 0;
  107. int i = 0;
  108. Point p0 = null, p1 = null;
  109. Point q0, q1;
  110. int count = poly.Points.Count;
  111. foreach (var seg in poly.Segments)
  112. {
  113. q0 = p0; // Previous segment start point.
  114. q1 = p1; // Previous segment end point.
  115. p0 = seg.GetVertex(0); // Current segment start point.
  116. p1 = seg.GetVertex(1); // Current segment end point.
  117. if (p0 == p1 || q0 == q1)
  118. {
  119. // Ignore zero-length segments.
  120. continue;
  121. }
  122. if (q0 != null && q1 != null)
  123. {
  124. // The two segments are connected.
  125. if (p0 == q1 && p1 != null)
  126. {
  127. if (IsBadAngle(q0, p0, p1, threshold))
  128. {
  129. horrors++;
  130. logger.Warning(String.Format("Bad segment angle found at index {0}.", i),
  131. "PolygonValidator.HasBadAngles()");
  132. }
  133. }
  134. }
  135. i++;
  136. }
  137. return horrors > 0;
  138. }
  139. private static bool IsBadAngle(Point a, Point b, Point c, double threshold = 0.0)
  140. {
  141. double x = DotProduct(a, b, c);
  142. double y = CrossProductLength(a, b, c);
  143. return Math.Abs(Math.Atan2(y, x)) <= threshold;
  144. }
  145. // Returns the dot product <AB, BC>.
  146. private static double DotProduct(Point a, Point b, Point c)
  147. {
  148. // Calculate the dot product.
  149. return (a.x - b.x) * (c.x - b.x) + (a.y - b.y) * (c.y - b.y);
  150. }
  151. // Returns the length of cross product AB x BC.
  152. private static double CrossProductLength(Point a, Point b, Point c)
  153. {
  154. // Calculate the Z coordinate of the cross product.
  155. return (a.x - b.x) * (c.y - b.y) - (a.y - b.y) * (c.x - b.x);
  156. }
  157. private static int CheckVertexIDs(IPolygon poly, int count)
  158. {
  159. var logger = Log.Instance;
  160. int horrors = 0;
  161. int i = 0;
  162. Vertex p, q;
  163. foreach (var seg in poly.Segments)
  164. {
  165. p = seg.GetVertex(0);
  166. q = seg.GetVertex(1);
  167. if (p.id < 0 || p.id >= count)
  168. {
  169. horrors++;
  170. logger.Warning(String.Format("Segment {0} has invalid startpoint.", i),
  171. "PolygonValidator.IsConsistent()");
  172. }
  173. if (q.id < 0 || q.id >= count)
  174. {
  175. horrors++;
  176. logger.Warning(String.Format("Segment {0} has invalid endpoint.", i),
  177. "PolygonValidator.IsConsistent()");
  178. }
  179. i++;
  180. }
  181. return horrors;
  182. }
  183. private static int CheckDuplicateIDs(IPolygon poly)
  184. {
  185. var ids = new HashSet<int>();
  186. // Check for duplicate ids.
  187. foreach (var p in poly.Points)
  188. {
  189. if (!ids.Add(p.id))
  190. {
  191. Log.Instance.Warning("Found duplicate vertex ids.", "PolygonValidator.IsConsistent()");
  192. return 1;
  193. }
  194. }
  195. return 0;
  196. }
  197. }
  198. }