TriangleLocator.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. // -----------------------------------------------------------------------
  2. // <copyright file="TriangleLocator.cs" company="">
  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 Animation.TriangleNet.Geometry;
  10. using Animation.TriangleNet.Topology;
  11. /// <summary>
  12. /// Locate triangles in a mesh.
  13. /// </summary>
  14. /// <remarks>
  15. /// WARNING: This routine is designed for convex triangulations, and will
  16. /// not generally work after the holes and concavities have been carved.
  17. ///
  18. /// Based on a paper by Ernst P. Mucke, Isaac Saias, and Binhai Zhu, "Fast
  19. /// Randomized Point Location Without Preprocessing in Two- and Three-Dimensional
  20. /// Delaunay Triangulations," Proceedings of the Twelfth Annual Symposium on
  21. /// Computational Geometry, ACM, May 1996.
  22. /// </remarks>
  23. internal class TriangleLocator
  24. {
  25. TriangleSampler sampler;
  26. Mesh mesh;
  27. IPredicates predicates;
  28. // Pointer to a recently visited triangle. Improves point location if
  29. // proximate vertices are inserted sequentially.
  30. internal Otri recenttri;
  31. public TriangleLocator(Mesh mesh)
  32. : this(mesh, RobustPredicates.Default)
  33. {
  34. }
  35. public TriangleLocator(Mesh mesh, IPredicates predicates)
  36. {
  37. this.mesh = mesh;
  38. this.predicates = predicates;
  39. sampler = new TriangleSampler(mesh);
  40. }
  41. /// <summary>
  42. /// Suggest the given triangle as a starting triangle for point location.
  43. /// </summary>
  44. /// <param name="otri"></param>
  45. public void Update(ref Otri otri)
  46. {
  47. otri.Copy(ref recenttri);
  48. }
  49. public void Reset()
  50. {
  51. sampler.Reset();
  52. recenttri.tri = null; // No triangle has been visited yet.
  53. }
  54. /// <summary>
  55. /// Find a triangle or edge containing a given point.
  56. /// </summary>
  57. /// <param name="searchpoint">The point to locate.</param>
  58. /// <param name="searchtri">The triangle to start the search at.</param>
  59. /// <param name="stopatsubsegment"> If 'stopatsubsegment' is set, the search
  60. /// will stop if it tries to walk through a subsegment, and will return OUTSIDE.</param>
  61. /// <returns>Location information.</returns>
  62. /// <remarks>
  63. /// Begins its search from 'searchtri'. It is important that 'searchtri'
  64. /// be a handle with the property that 'searchpoint' is strictly to the left
  65. /// of the edge denoted by 'searchtri', or is collinear with that edge and
  66. /// does not intersect that edge. (In particular, 'searchpoint' should not
  67. /// be the origin or destination of that edge.)
  68. ///
  69. /// These conditions are imposed because preciselocate() is normally used in
  70. /// one of two situations:
  71. ///
  72. /// (1) To try to find the location to insert a new point. Normally, we
  73. /// know an edge that the point is strictly to the left of. In the
  74. /// incremental Delaunay algorithm, that edge is a bounding box edge.
  75. /// In Ruppert's Delaunay refinement algorithm for quality meshing,
  76. /// that edge is the shortest edge of the triangle whose circumcenter
  77. /// is being inserted.
  78. ///
  79. /// (2) To try to find an existing point. In this case, any edge on the
  80. /// convex hull is a good starting edge. You must screen out the
  81. /// possibility that the vertex sought is an endpoint of the starting
  82. /// edge before you call preciselocate().
  83. ///
  84. /// On completion, 'searchtri' is a triangle that contains 'searchpoint'.
  85. ///
  86. /// This implementation differs from that given by Guibas and Stolfi. It
  87. /// walks from triangle to triangle, crossing an edge only if 'searchpoint'
  88. /// is on the other side of the line containing that edge. After entering
  89. /// a triangle, there are two edges by which one can leave that triangle.
  90. /// If both edges are valid ('searchpoint' is on the other side of both
  91. /// edges), one of the two is chosen by drawing a line perpendicular to
  92. /// the label edge (whose endpoints are 'forg' and 'fdest') passing through
  93. /// 'fapex'. Depending on which side of this perpendicular 'searchpoint'
  94. /// falls on, an exit edge is chosen.
  95. ///
  96. /// This implementation is empirically faster than the Guibas and Stolfi
  97. /// point location routine (which I originally used), which tends to spiral
  98. /// in toward its target.
  99. ///
  100. /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
  101. /// is a handle whose origin is the existing vertex.
  102. ///
  103. /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
  104. /// handle whose primary edge is the edge on which the point lies.
  105. ///
  106. /// Returns INTRIANGLE if the point lies strictly within a triangle.
  107. /// 'searchtri' is a handle on the triangle that contains the point.
  108. ///
  109. /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
  110. /// handle whose primary edge the point is to the right of. This might
  111. /// occur when the circumcenter of a triangle falls just slightly outside
  112. /// the mesh due to floating-point roundoff error. It also occurs when
  113. /// seeking a hole or region point that a foolish user has placed outside
  114. /// the mesh.
  115. ///
  116. /// WARNING: This routine is designed for convex triangulations, and will
  117. /// not generally work after the holes and concavities have been carved.
  118. /// However, it can still be used to find the circumcenter of a triangle, as
  119. /// long as the search is begun from the triangle in question.</remarks>
  120. public LocateResult PreciseLocate(Point searchpoint, ref Otri searchtri,
  121. bool stopatsubsegment)
  122. {
  123. Otri backtracktri = default(Otri);
  124. Osub checkedge = default(Osub);
  125. Vertex forg, fdest, fapex;
  126. double orgorient, destorient;
  127. bool moveleft;
  128. // Where are we?
  129. forg = searchtri.Org();
  130. fdest = searchtri.Dest();
  131. fapex = searchtri.Apex();
  132. while (true)
  133. {
  134. // Check whether the apex is the point we seek.
  135. if ((fapex.x == searchpoint.x) && (fapex.y == searchpoint.y))
  136. {
  137. searchtri.Lprev();
  138. return LocateResult.OnVertex;
  139. }
  140. // Does the point lie on the other side of the line defined by the
  141. // triangle edge opposite the triangle's destination?
  142. destorient = predicates.CounterClockwise(forg, fapex, searchpoint);
  143. // Does the point lie on the other side of the line defined by the
  144. // triangle edge opposite the triangle's origin?
  145. orgorient = predicates.CounterClockwise(fapex, fdest, searchpoint);
  146. if (destorient > 0.0)
  147. {
  148. if (orgorient > 0.0)
  149. {
  150. // Move left if the inner product of (fapex - searchpoint) and
  151. // (fdest - forg) is positive. This is equivalent to drawing
  152. // a line perpendicular to the line (forg, fdest) and passing
  153. // through 'fapex', and determining which side of this line
  154. // 'searchpoint' falls on.
  155. moveleft = (fapex.x - searchpoint.x) * (fdest.x - forg.x) +
  156. (fapex.y - searchpoint.y) * (fdest.y - forg.y) > 0.0;
  157. }
  158. else
  159. {
  160. moveleft = true;
  161. }
  162. }
  163. else
  164. {
  165. if (orgorient > 0.0)
  166. {
  167. moveleft = false;
  168. }
  169. else
  170. {
  171. // The point we seek must be on the boundary of or inside this
  172. // triangle.
  173. if (destorient == 0.0)
  174. {
  175. searchtri.Lprev();
  176. return LocateResult.OnEdge;
  177. }
  178. if (orgorient == 0.0)
  179. {
  180. searchtri.Lnext();
  181. return LocateResult.OnEdge;
  182. }
  183. return LocateResult.InTriangle;
  184. }
  185. }
  186. // Move to another triangle. Leave a trace 'backtracktri' in case
  187. // floating-point roundoff or some such bogey causes us to walk
  188. // off a boundary of the triangulation.
  189. if (moveleft)
  190. {
  191. searchtri.Lprev(ref backtracktri);
  192. fdest = fapex;
  193. }
  194. else
  195. {
  196. searchtri.Lnext(ref backtracktri);
  197. forg = fapex;
  198. }
  199. backtracktri.Sym(ref searchtri);
  200. if (mesh.checksegments && stopatsubsegment)
  201. {
  202. // Check for walking through a subsegment.
  203. backtracktri.Pivot(ref checkedge);
  204. if (checkedge.seg.hash != Mesh.DUMMY)
  205. {
  206. // Go back to the last triangle.
  207. backtracktri.Copy(ref searchtri);
  208. return LocateResult.Outside;
  209. }
  210. }
  211. // Check for walking right out of the triangulation.
  212. if (searchtri.tri.id == Mesh.DUMMY)
  213. {
  214. // Go back to the last triangle.
  215. backtracktri.Copy(ref searchtri);
  216. return LocateResult.Outside;
  217. }
  218. fapex = searchtri.Apex();
  219. }
  220. }
  221. /// <summary>
  222. /// Find a triangle or edge containing a given point.
  223. /// </summary>
  224. /// <param name="searchpoint">The point to locate.</param>
  225. /// <param name="searchtri">The triangle to start the search at.</param>
  226. /// <returns>Location information.</returns>
  227. /// <remarks>
  228. /// Searching begins from one of: the input 'searchtri', a recently
  229. /// encountered triangle 'recenttri', or from a triangle chosen from a
  230. /// random sample. The choice is made by determining which triangle's
  231. /// origin is closest to the point we are searching for. Normally,
  232. /// 'searchtri' should be a handle on the convex hull of the triangulation.
  233. ///
  234. /// Details on the random sampling method can be found in the Mucke, Saias,
  235. /// and Zhu paper cited in the header of this code.
  236. ///
  237. /// On completion, 'searchtri' is a triangle that contains 'searchpoint'.
  238. ///
  239. /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
  240. /// is a handle whose origin is the existing vertex.
  241. ///
  242. /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
  243. /// handle whose primary edge is the edge on which the point lies.
  244. ///
  245. /// Returns INTRIANGLE if the point lies strictly within a triangle.
  246. /// 'searchtri' is a handle on the triangle that contains the point.
  247. ///
  248. /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
  249. /// handle whose primary edge the point is to the right of. This might
  250. /// occur when the circumcenter of a triangle falls just slightly outside
  251. /// the mesh due to floating-point roundoff error. It also occurs when
  252. /// seeking a hole or region point that a foolish user has placed outside
  253. /// the mesh.
  254. ///
  255. /// WARNING: This routine is designed for convex triangulations, and will
  256. /// not generally work after the holes and concavities have been carved.
  257. /// </remarks>
  258. public LocateResult Locate(Point searchpoint, ref Otri searchtri)
  259. {
  260. Otri sampletri = default(Otri);
  261. Vertex torg, tdest;
  262. double searchdist, dist;
  263. double ahead;
  264. // Record the distance from the suggested starting triangle to the
  265. // point we seek.
  266. torg = searchtri.Org();
  267. searchdist = (searchpoint.x - torg.x) * (searchpoint.x - torg.x) +
  268. (searchpoint.y - torg.y) * (searchpoint.y - torg.y);
  269. // If a recently encountered triangle has been recorded and has not been
  270. // deallocated, test it as a good starting point.
  271. if (recenttri.tri != null)
  272. {
  273. if (!Otri.IsDead(recenttri.tri))
  274. {
  275. torg = recenttri.Org();
  276. if ((torg.x == searchpoint.x) && (torg.y == searchpoint.y))
  277. {
  278. recenttri.Copy(ref searchtri);
  279. return LocateResult.OnVertex;
  280. }
  281. dist = (searchpoint.x - torg.x) * (searchpoint.x - torg.x) +
  282. (searchpoint.y - torg.y) * (searchpoint.y - torg.y);
  283. if (dist < searchdist)
  284. {
  285. recenttri.Copy(ref searchtri);
  286. searchdist = dist;
  287. }
  288. }
  289. }
  290. // TODO: Improve sampling.
  291. sampler.Update();
  292. foreach (var t in sampler)
  293. {
  294. sampletri.tri = t;
  295. if (!Otri.IsDead(sampletri.tri))
  296. {
  297. torg = sampletri.Org();
  298. dist = (searchpoint.x - torg.x) * (searchpoint.x - torg.x) +
  299. (searchpoint.y - torg.y) * (searchpoint.y - torg.y);
  300. if (dist < searchdist)
  301. {
  302. sampletri.Copy(ref searchtri);
  303. searchdist = dist;
  304. }
  305. }
  306. }
  307. // Where are we?
  308. torg = searchtri.Org();
  309. tdest = searchtri.Dest();
  310. // Check the starting triangle's vertices.
  311. if ((torg.x == searchpoint.x) && (torg.y == searchpoint.y))
  312. {
  313. return LocateResult.OnVertex;
  314. }
  315. if ((tdest.x == searchpoint.x) && (tdest.y == searchpoint.y))
  316. {
  317. searchtri.Lnext();
  318. return LocateResult.OnVertex;
  319. }
  320. // Orient 'searchtri' to fit the preconditions of calling preciselocate().
  321. ahead = predicates.CounterClockwise(torg, tdest, searchpoint);
  322. if (ahead < 0.0)
  323. {
  324. // Turn around so that 'searchpoint' is to the left of the
  325. // edge specified by 'searchtri'.
  326. searchtri.Sym();
  327. }
  328. else if (ahead == 0.0)
  329. {
  330. // Check if 'searchpoint' is between 'torg' and 'tdest'.
  331. if (((torg.x < searchpoint.x) == (searchpoint.x < tdest.x)) &&
  332. ((torg.y < searchpoint.y) == (searchpoint.y < tdest.y)))
  333. {
  334. return LocateResult.OnEdge;
  335. }
  336. }
  337. return PreciseLocate(searchpoint, ref searchtri, false);
  338. }
  339. }
  340. }