123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- // -----------------------------------------------------------------------
- // <copyright file="TriangleLocator.cs" company="">
- // Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
- // Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
- // </copyright>
- // -----------------------------------------------------------------------
- namespace UnityEngine.U2D.Animation.TriangleNet
- {
- using Animation.TriangleNet.Geometry;
- using Animation.TriangleNet.Topology;
- /// <summary>
- /// Locate triangles in a mesh.
- /// </summary>
- /// <remarks>
- /// WARNING: This routine is designed for convex triangulations, and will
- /// not generally work after the holes and concavities have been carved.
- ///
- /// Based on a paper by Ernst P. Mucke, Isaac Saias, and Binhai Zhu, "Fast
- /// Randomized Point Location Without Preprocessing in Two- and Three-Dimensional
- /// Delaunay Triangulations," Proceedings of the Twelfth Annual Symposium on
- /// Computational Geometry, ACM, May 1996.
- /// </remarks>
- internal class TriangleLocator
- {
- TriangleSampler sampler;
- Mesh mesh;
- IPredicates predicates;
- // Pointer to a recently visited triangle. Improves point location if
- // proximate vertices are inserted sequentially.
- internal Otri recenttri;
- public TriangleLocator(Mesh mesh)
- : this(mesh, RobustPredicates.Default)
- {
- }
- public TriangleLocator(Mesh mesh, IPredicates predicates)
- {
- this.mesh = mesh;
- this.predicates = predicates;
- sampler = new TriangleSampler(mesh);
- }
- /// <summary>
- /// Suggest the given triangle as a starting triangle for point location.
- /// </summary>
- /// <param name="otri"></param>
- public void Update(ref Otri otri)
- {
- otri.Copy(ref recenttri);
- }
- public void Reset()
- {
- sampler.Reset();
- recenttri.tri = null; // No triangle has been visited yet.
- }
- /// <summary>
- /// Find a triangle or edge containing a given point.
- /// </summary>
- /// <param name="searchpoint">The point to locate.</param>
- /// <param name="searchtri">The triangle to start the search at.</param>
- /// <param name="stopatsubsegment"> If 'stopatsubsegment' is set, the search
- /// will stop if it tries to walk through a subsegment, and will return OUTSIDE.</param>
- /// <returns>Location information.</returns>
- /// <remarks>
- /// Begins its search from 'searchtri'. It is important that 'searchtri'
- /// be a handle with the property that 'searchpoint' is strictly to the left
- /// of the edge denoted by 'searchtri', or is collinear with that edge and
- /// does not intersect that edge. (In particular, 'searchpoint' should not
- /// be the origin or destination of that edge.)
- ///
- /// These conditions are imposed because preciselocate() is normally used in
- /// one of two situations:
- ///
- /// (1) To try to find the location to insert a new point. Normally, we
- /// know an edge that the point is strictly to the left of. In the
- /// incremental Delaunay algorithm, that edge is a bounding box edge.
- /// In Ruppert's Delaunay refinement algorithm for quality meshing,
- /// that edge is the shortest edge of the triangle whose circumcenter
- /// is being inserted.
- ///
- /// (2) To try to find an existing point. In this case, any edge on the
- /// convex hull is a good starting edge. You must screen out the
- /// possibility that the vertex sought is an endpoint of the starting
- /// edge before you call preciselocate().
- ///
- /// On completion, 'searchtri' is a triangle that contains 'searchpoint'.
- ///
- /// This implementation differs from that given by Guibas and Stolfi. It
- /// walks from triangle to triangle, crossing an edge only if 'searchpoint'
- /// is on the other side of the line containing that edge. After entering
- /// a triangle, there are two edges by which one can leave that triangle.
- /// If both edges are valid ('searchpoint' is on the other side of both
- /// edges), one of the two is chosen by drawing a line perpendicular to
- /// the label edge (whose endpoints are 'forg' and 'fdest') passing through
- /// 'fapex'. Depending on which side of this perpendicular 'searchpoint'
- /// falls on, an exit edge is chosen.
- ///
- /// This implementation is empirically faster than the Guibas and Stolfi
- /// point location routine (which I originally used), which tends to spiral
- /// in toward its target.
- ///
- /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
- /// is a handle whose origin is the existing vertex.
- ///
- /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
- /// handle whose primary edge is the edge on which the point lies.
- ///
- /// Returns INTRIANGLE if the point lies strictly within a triangle.
- /// 'searchtri' is a handle on the triangle that contains the point.
- ///
- /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
- /// handle whose primary edge the point is to the right of. This might
- /// occur when the circumcenter of a triangle falls just slightly outside
- /// the mesh due to floating-point roundoff error. It also occurs when
- /// seeking a hole or region point that a foolish user has placed outside
- /// the mesh.
- ///
- /// WARNING: This routine is designed for convex triangulations, and will
- /// not generally work after the holes and concavities have been carved.
- /// However, it can still be used to find the circumcenter of a triangle, as
- /// long as the search is begun from the triangle in question.</remarks>
- public LocateResult PreciseLocate(Point searchpoint, ref Otri searchtri,
- bool stopatsubsegment)
- {
- Otri backtracktri = default(Otri);
- Osub checkedge = default(Osub);
- Vertex forg, fdest, fapex;
- double orgorient, destorient;
- bool moveleft;
- // Where are we?
- forg = searchtri.Org();
- fdest = searchtri.Dest();
- fapex = searchtri.Apex();
- while (true)
- {
- // Check whether the apex is the point we seek.
- if ((fapex.x == searchpoint.x) && (fapex.y == searchpoint.y))
- {
- searchtri.Lprev();
- return LocateResult.OnVertex;
- }
- // Does the point lie on the other side of the line defined by the
- // triangle edge opposite the triangle's destination?
- destorient = predicates.CounterClockwise(forg, fapex, searchpoint);
- // Does the point lie on the other side of the line defined by the
- // triangle edge opposite the triangle's origin?
- orgorient = predicates.CounterClockwise(fapex, fdest, searchpoint);
- if (destorient > 0.0)
- {
- if (orgorient > 0.0)
- {
- // Move left if the inner product of (fapex - searchpoint) and
- // (fdest - forg) is positive. This is equivalent to drawing
- // a line perpendicular to the line (forg, fdest) and passing
- // through 'fapex', and determining which side of this line
- // 'searchpoint' falls on.
- moveleft = (fapex.x - searchpoint.x) * (fdest.x - forg.x) +
- (fapex.y - searchpoint.y) * (fdest.y - forg.y) > 0.0;
- }
- else
- {
- moveleft = true;
- }
- }
- else
- {
- if (orgorient > 0.0)
- {
- moveleft = false;
- }
- else
- {
- // The point we seek must be on the boundary of or inside this
- // triangle.
- if (destorient == 0.0)
- {
- searchtri.Lprev();
- return LocateResult.OnEdge;
- }
- if (orgorient == 0.0)
- {
- searchtri.Lnext();
- return LocateResult.OnEdge;
- }
- return LocateResult.InTriangle;
- }
- }
- // Move to another triangle. Leave a trace 'backtracktri' in case
- // floating-point roundoff or some such bogey causes us to walk
- // off a boundary of the triangulation.
- if (moveleft)
- {
- searchtri.Lprev(ref backtracktri);
- fdest = fapex;
- }
- else
- {
- searchtri.Lnext(ref backtracktri);
- forg = fapex;
- }
- backtracktri.Sym(ref searchtri);
- if (mesh.checksegments && stopatsubsegment)
- {
- // Check for walking through a subsegment.
- backtracktri.Pivot(ref checkedge);
- if (checkedge.seg.hash != Mesh.DUMMY)
- {
- // Go back to the last triangle.
- backtracktri.Copy(ref searchtri);
- return LocateResult.Outside;
- }
- }
- // Check for walking right out of the triangulation.
- if (searchtri.tri.id == Mesh.DUMMY)
- {
- // Go back to the last triangle.
- backtracktri.Copy(ref searchtri);
- return LocateResult.Outside;
- }
- fapex = searchtri.Apex();
- }
- }
- /// <summary>
- /// Find a triangle or edge containing a given point.
- /// </summary>
- /// <param name="searchpoint">The point to locate.</param>
- /// <param name="searchtri">The triangle to start the search at.</param>
- /// <returns>Location information.</returns>
- /// <remarks>
- /// Searching begins from one of: the input 'searchtri', a recently
- /// encountered triangle 'recenttri', or from a triangle chosen from a
- /// random sample. The choice is made by determining which triangle's
- /// origin is closest to the point we are searching for. Normally,
- /// 'searchtri' should be a handle on the convex hull of the triangulation.
- ///
- /// Details on the random sampling method can be found in the Mucke, Saias,
- /// and Zhu paper cited in the header of this code.
- ///
- /// On completion, 'searchtri' is a triangle that contains 'searchpoint'.
- ///
- /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri'
- /// is a handle whose origin is the existing vertex.
- ///
- /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a
- /// handle whose primary edge is the edge on which the point lies.
- ///
- /// Returns INTRIANGLE if the point lies strictly within a triangle.
- /// 'searchtri' is a handle on the triangle that contains the point.
- ///
- /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a
- /// handle whose primary edge the point is to the right of. This might
- /// occur when the circumcenter of a triangle falls just slightly outside
- /// the mesh due to floating-point roundoff error. It also occurs when
- /// seeking a hole or region point that a foolish user has placed outside
- /// the mesh.
- ///
- /// WARNING: This routine is designed for convex triangulations, and will
- /// not generally work after the holes and concavities have been carved.
- /// </remarks>
- public LocateResult Locate(Point searchpoint, ref Otri searchtri)
- {
- Otri sampletri = default(Otri);
- Vertex torg, tdest;
- double searchdist, dist;
- double ahead;
- // Record the distance from the suggested starting triangle to the
- // point we seek.
- torg = searchtri.Org();
- searchdist = (searchpoint.x - torg.x) * (searchpoint.x - torg.x) +
- (searchpoint.y - torg.y) * (searchpoint.y - torg.y);
- // If a recently encountered triangle has been recorded and has not been
- // deallocated, test it as a good starting point.
- if (recenttri.tri != null)
- {
- if (!Otri.IsDead(recenttri.tri))
- {
- torg = recenttri.Org();
- if ((torg.x == searchpoint.x) && (torg.y == searchpoint.y))
- {
- recenttri.Copy(ref searchtri);
- return LocateResult.OnVertex;
- }
- dist = (searchpoint.x - torg.x) * (searchpoint.x - torg.x) +
- (searchpoint.y - torg.y) * (searchpoint.y - torg.y);
- if (dist < searchdist)
- {
- recenttri.Copy(ref searchtri);
- searchdist = dist;
- }
- }
- }
- // TODO: Improve sampling.
- sampler.Update();
- foreach (var t in sampler)
- {
- sampletri.tri = t;
- if (!Otri.IsDead(sampletri.tri))
- {
- torg = sampletri.Org();
- dist = (searchpoint.x - torg.x) * (searchpoint.x - torg.x) +
- (searchpoint.y - torg.y) * (searchpoint.y - torg.y);
- if (dist < searchdist)
- {
- sampletri.Copy(ref searchtri);
- searchdist = dist;
- }
- }
- }
- // Where are we?
- torg = searchtri.Org();
- tdest = searchtri.Dest();
- // Check the starting triangle's vertices.
- if ((torg.x == searchpoint.x) && (torg.y == searchpoint.y))
- {
- return LocateResult.OnVertex;
- }
- if ((tdest.x == searchpoint.x) && (tdest.y == searchpoint.y))
- {
- searchtri.Lnext();
- return LocateResult.OnVertex;
- }
- // Orient 'searchtri' to fit the preconditions of calling preciselocate().
- ahead = predicates.CounterClockwise(torg, tdest, searchpoint);
- if (ahead < 0.0)
- {
- // Turn around so that 'searchpoint' is to the left of the
- // edge specified by 'searchtri'.
- searchtri.Sym();
- }
- else if (ahead == 0.0)
- {
- // Check if 'searchpoint' is between 'torg' and 'tdest'.
- if (((torg.x < searchpoint.x) == (searchpoint.x < tdest.x)) &&
- ((torg.y < searchpoint.y) == (searchpoint.y < tdest.y)))
- {
- return LocateResult.OnEdge;
- }
- }
- return PreciseLocate(searchpoint, ref searchtri, false);
- }
- }
- }
|