/// <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 entry 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); Point 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 = RobustPredicates.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 = RobustPredicates.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(mesh); var samples = sampler.GetSamples(mesh); foreach (var key in samples) { sampletri.tri = mesh.triangles[key]; 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 = RobustPredicates.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)); }
/// <summary> /// Merge two adjacent Delaunay triangulations into a single Delaunay triangulation. /// </summary> /// <param name="farleft">Bounding triangles of the left triangulation.</param> /// <param name="innerleft">Bounding triangles of the left triangulation.</param> /// <param name="innerright">Bounding triangles of the right triangulation.</param> /// <param name="farright">Bounding triangles of the right triangulation.</param> /// <param name="axis"></param> /// <remarks> /// This is similar to the algorithm given by Guibas and Stolfi, but uses /// a triangle-based, rather than edge-based, data structure. /// /// The algorithm walks up the gap between the two triangulations, knitting /// them together. As they are merged, some of their bounding triangles /// are converted into real triangles of the triangulation. The procedure /// pulls each hull's bounding triangles apart, then knits them together /// like the teeth of two gears. The Delaunay property determines, at each /// step, whether the next "tooth" is a bounding triangle of the left hull /// or the right. When a bounding triangle becomes real, its apex is /// changed from NULL to a real vertex. /// /// Only two new triangles need to be allocated. These become new bounding /// triangles at the top and bottom of the seam. They are used to connect /// the remaining bounding triangles (those that have not been converted /// into real triangles) into a single fan. /// /// On entry, 'farleft' and 'innerleft' are bounding triangles of the left /// triangulation. The origin of 'farleft' is the leftmost vertex, and /// the destination of 'innerleft' is the rightmost vertex of the /// triangulation. Similarly, 'innerright' and 'farright' are bounding /// triangles of the right triangulation. The origin of 'innerright' and /// destination of 'farright' are the leftmost and rightmost vertices. /// /// On completion, the origin of 'farleft' is the leftmost vertex of the /// merged triangulation, and the destination of 'farright' is the rightmost /// vertex. /// </remarks> void MergeHulls(ref Otri farleft, ref Otri innerleft, ref Otri innerright, ref Otri farright, int axis) { Otri leftcand = default(Otri), rightcand = default(Otri); Otri nextedge = default(Otri); Otri sidecasing = default(Otri), topcasing = default(Otri), outercasing = default(Otri); Otri checkedge = default(Otri); Otri baseedge = default(Otri); Vertex innerleftdest; Vertex innerrightorg; Vertex innerleftapex, innerrightapex; Vertex farleftpt, farrightpt; Vertex farleftapex, farrightapex; Vertex lowerleft, lowerright; Vertex upperleft, upperright; Vertex nextapex; Vertex checkvertex; bool changemade; bool badedge; bool leftfinished, rightfinished; innerleftdest = innerleft.Dest(); innerleftapex = innerleft.Apex(); innerrightorg = innerright.Org(); innerrightapex = innerright.Apex(); // Special treatment for horizontal cuts. if (useDwyer && (axis == 1)) { farleftpt = farleft.Org(); farleftapex = farleft.Apex(); farrightpt = farright.Dest(); farrightapex = farright.Apex(); // The pointers to the extremal vertices are shifted to point to the // topmost and bottommost vertex of each hull, rather than the // leftmost and rightmost vertices. while (farleftapex.y < farleftpt.y) { farleft.LnextSelf(); farleft.SymSelf(); farleftpt = farleftapex; farleftapex = farleft.Apex(); } innerleft.Sym(ref checkedge); checkvertex = checkedge.Apex(); while (checkvertex.y > innerleftdest.y) { checkedge.Lnext(ref innerleft); innerleftapex = innerleftdest; innerleftdest = checkvertex; innerleft.Sym(ref checkedge); checkvertex = checkedge.Apex(); } while (innerrightapex.y < innerrightorg.y) { innerright.LnextSelf(); innerright.SymSelf(); innerrightorg = innerrightapex; innerrightapex = innerright.Apex(); } farright.Sym(ref checkedge); checkvertex = checkedge.Apex(); while (checkvertex.y > farrightpt.y) { checkedge.Lnext(ref farright); farrightapex = farrightpt; farrightpt = checkvertex; farright.Sym(ref checkedge); checkvertex = checkedge.Apex(); } } // Find a line tangent to and below both hulls. do { changemade = false; // Make innerleftdest the "bottommost" vertex of the left hull. if (Primitives.CounterClockwise(innerleftdest, innerleftapex, innerrightorg) > 0.0) { innerleft.LprevSelf(); innerleft.SymSelf(); innerleftdest = innerleftapex; innerleftapex = innerleft.Apex(); changemade = true; } // Make innerrightorg the "bottommost" vertex of the right hull. if (Primitives.CounterClockwise(innerrightapex, innerrightorg, innerleftdest) > 0.0) { innerright.LnextSelf(); innerright.SymSelf(); innerrightorg = innerrightapex; innerrightapex = innerright.Apex(); changemade = true; } } while (changemade); // Find the two candidates to be the next "gear tooth." innerleft.Sym(ref leftcand); innerright.Sym(ref rightcand); // Create the bottom new bounding triangle. mesh.MakeTriangle(ref baseedge); // Connect it to the bounding boxes of the left and right triangulations. baseedge.Bond(ref innerleft); baseedge.LnextSelf(); baseedge.Bond(ref innerright); baseedge.LnextSelf(); baseedge.SetOrg(innerrightorg); baseedge.SetDest(innerleftdest); // Apex is intentionally left NULL. // Fix the extreme triangles if necessary. farleftpt = farleft.Org(); if (innerleftdest == farleftpt) { baseedge.Lnext(ref farleft); } farrightpt = farright.Dest(); if (innerrightorg == farrightpt) { baseedge.Lprev(ref farright); } // The vertices of the current knitting edge. lowerleft = innerleftdest; lowerright = innerrightorg; // The candidate vertices for knitting. upperleft = leftcand.Apex(); upperright = rightcand.Apex(); // Walk up the gap between the two triangulations, knitting them together. while (true) { // Have we reached the top? (This isn't quite the right question, // because even though the left triangulation might seem finished now, // moving up on the right triangulation might reveal a new vertex of // the left triangulation. And vice-versa.) leftfinished = Primitives.CounterClockwise(upperleft, lowerleft, lowerright) <= 0.0; rightfinished = Primitives.CounterClockwise(upperright, lowerleft, lowerright) <= 0.0; if (leftfinished && rightfinished) { // Create the top new bounding triangle. mesh.MakeTriangle(ref nextedge); nextedge.SetOrg(lowerleft); nextedge.SetDest(lowerright); // Apex is intentionally left NULL. // Connect it to the bounding boxes of the two triangulations. nextedge.Bond(ref baseedge); nextedge.LnextSelf(); nextedge.Bond(ref rightcand); nextedge.LnextSelf(); nextedge.Bond(ref leftcand); // Special treatment for horizontal cuts. if (useDwyer && (axis == 1)) { farleftpt = farleft.Org(); farleftapex = farleft.Apex(); farrightpt = farright.Dest(); farrightapex = farright.Apex(); farleft.Sym(ref checkedge); checkvertex = checkedge.Apex(); // The pointers to the extremal vertices are restored to the // leftmost and rightmost vertices (rather than topmost and // bottommost). while (checkvertex.x < farleftpt.x) { checkedge.Lprev(ref farleft); farleftapex = farleftpt; farleftpt = checkvertex; farleft.Sym(ref checkedge); checkvertex = checkedge.Apex(); } while (farrightapex.x > farrightpt.x) { farright.LprevSelf(); farright.SymSelf(); farrightpt = farrightapex; farrightapex = farright.Apex(); } } return; } // Consider eliminating edges from the left triangulation. if (!leftfinished) { // What vertex would be exposed if an edge were deleted? leftcand.Lprev(ref nextedge); nextedge.SymSelf(); nextapex = nextedge.Apex(); // If nextapex is NULL, then no vertex would be exposed; the // triangulation would have been eaten right through. if (nextapex != null) { // Check whether the edge is Delaunay. badedge = Primitives.InCircle(lowerleft, lowerright, upperleft, nextapex) > 0.0; while (badedge) { // Eliminate the edge with an edge flip. As a result, the // left triangulation will have one more boundary triangle. nextedge.LnextSelf(); nextedge.Sym(ref topcasing); nextedge.LnextSelf(); nextedge.Sym(ref sidecasing); nextedge.Bond(ref topcasing); leftcand.Bond(ref sidecasing); leftcand.LnextSelf(); leftcand.Sym(ref outercasing); nextedge.LprevSelf(); nextedge.Bond(ref outercasing); // Correct the vertices to reflect the edge flip. leftcand.SetOrg(lowerleft); leftcand.SetDest(null); leftcand.SetApex(nextapex); nextedge.SetOrg(null); nextedge.SetDest(upperleft); nextedge.SetApex(nextapex); // Consider the newly exposed vertex. upperleft = nextapex; // What vertex would be exposed if another edge were deleted? sidecasing.Copy(ref nextedge); nextapex = nextedge.Apex(); if (nextapex != null) { // Check whether the edge is Delaunay. badedge = Primitives.InCircle(lowerleft, lowerright, upperleft, nextapex) > 0.0; } else { // Avoid eating right through the triangulation. badedge = false; } } } } // Consider eliminating edges from the right triangulation. if (!rightfinished) { // What vertex would be exposed if an edge were deleted? rightcand.Lnext(ref nextedge); nextedge.SymSelf(); nextapex = nextedge.Apex(); // If nextapex is NULL, then no vertex would be exposed; the // triangulation would have been eaten right through. if (nextapex != null) { // Check whether the edge is Delaunay. badedge = Primitives.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0; while (badedge) { // Eliminate the edge with an edge flip. As a result, the // right triangulation will have one more boundary triangle. nextedge.LprevSelf(); nextedge.Sym(ref topcasing); nextedge.LprevSelf(); nextedge.Sym(ref sidecasing); nextedge.Bond(ref topcasing); rightcand.Bond(ref sidecasing); rightcand.LprevSelf(); rightcand.Sym(ref outercasing); nextedge.LnextSelf(); nextedge.Bond(ref outercasing); // Correct the vertices to reflect the edge flip. rightcand.SetOrg(null); rightcand.SetDest(lowerright); rightcand.SetApex(nextapex); nextedge.SetOrg(upperright); nextedge.SetDest(null); nextedge.SetApex(nextapex); // Consider the newly exposed vertex. upperright = nextapex; // What vertex would be exposed if another edge were deleted? sidecasing.Copy(ref nextedge); nextapex = nextedge.Apex(); if (nextapex != null) { // Check whether the edge is Delaunay. badedge = Primitives.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0; } else { // Avoid eating right through the triangulation. badedge = false; } } } } if (leftfinished || (!rightfinished && (Primitives.InCircle(upperleft, lowerleft, lowerright, upperright) > 0.0))) { // Knit the triangulations, adding an edge from 'lowerleft' // to 'upperright'. baseedge.Bond(ref rightcand); rightcand.Lprev(ref baseedge); baseedge.SetDest(lowerleft); lowerright = upperright; baseedge.Sym(ref rightcand); upperright = rightcand.Apex(); } else { // Knit the triangulations, adding an edge from 'upperleft' // to 'lowerright'. baseedge.Bond(ref leftcand); leftcand.Lnext(ref baseedge); baseedge.SetOrg(lowerright); lowerleft = upperleft; baseedge.Sym(ref leftcand); upperleft = leftcand.Apex(); } } }
/// <summary> /// Recursively form a Delaunay triangulation by the divide-and-conquer method. /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <param name="axis"></param> /// <param name="farleft"></param> /// <param name="farright"></param> /// <remarks> /// Recursively breaks down the problem into smaller pieces, which are /// knitted together by mergehulls(). The base cases (problems of two or /// three vertices) are handled specially here. /// /// On completion, 'farleft' and 'farright' are bounding triangles such that /// the origin of 'farleft' is the leftmost vertex (breaking ties by /// choosing the highest leftmost vertex), and the destination of /// 'farright' is the rightmost vertex (breaking ties by choosing the /// lowest rightmost vertex). /// </remarks> void DivconqRecurse(int left, int right, int axis, ref Otri farleft, ref Otri farright) { Otri midtri = default(Otri); Otri tri1 = default(Otri); Otri tri2 = default(Otri); Otri tri3 = default(Otri); Otri innerleft = default(Otri), innerright = default(Otri); double area; int vertices = right - left + 1; int divider; if (vertices == 2) { // The triangulation of two vertices is an edge. An edge is // represented by two bounding triangles. mesh.MakeTriangle(ref farleft); farleft.SetOrg(sortarray[left]); farleft.SetDest(sortarray[left + 1]); // The apex is intentionally left NULL. mesh.MakeTriangle(ref farright); farright.SetOrg(sortarray[left + 1]); farright.SetDest(sortarray[left]); // The apex is intentionally left NULL. farleft.Bond(ref farright); farleft.LprevSelf(); farright.LnextSelf(); farleft.Bond(ref farright); farleft.LprevSelf(); farright.LnextSelf(); farleft.Bond(ref farright); // Ensure that the origin of 'farleft' is sortarray[0]. farright.Lprev(ref farleft); return; } else if (vertices == 3) { // The triangulation of three vertices is either a triangle (with // three bounding triangles) or two edges (with four bounding // triangles). In either case, four triangles are created. mesh.MakeTriangle(ref midtri); mesh.MakeTriangle(ref tri1); mesh.MakeTriangle(ref tri2); mesh.MakeTriangle(ref tri3); area = Primitives.CounterClockwise(sortarray[left], sortarray[left + 1], sortarray[left + 2]); if (area == 0.0) { // Three collinear vertices; the triangulation is two edges. midtri.SetOrg(sortarray[left]); midtri.SetDest(sortarray[left + 1]); tri1.SetOrg(sortarray[left + 1]); tri1.SetDest(sortarray[left]); tri2.SetOrg(sortarray[left + 2]); tri2.SetDest(sortarray[left + 1]); tri3.SetOrg(sortarray[left + 1]); tri3.SetDest(sortarray[left + 2]); // All apices are intentionally left NULL. midtri.Bond(ref tri1); tri2.Bond(ref tri3); midtri.LnextSelf(); tri1.LprevSelf(); tri2.LnextSelf(); tri3.LprevSelf(); midtri.Bond(ref tri3); tri1.Bond(ref tri2); midtri.LnextSelf(); tri1.LprevSelf(); tri2.LnextSelf(); tri3.LprevSelf(); midtri.Bond(ref tri1); tri2.Bond(ref tri3); // Ensure that the origin of 'farleft' is sortarray[0]. tri1.Copy(ref farleft); // Ensure that the destination of 'farright' is sortarray[2]. tri2.Copy(ref farright); } else { // The three vertices are not collinear; the triangulation is one // triangle, namely 'midtri'. midtri.SetOrg(sortarray[left]); tri1.SetDest(sortarray[left]); tri3.SetOrg(sortarray[left]); // Apices of tri1, tri2, and tri3 are left NULL. if (area > 0.0) { // The vertices are in counterclockwise order. midtri.SetDest(sortarray[left + 1]); tri1.SetOrg(sortarray[left + 1]); tri2.SetDest(sortarray[left + 1]); midtri.SetApex(sortarray[left + 2]); tri2.SetOrg(sortarray[left + 2]); tri3.SetDest(sortarray[left + 2]); } else { // The vertices are in clockwise order. midtri.SetDest(sortarray[left + 2]); tri1.SetOrg(sortarray[left + 2]); tri2.SetDest(sortarray[left + 2]); midtri.SetApex(sortarray[left + 1]); tri2.SetOrg(sortarray[left + 1]); tri3.SetDest(sortarray[left + 1]); } // The topology does not depend on how the vertices are ordered. midtri.Bond(ref tri1); midtri.LnextSelf(); midtri.Bond(ref tri2); midtri.LnextSelf(); midtri.Bond(ref tri3); tri1.LprevSelf(); tri2.LnextSelf(); tri1.Bond(ref tri2); tri1.LprevSelf(); tri3.LprevSelf(); tri1.Bond(ref tri3); tri2.LnextSelf(); tri3.LprevSelf(); tri2.Bond(ref tri3); // Ensure that the origin of 'farleft' is sortarray[0]. tri1.Copy(ref farleft); // Ensure that the destination of 'farright' is sortarray[2]. if (area > 0.0) { tri2.Copy(ref farright); } else { farleft.Lnext(ref farright); } } return; } else { // Split the vertices in half. divider = vertices >> 1; // Recursively triangulate each half. DivconqRecurse(left, left + divider - 1, 1 - axis, ref farleft, ref innerleft); //DebugWriter.Session.Write(mesh, true); DivconqRecurse(left + divider, right, 1 - axis, ref innerright, ref farright); //DebugWriter.Session.Write(mesh, true); // Merge the two triangulations into one. MergeHulls(ref farleft, ref innerleft, ref innerright, ref farright, axis); //DebugWriter.Session.Write(mesh, true); } }
/// <summary> /// Remove the "infinite" bounding triangle, setting boundary markers as appropriate. /// </summary> /// <returns>Returns the number of edges on the convex hull of the triangulation.</returns> /// <remarks> /// The triangular bounding box has three boundary triangles (one for each /// side of the bounding box), and a bunch of triangles fanning out from /// the three bounding box vertices (one triangle for each edge of the /// convex hull of the inner mesh). This routine removes these triangles. /// </remarks> int RemoveBox() { Otri deadtriangle = default(Otri); Otri searchedge = default(Otri); Otri checkedge = default(Otri); Otri nextedge = default(Otri), finaledge = default(Otri), dissolveedge = default(Otri); Vertex markorg; int hullsize; bool noPoly = !_TriangleNetMesh.behavior.Poly; // Find a boundary triangle. nextedge.tri = _TriangleNetMesh.dummytri; nextedge.orient = 0; nextedge.Sym(); // Mark a place to stop. nextedge.Lprev(ref finaledge); nextedge.Lnext(); nextedge.Sym(); // Find a triangle (on the boundary of the vertex set) that isn't // a bounding box triangle. nextedge.Lprev(ref searchedge); searchedge.Sym(); // Check whether nextedge is another boundary triangle // adjacent to the first one. nextedge.Lnext(ref checkedge); checkedge.Sym(); if (checkedge.tri.id == TriangleNetMesh.DUMMY) { // Go on to the next triangle. There are only three boundary // triangles, and this next triangle cannot be the third one, // so it's safe to stop here. searchedge.Lprev(); searchedge.Sym(); } // Find a new boundary edge to search from, as the current search // edge lies on a bounding box triangle and will be deleted. _TriangleNetMesh.dummytri.neighbors[0] = searchedge; hullsize = -2; while (!nextedge.Equals(finaledge)) { hullsize++; nextedge.Lprev(ref dissolveedge); dissolveedge.Sym(); // If not using a PSLG, the vertices should be marked now. // (If using a PSLG, markhull() will do the job.) if (noPoly) { // Be careful! One must check for the case where all the input // vertices are collinear, and thus all the triangles are part of // the bounding box. Otherwise, the setvertexmark() call below // will cause a bad pointer reference. if (dissolveedge.tri.id != TriangleNetMesh.DUMMY) { markorg = dissolveedge.Org(); if (markorg.label == 0) { markorg.label = 1; } } } // Disconnect the bounding box triangle from the mesh triangle. dissolveedge.Dissolve(_TriangleNetMesh.dummytri); nextedge.Lnext(ref deadtriangle); deadtriangle.Sym(ref nextedge); // Get rid of the bounding box triangle. _TriangleNetMesh.TriangleDealloc(deadtriangle.tri); // Do we need to turn the corner? if (nextedge.tri.id == TriangleNetMesh.DUMMY) { // Turn the corner. dissolveedge.Copy(ref nextedge); } } _TriangleNetMesh.TriangleDealloc(finaledge.tri); return(hullsize); }