SimpleVoronoi.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. // -----------------------------------------------------------------------
  2. // <copyright file="Voronoi.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. .Voronoi.Legacy
  9. {
  10. using System;
  11. using System.Collections.Generic;
  12. using Animation.TriangleNet.Topology;
  13. using Animation.TriangleNet.Geometry;
  14. using Animation.TriangleNet.Tools;
  15. /// <summary>
  16. /// The Voronoi Diagram is the dual of a pointset triangulation.
  17. /// </summary>
  18. [Obsolete("Use TriangleNet.Voronoi.StandardVoronoi class instead.")]
  19. internal class SimpleVoronoi : IVoronoi
  20. {
  21. IPredicates predicates = RobustPredicates.Default;
  22. Mesh mesh;
  23. Point[] points;
  24. Dictionary<int, VoronoiRegion> regions;
  25. // Stores the endpoints of rays of unbounded Voronoi cells
  26. Dictionary<int, Point> rayPoints;
  27. int rayIndex;
  28. // Bounding box of the triangles circumcenters.
  29. Rectangle bounds;
  30. /// <summary>
  31. /// Initializes a new instance of the <see cref="SimpleVoronoi" /> class.
  32. /// </summary>
  33. /// <param name="mesh"></param>
  34. /// <remarks>
  35. /// Be sure MakeVertexMap has been called (should always be the case).
  36. /// </remarks>
  37. public SimpleVoronoi(Mesh mesh)
  38. {
  39. this.mesh = mesh;
  40. Generate();
  41. }
  42. /// <summary>
  43. /// Gets the list of Voronoi vertices.
  44. /// </summary>
  45. public Point[] Points
  46. {
  47. get { return points; }
  48. }
  49. /// <summary>
  50. /// Gets the list of Voronoi regions.
  51. /// </summary>
  52. public ICollection<VoronoiRegion> Regions
  53. {
  54. get { return regions.Values; }
  55. }
  56. public IEnumerable<IEdge> Edges
  57. {
  58. get { return EnumerateEdges(); }
  59. }
  60. /// <summary>
  61. /// Gets the Voronoi diagram as raw output data.
  62. /// </summary>
  63. /// <param name="mesh"></param>
  64. /// <returns></returns>
  65. /// <remarks>
  66. /// The Voronoi diagram is the geometric dual of the Delaunay triangulation.
  67. /// Hence, the Voronoi vertices are listed by traversing the Delaunay
  68. /// triangles, and the Voronoi edges are listed by traversing the Delaunay
  69. /// edges.
  70. ///</remarks>
  71. private void Generate()
  72. {
  73. mesh.Renumber();
  74. mesh.MakeVertexMap();
  75. // Allocate space for voronoi diagram
  76. this.points = new Point[mesh.triangles.Count + mesh.hullsize];
  77. this.regions = new Dictionary<int, VoronoiRegion>(mesh.vertices.Count);
  78. rayPoints = new Dictionary<int, Point>();
  79. rayIndex = 0;
  80. bounds = new Rectangle();
  81. // Compute triangles circumcenters and setup bounding box
  82. ComputeCircumCenters();
  83. // Add all Voronoi regions to the map.
  84. foreach (var vertex in mesh.vertices.Values)
  85. {
  86. regions.Add(vertex.id, new VoronoiRegion(vertex));
  87. }
  88. // Loop over the mesh vertices (Voronoi generators).
  89. foreach (var region in regions.Values)
  90. {
  91. //if (item.Boundary == 0)
  92. {
  93. ConstructCell(region);
  94. }
  95. }
  96. }
  97. private void ComputeCircumCenters()
  98. {
  99. Otri tri = default(Otri);
  100. double xi = 0, eta = 0;
  101. Point pt;
  102. // Compue triangle circumcenters
  103. foreach (var item in mesh.triangles)
  104. {
  105. tri.tri = item;
  106. pt = predicates.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta);
  107. pt.id = item.id;
  108. points[item.id] = pt;
  109. bounds.Expand(pt);
  110. }
  111. double ds = Math.Max(bounds.Width, bounds.Height);
  112. bounds.Resize(ds / 10, ds / 10);
  113. }
  114. /// <summary>
  115. /// Construct Voronoi region for given vertex.
  116. /// </summary>
  117. /// <param name="region"></param>
  118. private void ConstructCell(VoronoiRegion region)
  119. {
  120. var vertex = region.Generator as Vertex;
  121. var vpoints = new List<Point>();
  122. Otri f = default(Otri);
  123. Otri f_init = default(Otri);
  124. Otri f_next = default(Otri);
  125. Otri f_prev = default(Otri);
  126. Osub sub = default(Osub);
  127. // Call f_init a triangle incident to x
  128. vertex.tri.Copy(ref f_init);
  129. f_init.Copy(ref f);
  130. f_init.Onext(ref f_next);
  131. // Check if f_init lies on the boundary of the triangulation.
  132. if (f_next.tri.id == Mesh.DUMMY)
  133. {
  134. f_init.Oprev(ref f_prev);
  135. if (f_prev.tri.id != Mesh.DUMMY)
  136. {
  137. f_init.Copy(ref f_next);
  138. // Move one triangle clockwise
  139. f_init.Oprev();
  140. f_init.Copy(ref f);
  141. }
  142. }
  143. // Go counterclockwise until we reach the border or the initial triangle.
  144. while (f_next.tri.id != Mesh.DUMMY)
  145. {
  146. // Add circumcenter of current triangle
  147. vpoints.Add(points[f.tri.id]);
  148. region.AddNeighbor(f.tri.id, regions[f.Apex().id]);
  149. if (f_next.Equals(f_init))
  150. {
  151. // Voronoi cell is complete (bounded case).
  152. region.Add(vpoints);
  153. return;
  154. }
  155. f_next.Copy(ref f);
  156. f_next.Onext();
  157. }
  158. // Voronoi cell is unbounded
  159. region.Bounded = false;
  160. Vertex torg, tdest, tapex;
  161. Point intersection;
  162. int sid, n = mesh.triangles.Count;
  163. // Find the boundary segment id (we use this id to number the endpoints of infinit rays).
  164. f.Lprev(ref f_next);
  165. f_next.Pivot(ref sub);
  166. sid = sub.seg.hash;
  167. // Last valid f lies at the boundary. Add the circumcenter.
  168. vpoints.Add(points[f.tri.id]);
  169. region.AddNeighbor(f.tri.id, regions[f.Apex().id]);
  170. // Check if the intersection with the bounding box has already been computed.
  171. if (!rayPoints.TryGetValue(sid, out intersection))
  172. {
  173. torg = f.Org();
  174. tapex = f.Apex();
  175. intersection = IntersectionHelper.BoxRayIntersection(bounds, points[f.tri.id], torg.y - tapex.y, tapex.x - torg.x);
  176. // Set the correct id for the vertex
  177. intersection.id = n + rayIndex;
  178. points[n + rayIndex] = intersection;
  179. rayIndex++;
  180. rayPoints.Add(sid, intersection);
  181. }
  182. vpoints.Add(intersection);
  183. // Now walk from f_init clockwise till we reach the boundary.
  184. vpoints.Reverse();
  185. f_init.Copy(ref f);
  186. f.Oprev(ref f_prev);
  187. while (f_prev.tri.id != Mesh.DUMMY)
  188. {
  189. vpoints.Add(points[f_prev.tri.id]);
  190. region.AddNeighbor(f_prev.tri.id, regions[f_prev.Apex().id]);
  191. f_prev.Copy(ref f);
  192. f_prev.Oprev();
  193. }
  194. // Find the boundary segment id.
  195. f.Pivot(ref sub);
  196. sid = sub.seg.hash;
  197. if (!rayPoints.TryGetValue(sid, out intersection))
  198. {
  199. // Intersection has not been computed yet.
  200. torg = f.Org();
  201. tdest = f.Dest();
  202. intersection = IntersectionHelper.BoxRayIntersection(bounds, points[f.tri.id], tdest.y - torg.y, torg.x - tdest.x);
  203. // Set the correct id for the vertex
  204. intersection.id = n + rayIndex;
  205. rayPoints.Add(sid, intersection);
  206. points[n + rayIndex] = intersection;
  207. rayIndex++;
  208. }
  209. vpoints.Add(intersection);
  210. region.AddNeighbor(intersection.id, regions[f.Dest().id]);
  211. // Add the new points to the region (in counter-clockwise order)
  212. vpoints.Reverse();
  213. region.Add(vpoints);
  214. }
  215. // TODO: Voronoi enumerate edges
  216. private IEnumerable<IEdge> EnumerateEdges()
  217. {
  218. // Copy edges
  219. Point first, last;
  220. var edges = new List<IEdge>(this.Regions.Count * 2);
  221. foreach (var region in this.Regions)
  222. {
  223. var ve = region.Vertices.GetEnumerator();
  224. ve.MoveNext();
  225. first = last = ve.Current;
  226. while (ve.MoveNext())
  227. {
  228. if (region.ID < region.GetNeighbor(last).ID)
  229. {
  230. edges.Add(new Edge(last.id, ve.Current.id));
  231. }
  232. last = ve.Current;
  233. }
  234. if (region.Bounded && region.ID < region.GetNeighbor(last).ID)
  235. {
  236. edges.Add(new Edge(last.id, first.id));
  237. }
  238. }
  239. return edges;
  240. }
  241. }
  242. }