Contour.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // -----------------------------------------------------------------------
  2. // <copyright file="Contour.cs" company="">
  3. // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
  4. // </copyright>
  5. // -----------------------------------------------------------------------
  6. namespace UnityEngine.U2D.Animation.TriangleNet
  7. .Geometry
  8. {
  9. using System;
  10. using System.Linq;
  11. using System.Collections.Generic;
  12. internal class Contour
  13. {
  14. int marker;
  15. bool convex;
  16. /// <summary>
  17. /// Gets or sets the list of points making up the contour.
  18. /// </summary>
  19. public List<Vertex> Points { get; set; }
  20. /// <summary>
  21. /// Initializes a new instance of the <see cref="Contour" /> class.
  22. /// </summary>
  23. /// <param name="points">The points that make up the contour.</param>
  24. public Contour(IEnumerable<Vertex> points)
  25. : this(points, 0, false)
  26. {
  27. }
  28. /// <summary>
  29. /// Initializes a new instance of the <see cref="Contour" /> class.
  30. /// </summary>
  31. /// <param name="points">The points that make up the contour.</param>
  32. /// <param name="marker">Contour marker.</param>
  33. public Contour(IEnumerable<Vertex> points, int marker)
  34. : this(points, marker, false)
  35. {
  36. }
  37. /// <summary>
  38. /// Initializes a new instance of the <see cref="Contour" /> class.
  39. /// </summary>
  40. /// <param name="points">The points that make up the contour.</param>
  41. /// <param name="marker">Contour marker.</param>
  42. /// <param name="convex">The hole is convex.</param>
  43. public Contour(IEnumerable<Vertex> points, int marker, bool convex)
  44. {
  45. AddPoints(points);
  46. this.marker = marker;
  47. this.convex = convex;
  48. }
  49. public List<ISegment> GetSegments()
  50. {
  51. var segments = new List<ISegment>();
  52. var p = this.Points;
  53. int count = p.Count - 1;
  54. for (int i = 0; i < count; i++)
  55. {
  56. // Add segments to polygon.
  57. segments.Add(new Segment(p[i], p[i + 1], marker));
  58. }
  59. // Close the contour.
  60. segments.Add(new Segment(p[count], p[0], marker));
  61. return segments;
  62. }
  63. /// <summary>
  64. /// Try to find a point inside the contour.
  65. /// </summary>
  66. /// <param name="limit">The number of iterations on each segment (default = 5).</param>
  67. /// <param name="eps">Threshold for co-linear points (default = 2e-5).</param>
  68. /// <returns>Point inside the contour</returns>
  69. /// <exception cref="Exception">Throws if no point could be found.</exception>
  70. /// <remarks>
  71. /// For each corner (index i) of the contour, the 3 points with indices i-1, i and i+1
  72. /// are considered and a search on the line through the corner vertex is started (either
  73. /// on the bisecting line, or, if <see cref="IPredicates.CounterClockwise"/> is less than
  74. /// eps, on the perpendicular line.
  75. /// A given number of points will be tested (limit), while the distance to the contour
  76. /// boundary will be reduced in each iteration (with a factor 1 / 2^i, i = 1 ... limit).
  77. /// </remarks>
  78. public Point FindInteriorPoint(int limit = 5, double eps = 2e-5)
  79. {
  80. if (convex)
  81. {
  82. int count = this.Points.Count;
  83. var point = new Point(0.0, 0.0);
  84. for (int i = 0; i < count; i++)
  85. {
  86. point.x += this.Points[i].x;
  87. point.y += this.Points[i].y;
  88. }
  89. // If the contour is convex, use its centroid.
  90. point.x /= count;
  91. point.y /= count;
  92. return point;
  93. }
  94. return FindPointInPolygon(this.Points, limit, eps);
  95. }
  96. private void AddPoints(IEnumerable<Vertex> points)
  97. {
  98. this.Points = new List<Vertex>(points);
  99. int count = Points.Count - 1;
  100. // Check if first vertex equals last vertex.
  101. if (Points[0] == Points[count])
  102. {
  103. Points.RemoveAt(count);
  104. }
  105. }
  106. #region Helper methods
  107. private static Point FindPointInPolygon(List<Vertex> contour, int limit, double eps)
  108. {
  109. var bounds = new Rectangle();
  110. bounds.Expand(contour.Cast<Point>());
  111. int length = contour.Count;
  112. var test = new Point();
  113. Point a, b, c; // Current corner points.
  114. double bx, by;
  115. double dx, dy;
  116. double h;
  117. var predicates = new RobustPredicates();
  118. a = contour[0];
  119. b = contour[1];
  120. for (int i = 0; i < length; i++)
  121. {
  122. c = contour[(i + 2) % length];
  123. // Corner point.
  124. bx = b.x;
  125. by = b.y;
  126. // NOTE: if we knew the contour points were in counterclockwise order, we
  127. // could skip concave corners and search only in one direction.
  128. h = predicates.CounterClockwise(a, b, c);
  129. if (Math.Abs(h) < eps)
  130. {
  131. // Points are nearly co-linear. Use perpendicular direction.
  132. dx = (c.y - a.y) / 2;
  133. dy = (a.x - c.x) / 2;
  134. }
  135. else
  136. {
  137. // Direction [midpoint(a-c) -> corner point]
  138. dx = (a.x + c.x) / 2 - bx;
  139. dy = (a.y + c.y) / 2 - by;
  140. }
  141. // Move around the contour.
  142. a = b;
  143. b = c;
  144. h = 1.0;
  145. for (int j = 0; j < limit; j++)
  146. {
  147. // Search in direction.
  148. test.x = bx + dx * h;
  149. test.y = by + dy * h;
  150. if (bounds.Contains(test) && IsPointInPolygon(test, contour))
  151. {
  152. return test;
  153. }
  154. // Search in opposite direction (see NOTE above).
  155. test.x = bx - dx * h;
  156. test.y = by - dy * h;
  157. if (bounds.Contains(test) && IsPointInPolygon(test, contour))
  158. {
  159. return test;
  160. }
  161. h = h / 2;
  162. }
  163. }
  164. throw new Exception();
  165. }
  166. /// <summary>
  167. /// Return true if the given point is inside the polygon, or false if it is not.
  168. /// </summary>
  169. /// <param name="point">The point to check.</param>
  170. /// <param name="poly">The polygon (list of contour points).</param>
  171. /// <returns></returns>
  172. /// <remarks>
  173. /// WARNING: If the point is exactly on the edge of the polygon, then the function
  174. /// may return true or false.
  175. ///
  176. /// See http://alienryderflex.com/polygon/
  177. /// </remarks>
  178. private static bool IsPointInPolygon(Point point, List<Vertex> poly)
  179. {
  180. bool inside = false;
  181. double x = point.x;
  182. double y = point.y;
  183. int count = poly.Count;
  184. for (int i = 0, j = count - 1; i < count; i++)
  185. {
  186. if (((poly[i].y < y && poly[j].y >= y) || (poly[j].y < y && poly[i].y >= y))
  187. && (poly[i].x <= x || poly[j].x <= x))
  188. {
  189. inside ^= (poly[i].x + (y - poly[i].y) / (poly[j].y - poly[i].y) * (poly[j].x - poly[i].x) < x);
  190. }
  191. j = i;
  192. }
  193. return inside;
  194. }
  195. #endregion
  196. }
  197. }