private int RemoveGhosts(ref Otri startghost) { Otri otri = new Otri(); Otri otri1 = new Otri(); Otri otri2 = new Otri(); bool poly = !this.mesh.behavior.Poly; startghost.Lprev(ref otri); otri.SymSelf(); Mesh.dummytri.neighbors[0] = otri; startghost.Copy(ref otri1); int num = 0; do { num++; otri1.Lnext(ref otri2); otri1.LprevSelf(); otri1.SymSelf(); if (poly && otri1.triangle != Mesh.dummytri) { Vertex vertex = otri1.Org(); if (vertex.mark == 0) { vertex.mark = 1; } } otri1.Dissolve(); otri2.Sym(ref otri1); this.mesh.TriangleDealloc(otri2.triangle); }while (!otri1.Equal(startghost)); return(num); }
private int RemoveBox() { Otri otri = new Otri(); Otri otri1 = new Otri(); Otri otri2 = new Otri(); Otri otri3 = new Otri(); Otri otri4 = new Otri(); Otri otri5 = new Otri(); bool poly = !this.mesh.behavior.Poly; otri3.triangle = Mesh.dummytri; otri3.orient = 0; otri3.SymSelf(); otri3.Lprev(ref otri4); otri3.LnextSelf(); otri3.SymSelf(); otri3.Lprev(ref otri1); otri1.SymSelf(); otri3.Lnext(ref otri2); otri2.SymSelf(); if (otri2.triangle == Mesh.dummytri) { otri1.LprevSelf(); otri1.SymSelf(); } Mesh.dummytri.neighbors[0] = otri1; int num = -2; while (!otri3.Equal(otri4)) { num++; otri3.Lprev(ref otri5); otri5.SymSelf(); if (poly && otri5.triangle != Mesh.dummytri) { Vertex vertex = otri5.Org(); if (vertex.mark == 0) { vertex.mark = 1; } } otri5.Dissolve(); otri3.Lnext(ref otri); otri.Sym(ref otri3); this.mesh.TriangleDealloc(otri.triangle); if (otri3.triangle != Mesh.dummytri) { continue; } otri5.Copy(ref otri3); } this.mesh.TriangleDealloc(otri4.triangle); return(num); }
/// <summary> /// Removes ghost triangles. /// </summary> /// <param name="startghost"></param> /// <returns>Number of vertices on the hull.</returns> int RemoveGhosts(ref Otri startghost) { Otri searchedge = default(Otri); Otri dissolveedge = default(Otri); Otri deadtriangle = default(Otri); Vertex markorg; int hullsize; bool noPoly = !mesh.behavior.Poly; // Find an edge on the convex hull to start point location from. startghost.Lprev(ref searchedge); searchedge.SymSelf(); Mesh.dummytri.neighbors[0] = searchedge; // Remove the bounding box and count the convex hull edges. startghost.Copy(ref dissolveedge); hullsize = 0; do { hullsize++; dissolveedge.Lnext(ref deadtriangle); dissolveedge.LprevSelf(); dissolveedge.SymSelf(); // If no PSLG is involved, set the boundary markers of all the vertices // on the convex hull. If a PSLG is used, this step is done later. if (noPoly) { // Watch out for the case where all the input vertices are collinear. if (dissolveedge.triangle != Mesh.dummytri) { markorg = dissolveedge.Org(); if (markorg.mark == 0) { markorg.mark = 1; } } } // Remove a bounding triangle from a convex hull triangle. dissolveedge.Dissolve(); // Find the next bounding triangle. deadtriangle.Sym(ref dissolveedge); // Delete the bounding triangle. mesh.TriangleDealloc(deadtriangle.triangle); } while (!dissolveedge.Equal(startghost)); return(hullsize); }
/// <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> /// 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> /// Inserts a vertex at the circumcenter of a triangle. Deletes /// the newly inserted vertex if it encroaches upon a segment. /// </summary> /// <param name="badtri"></param> private void SplitTriangle(BadTriangle badtri) { Otri badotri = default(Otri); Vertex borg, bdest, bapex; Point newloc; // Location of the new vertex float xi = 0, eta = 0; InsertVertexResult success; bool errorflag; badotri = badtri.poortri; borg = badotri.Org(); bdest = badotri.Dest(); bapex = badotri.Apex(); // Make sure that this triangle is still the same triangle it was // when it was tested and determined to be of bad quality. // Subsequent transformations may have made it a different triangle. if (!Otri.IsDead(badotri.triangle) && (borg == badtri.triangorg) && (bdest == badtri.triangdest) && (bapex == badtri.triangapex)) { errorflag = false; // Create a new vertex at the triangle's circumcenter. // Using the original (simpler) Steiner point location method // for mesh refinement. // TODO: NewLocation doesn't work for refinement. Why? Maybe // reset VertexType? if (behavior.fixedArea || behavior.VarArea) { newloc = Primitives.FindCircumcenter(borg, bdest, bapex, ref xi, ref eta, behavior.offconstant); } else { newloc = newLocation.FindLocation(borg, bdest, bapex, ref xi, ref eta, true, badotri); } // Check whether the new vertex lies on a triangle vertex. if (((newloc.x == borg.x) && (newloc.y == borg.y)) || ((newloc.x == bdest.x) && (newloc.y == bdest.y)) || ((newloc.x == bapex.x) && (newloc.y == bapex.y))) { if (Behavior.Verbose) { logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()"); errorflag = true; } } else { // The new vertex must be in the interior, and therefore is a // free vertex with a marker of zero. Vertex newvertex = new Vertex(newloc.x, newloc.y, 0, mesh.nextras); newvertex.type = VertexType.FreeVertex; for (int i = 0; i < mesh.nextras; i++) { // Interpolate the vertex attributes at the circumcenter. newvertex.attributes[i] = borg.attributes[i] + xi * (bdest.attributes[i] - borg.attributes[i]) + eta * (bapex.attributes[i] - borg.attributes[i]); } // Ensure that the handle 'badotri' does not represent the longest // edge of the triangle. This ensures that the circumcenter must // fall to the left of this edge, so point location will work. // (If the angle org-apex-dest exceeds 90 degrees, then the // circumcenter lies outside the org-dest edge, and eta is // negative. Roundoff error might prevent eta from being // negative when it should be, so I test eta against xi.) if (eta < xi) { badotri.LprevSelf(); } // Insert the circumcenter, searching from the edge of the triangle, // and maintain the Delaunay property of the triangulation. Osub tmp = default(Osub); success = mesh.InsertVertex(newvertex, ref badotri, ref tmp, true, true); if (success == InsertVertexResult.Successful) { newvertex.hash = mesh.hash_vtx++; newvertex.id = newvertex.hash; mesh.vertices.Add(newvertex.hash, newvertex); if (mesh.steinerleft > 0) { mesh.steinerleft--; } } else if (success == InsertVertexResult.Encroaching) { // If the newly inserted vertex encroaches upon a subsegment, // delete the new vertex. mesh.UndoVertex(); } else if (success == InsertVertexResult.Violating) { // Failed to insert the new vertex, but some subsegment was // marked as being encroached. } else { // success == DUPLICATEVERTEX // Couldn't insert the new vertex because a vertex is already there. if (Behavior.Verbose) { logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()"); errorflag = true; } } } if (errorflag) { logger.Error("The new vertex is at the circumcenter of triangle: This probably " + "means that I am trying to refine triangles to a smaller size than can be " + "accommodated by the finite precision of floating point arithmetic.", "Quality.SplitTriangle()"); throw new Exception("The new vertex is at the circumcenter of triangle."); } } }
/// <summary> /// Split all the encroached subsegments. /// </summary> /// <param name="triflaws">A flag that specifies whether one should take /// note of new bad triangles that result from inserting vertices to repair /// encroached subsegments.</param> /// <remarks> /// Each encroached subsegment is repaired by splitting it - inserting a /// vertex at or near its midpoint. Newly inserted vertices may encroach /// upon other subsegments; these are also repaired. /// </remarks> private void SplitEncSegs(bool triflaws) { Otri enctri = default(Otri); Otri testtri = default(Otri); Osub testsh = default(Osub); Osub currentenc = default(Osub); BadSubseg seg; Vertex eorg, edest, eapex; Vertex newvertex; InsertVertexResult success; float segmentlength, nearestpoweroftwo; float split; float multiplier, divisor; bool acuteorg, acuteorg2, acutedest, acutedest2; // Note that steinerleft == -1 if an unlimited number // of Steiner points is allowed. while (badsubsegs.Count > 0) { if (mesh.steinerleft == 0) { break; } seg = badsubsegs.Dequeue(); currentenc = seg.encsubseg; eorg = currentenc.Org(); edest = currentenc.Dest(); // Make sure that this segment is still the same segment it was // when it was determined to be encroached. If the segment was // enqueued multiple times (because several newly inserted // vertices encroached it), it may have already been split. if (!Osub.IsDead(currentenc.seg) && (eorg == seg.subsegorg) && (edest == seg.subsegdest)) { // To decide where to split a segment, we need to know if the // segment shares an endpoint with an adjacent segment. // The concern is that, if we simply split every encroached // segment in its center, two adjacent segments with a small // angle between them might lead to an infinite loop; each // vertex added to split one segment will encroach upon the // other segment, which must then be split with a vertex that // will encroach upon the first segment, and so on forever. // To avoid this, imagine a set of concentric circles, whose // radii are powers of two, about each segment endpoint. // These concentric circles determine where the segment is // split. (If both endpoints are shared with adjacent // segments, split the segment in the middle, and apply the // concentric circles for later splittings.) // Is the origin shared with another segment? currentenc.TriPivot(ref enctri); enctri.Lnext(ref testtri); testtri.SegPivot(ref testsh); acuteorg = testsh.seg != Mesh.dummysub; // Is the destination shared with another segment? testtri.LnextSelf(); testtri.SegPivot(ref testsh); acutedest = testsh.seg != Mesh.dummysub; // If we're using Chew's algorithm (rather than Ruppert's) // to define encroachment, delete free vertices from the // subsegment's diametral circle. if (!behavior.ConformingDelaunay && !acuteorg && !acutedest) { eapex = enctri.Apex(); while ((eapex.type == VertexType.FreeVertex) && ((eorg.x - eapex.x) * (edest.x - eapex.x) + (eorg.y - eapex.y) * (edest.y - eapex.y) < 0.0)) { mesh.DeleteVertex(ref testtri); currentenc.TriPivot(ref enctri); eapex = enctri.Apex(); enctri.Lprev(ref testtri); } } // Now, check the other side of the segment, if there's a triangle there. enctri.Sym(ref testtri); if (testtri.triangle != Mesh.dummytri) { // Is the destination shared with another segment? testtri.LnextSelf(); testtri.SegPivot(ref testsh); acutedest2 = testsh.seg != Mesh.dummysub; acutedest = acutedest || acutedest2; // Is the origin shared with another segment? testtri.LnextSelf(); testtri.SegPivot(ref testsh); acuteorg2 = testsh.seg != Mesh.dummysub; acuteorg = acuteorg || acuteorg2; // Delete free vertices from the subsegment's diametral circle. if (!behavior.ConformingDelaunay && !acuteorg2 && !acutedest2) { eapex = testtri.Org(); while ((eapex.type == VertexType.FreeVertex) && ((eorg.x - eapex.x) * (edest.x - eapex.x) + (eorg.y - eapex.y) * (edest.y - eapex.y) < 0.0)) { mesh.DeleteVertex(ref testtri); enctri.Sym(ref testtri); eapex = testtri.Apex(); testtri.LprevSelf(); } } } // Use the concentric circles if exactly one endpoint is shared // with another adjacent segment. if (acuteorg || acutedest) { segmentlength = UnityEngine.Mathf.Sqrt((edest.x - eorg.x) * (edest.x - eorg.x) + (edest.y - eorg.y) * (edest.y - eorg.y)); // Find the power of two that most evenly splits the segment. // The worst case is a 2:1 ratio between subsegment lengths. nearestpoweroftwo = 1.0f; while (segmentlength > 3.0f * nearestpoweroftwo) { nearestpoweroftwo *= 2.0f; } while (segmentlength < 1.5f * nearestpoweroftwo) { nearestpoweroftwo *= 0.5f; } // Where do we split the segment? split = nearestpoweroftwo / segmentlength; if (acutedest) { split = 1.0f - split; } } else { // If we're not worried about adjacent segments, split // this segment in the middle. split = 0.5f; } // Create the new vertex (interpolate coordinates). newvertex = new Vertex( eorg.x + split * (edest.x - eorg.x), eorg.y + split * (edest.y - eorg.y), currentenc.Mark(), mesh.nextras); newvertex.type = VertexType.SegmentVertex; newvertex.hash = mesh.hash_vtx++; newvertex.id = newvertex.hash; mesh.vertices.Add(newvertex.hash, newvertex); // Interpolate attributes. for (int i = 0; i < mesh.nextras; i++) { newvertex.attributes[i] = eorg.attributes[i] + split * (edest.attributes[i] - eorg.attributes[i]); } if (!Behavior.NoExact) { // Roundoff in the above calculation may yield a 'newvertex' // that is not precisely collinear with 'eorg' and 'edest'. // Improve collinearity by one step of iterative refinement. multiplier = Primitives.CounterClockwise(eorg, edest, newvertex); divisor = ((eorg.x - edest.x) * (eorg.x - edest.x) + (eorg.y - edest.y) * (eorg.y - edest.y)); if ((multiplier != 0.0) && (divisor != 0.0)) { multiplier = multiplier / divisor; // Watch out for NANs. if (!float.IsNaN(multiplier)) { newvertex.x += multiplier * (edest.y - eorg.y); newvertex.y += multiplier * (eorg.x - edest.x); } } } // Check whether the new vertex lies on an endpoint. if (((newvertex.x == eorg.x) && (newvertex.y == eorg.y)) || ((newvertex.x == edest.x) && (newvertex.y == edest.y))) { logger.Error("Ran out of precision: I attempted to split a" + " segment to a smaller size than can be accommodated by" + " the finite precision of floating point arithmetic.", "Quality.SplitEncSegs()"); throw new Exception("Ran out of precision"); } // Insert the splitting vertex. This should always succeed. success = mesh.InsertVertex(newvertex, ref enctri, ref currentenc, true, triflaws); if ((success != InsertVertexResult.Successful) && (success != InsertVertexResult.Encroaching)) { logger.Error("Failure to split a segment.", "Quality.SplitEncSegs()"); throw new Exception("Failure to split a segment."); } if (mesh.steinerleft > 0) { mesh.steinerleft--; } // Check the two new subsegments to see if they're encroached. CheckSeg4Encroach(ref currentenc); currentenc.NextSelf(); CheckSeg4Encroach(ref currentenc); } // Set subsegment's origin to NULL. This makes it possible to detect dead // badsubsegs when traversing the list of all badsubsegs. seg.subsegorg = null; } }
public int Triangulate(Mesh mesh) { SweepLine.SweepEvent[] sweepEventArray; SweepLine.SweepEvent sweepEvent; Vertex vertex; Vertex vertex1; Vertex vertex2; Vertex vertex3; this.mesh = mesh; this.xminextreme = 10 * mesh.bounds.Xmin - 9 * mesh.bounds.Xmax; Otri otri = new Otri(); Otri otri1 = new Otri(); Otri otri2 = new Otri(); Otri otri3 = new Otri(); Otri otri4 = new Otri(); Otri otri5 = new Otri(); Otri otri6 = new Otri(); bool i = false; this.splaynodes = new List <SweepLine.SplayNode>(); SweepLine.SplayNode splayNode = null; this.CreateHeap(out sweepEventArray); int num = mesh.invertices; mesh.MakeTriangle(ref otri2); mesh.MakeTriangle(ref otri3); otri2.Bond(ref otri3); otri2.LnextSelf(); otri3.LprevSelf(); otri2.Bond(ref otri3); otri2.LnextSelf(); otri3.LprevSelf(); otri2.Bond(ref otri3); Vertex vertex4 = sweepEventArray[0].vertexEvent; this.HeapDelete(sweepEventArray, num, 0); num--; do { if (num == 0) { SimpleLog.Instance.Error("Input vertices are all identical.", "SweepLine.SweepLineDelaunay()"); throw new Exception("Input vertices are all identical."); } vertex = sweepEventArray[0].vertexEvent; this.HeapDelete(sweepEventArray, num, 0); num--; if (vertex4.x != vertex.x || vertex4.y != vertex.y) { continue; } if (Behavior.Verbose) { SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.", "SweepLine.SweepLineDelaunay().1"); } vertex.type = VertexType.UndeadVertex; Mesh mesh1 = mesh; mesh1.undeads = mesh1.undeads + 1; }while (vertex4.x == vertex.x && vertex4.y == vertex.y); otri2.SetOrg(vertex4); otri2.SetDest(vertex); otri3.SetOrg(vertex); otri3.SetDest(vertex4); otri2.Lprev(ref otri); Vertex vertex5 = vertex; while (num > 0) { SweepLine.SweepEvent sweepEvent1 = sweepEventArray[0]; this.HeapDelete(sweepEventArray, num, 0); num--; bool flag = true; if (sweepEvent1.xkey >= mesh.bounds.Xmin) { Vertex vertex6 = sweepEvent1.vertexEvent; if (vertex6.x != vertex5.x || vertex6.y != vertex5.y) { vertex5 = vertex6; splayNode = this.FrontLocate(splayNode, otri, vertex6, ref otri1, ref i); otri.Copy(ref otri1); for (i = false; !i && this.RightOfHyperbola(ref otri1, vertex6); i = otri1.Equal(otri)) { otri1.OnextSelf(); } this.Check4DeadEvent(ref otri1, sweepEventArray, ref num); otri1.Copy(ref otri5); otri1.Sym(ref otri4); mesh.MakeTriangle(ref otri2); mesh.MakeTriangle(ref otri3); Vertex vertex7 = otri5.Dest(); otri2.SetOrg(vertex7); otri2.SetDest(vertex6); otri3.SetOrg(vertex6); otri3.SetDest(vertex7); otri2.Bond(ref otri3); otri2.LnextSelf(); otri3.LprevSelf(); otri2.Bond(ref otri3); otri2.LnextSelf(); otri3.LprevSelf(); otri2.Bond(ref otri4); otri3.Bond(ref otri5); if (!i && otri5.Equal(otri)) { otri2.Copy(ref otri); } if (this.randomnation(SweepLine.SAMPLERATE) == 0) { splayNode = this.SplayInsert(splayNode, otri2, vertex6); } else if (this.randomnation(SweepLine.SAMPLERATE) == 0) { otri3.Lnext(ref otri6); splayNode = this.SplayInsert(splayNode, otri6, vertex6); } } else { if (Behavior.Verbose) { SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.", "SweepLine.SweepLineDelaunay().2"); } vertex6.type = VertexType.UndeadVertex; Mesh mesh2 = mesh; mesh2.undeads = mesh2.undeads + 1; flag = false; } } else { Otri otri7 = sweepEvent1.otriEvent; otri7.Oprev(ref otri4); this.Check4DeadEvent(ref otri4, sweepEventArray, ref num); otri7.Onext(ref otri5); this.Check4DeadEvent(ref otri5, sweepEventArray, ref num); if (otri4.Equal(otri)) { otri7.Lprev(ref otri); } mesh.Flip(ref otri7); otri7.SetApex(null); otri7.Lprev(ref otri2); otri7.Lnext(ref otri3); otri2.Sym(ref otri4); if (this.randomnation(SweepLine.SAMPLERATE) == 0) { otri7.SymSelf(); vertex1 = otri7.Dest(); vertex2 = otri7.Apex(); vertex3 = otri7.Org(); splayNode = this.CircleTopInsert(splayNode, otri2, vertex1, vertex2, vertex3, sweepEvent1.ykey); } } if (!flag) { continue; } vertex1 = otri4.Apex(); vertex2 = otri2.Dest(); vertex3 = otri2.Apex(); double num1 = Primitives.CounterClockwise(vertex1, vertex2, vertex3); if (num1 > 0) { sweepEvent = new SweepLine.SweepEvent() { xkey = this.xminextreme, ykey = this.CircleTop(vertex1, vertex2, vertex3, num1), otriEvent = otri2 }; this.HeapInsert(sweepEventArray, num, sweepEvent); num++; otri2.SetOrg(new SweepLine.SweepEventVertex(sweepEvent)); } vertex1 = otri3.Apex(); vertex2 = otri3.Org(); vertex3 = otri5.Apex(); double num2 = Primitives.CounterClockwise(vertex1, vertex2, vertex3); if (num2 <= 0) { continue; } sweepEvent = new SweepLine.SweepEvent() { xkey = this.xminextreme, ykey = this.CircleTop(vertex1, vertex2, vertex3, num2), otriEvent = otri5 }; this.HeapInsert(sweepEventArray, num, sweepEvent); num++; otri5.SetOrg(new SweepLine.SweepEventVertex(sweepEvent)); } this.splaynodes.Clear(); otri.LprevSelf(); return(this.RemoveGhosts(ref otri)); }
public int Triangulate(Mesh mesh) { this.mesh = mesh; // Nonexistent x value used as a flag to mark circle events in sweepline // Delaunay algorithm. xminextreme = 10 * mesh.bounds.Xmin - 9 * mesh.bounds.Xmax; SweepEvent[] eventheap; SweepEvent nextevent; SweepEvent newevent; SplayNode splayroot; Otri bottommost = default(Otri); Otri searchtri = default(Otri); Otri fliptri; Otri lefttri = default(Otri); Otri righttri = default(Otri); Otri farlefttri = default(Otri); Otri farrighttri = default(Otri); Otri inserttri = default(Otri); Vertex firstvertex, secondvertex; Vertex nextvertex, lastvertex; Vertex connectvertex; Vertex leftvertex, midvertex, rightvertex; float lefttest, righttest; int heapsize; bool check4events, farrightflag = false; splaynodes = new List <SplayNode>(); splayroot = null; CreateHeap(out eventheap);//, out events, out freeevents); heapsize = mesh.invertices; mesh.MakeTriangle(ref lefttri); mesh.MakeTriangle(ref righttri); lefttri.Bond(ref righttri); lefttri.LnextSelf(); righttri.LprevSelf(); lefttri.Bond(ref righttri); lefttri.LnextSelf(); righttri.LprevSelf(); lefttri.Bond(ref righttri); firstvertex = eventheap[0].vertexEvent; HeapDelete(eventheap, heapsize, 0); heapsize--; do { if (heapsize == 0) { SimpleLog.Instance.Error("Input vertices are all identical.", "SweepLine.SweepLineDelaunay()"); throw new Exception("Input vertices are all identical."); } secondvertex = eventheap[0].vertexEvent; HeapDelete(eventheap, heapsize, 0); heapsize--; if ((firstvertex.x == secondvertex.x) && (firstvertex.y == secondvertex.y)) { if (Behavior.Verbose) { SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.", "SweepLine.SweepLineDelaunay().1"); } secondvertex.type = VertexType.UndeadVertex; mesh.undeads++; } } while ((firstvertex.x == secondvertex.x) && (firstvertex.y == secondvertex.y)); lefttri.SetOrg(firstvertex); lefttri.SetDest(secondvertex); righttri.SetOrg(secondvertex); righttri.SetDest(firstvertex); lefttri.Lprev(ref bottommost); lastvertex = secondvertex; while (heapsize > 0) { nextevent = eventheap[0]; HeapDelete(eventheap, heapsize, 0); heapsize--; check4events = true; if (nextevent.xkey < mesh.bounds.Xmin) { fliptri = nextevent.otriEvent; fliptri.Oprev(ref farlefttri); Check4DeadEvent(ref farlefttri, eventheap, ref heapsize); fliptri.Onext(ref farrighttri); Check4DeadEvent(ref farrighttri, eventheap, ref heapsize); if (farlefttri.Equal(bottommost)) { fliptri.Lprev(ref bottommost); } mesh.Flip(ref fliptri); fliptri.SetApex(null); fliptri.Lprev(ref lefttri); fliptri.Lnext(ref righttri); lefttri.Sym(ref farlefttri); if (randomnation(SAMPLERATE) == 0) { fliptri.SymSelf(); leftvertex = fliptri.Dest(); midvertex = fliptri.Apex(); rightvertex = fliptri.Org(); splayroot = CircleTopInsert(splayroot, lefttri, leftvertex, midvertex, rightvertex, nextevent.ykey); } } else { nextvertex = nextevent.vertexEvent; if ((nextvertex.x == lastvertex.x) && (nextvertex.y == lastvertex.y)) { if (Behavior.Verbose) { SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.", "SweepLine.SweepLineDelaunay().2"); } nextvertex.type = VertexType.UndeadVertex; mesh.undeads++; check4events = false; } else { lastvertex = nextvertex; splayroot = FrontLocate(splayroot, bottommost, nextvertex, ref searchtri, ref farrightflag); // bottommost.Copy(ref searchtri); farrightflag = false; while (!farrightflag && RightOfHyperbola(ref searchtri, nextvertex)) { searchtri.OnextSelf(); farrightflag = searchtri.Equal(bottommost); } Check4DeadEvent(ref searchtri, eventheap, ref heapsize); searchtri.Copy(ref farrighttri); searchtri.Sym(ref farlefttri); mesh.MakeTriangle(ref lefttri); mesh.MakeTriangle(ref righttri); connectvertex = farrighttri.Dest(); lefttri.SetOrg(connectvertex); lefttri.SetDest(nextvertex); righttri.SetOrg(nextvertex); righttri.SetDest(connectvertex); lefttri.Bond(ref righttri); lefttri.LnextSelf(); righttri.LprevSelf(); lefttri.Bond(ref righttri); lefttri.LnextSelf(); righttri.LprevSelf(); lefttri.Bond(ref farlefttri); righttri.Bond(ref farrighttri); if (!farrightflag && farrighttri.Equal(bottommost)) { lefttri.Copy(ref bottommost); } if (randomnation(SAMPLERATE) == 0) { splayroot = SplayInsert(splayroot, lefttri, nextvertex); } else if (randomnation(SAMPLERATE) == 0) { righttri.Lnext(ref inserttri); splayroot = SplayInsert(splayroot, inserttri, nextvertex); } } } if (check4events) { leftvertex = farlefttri.Apex(); midvertex = lefttri.Dest(); rightvertex = lefttri.Apex(); lefttest = Primitives.CounterClockwise(leftvertex, midvertex, rightvertex); if (lefttest > 0.0) { newevent = new SweepEvent(); newevent.xkey = xminextreme; newevent.ykey = CircleTop(leftvertex, midvertex, rightvertex, lefttest); newevent.otriEvent = lefttri; HeapInsert(eventheap, heapsize, newevent); heapsize++; lefttri.SetOrg(new SweepEventVertex(newevent)); } leftvertex = righttri.Apex(); midvertex = righttri.Org(); rightvertex = farrighttri.Apex(); righttest = Primitives.CounterClockwise(leftvertex, midvertex, rightvertex); if (righttest > 0.0) { newevent = new SweepEvent(); newevent.xkey = xminextreme; newevent.ykey = CircleTop(leftvertex, midvertex, rightvertex, righttest); newevent.otriEvent = farrighttri; HeapInsert(eventheap, heapsize, newevent); heapsize++; farrighttri.SetOrg(new SweepEventVertex(newevent)); } } } splaynodes.Clear(); bottommost.LprevSelf(); return(RemoveGhosts(ref bottommost)); }
/// <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); 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.LprevSelf(); 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 = Primitives.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 = Primitives.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.LprevSelf(); return(LocateResult.OnEdge); } if (orgorient == 0.0) { searchtri.LnextSelf(); 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.SegPivot(ref checkedge); if (checkedge.seg != Mesh.dummysub) { // Go back to the last triangle. backtracktri.Copy(ref searchtri); return(LocateResult.Outside); } } // Check for walking right out of the triangulation. if (searchtri.triangle == Mesh.dummytri) { // Go back to the last triangle. backtracktri.Copy(ref searchtri); return(LocateResult.Outside); } fapex = searchtri.Apex(); } }
/// <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 = !mesh.behavior.Poly; // Find a boundary triangle. nextedge.triangle = Mesh.dummytri; nextedge.orient = 0; nextedge.SymSelf(); // Mark a place to stop. nextedge.Lprev(ref finaledge); nextedge.LnextSelf(); nextedge.SymSelf(); // Find a triangle (on the boundary of the vertex set) that isn't // a bounding box triangle. nextedge.Lprev(ref searchedge); searchedge.SymSelf(); // Check whether nextedge is another boundary triangle // adjacent to the first one. nextedge.Lnext(ref checkedge); checkedge.SymSelf(); if (checkedge.triangle == Mesh.dummytri) { // 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.LprevSelf(); searchedge.SymSelf(); } // Find a new boundary edge to search from, as the current search // edge lies on a bounding box triangle and will be deleted. Mesh.dummytri.neighbors[0] = searchedge; hullsize = -2; while (!nextedge.Equal(finaledge)) { hullsize++; nextedge.Lprev(ref dissolveedge); dissolveedge.SymSelf(); // 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.triangle != Mesh.dummytri) { markorg = dissolveedge.Org(); if (markorg.mark == 0) { markorg.mark = 1; } } } // Disconnect the bounding box triangle from the mesh triangle. dissolveedge.Dissolve(); nextedge.Lnext(ref deadtriangle); deadtriangle.Sym(ref nextedge); // Get rid of the bounding box triangle. mesh.TriangleDealloc(deadtriangle.triangle); // Do we need to turn the corner? if (nextedge.triangle == Mesh.dummytri) { // Turn the corner. dissolveedge.Copy(ref nextedge); } } mesh.TriangleDealloc(finaledge.triangle); return(hullsize); }
private void SplitTriangle(BadTriangle badtri) { Point point; Otri otri = new Otri(); double num = 0; double num1 = 0; otri = badtri.poortri; Vertex vertex = otri.Org(); Vertex vertex1 = otri.Dest(); Vertex vertex2 = otri.Apex(); if (!Otri.IsDead(otri.triangle) && vertex == badtri.triangorg && vertex1 == badtri.triangdest && vertex2 == badtri.triangapex) { bool flag = false; point = (this.behavior.fixedArea || this.behavior.VarArea ? Primitives.FindCircumcenter(vertex, vertex1, vertex2, ref num, ref num1, this.behavior.offconstant) : this.newLocation.FindLocation(vertex, vertex1, vertex2, ref num, ref num1, true, otri)); if ((point.x != vertex.x || point.y != vertex.y) && (point.x != vertex1.x || point.y != vertex1.y) && (point.x != vertex2.x || point.y != vertex2.y)) { Vertex vertex3 = new Vertex(point.x, point.y, 0, this.mesh.nextras) { type = VertexType.FreeVertex }; for (int i = 0; i < this.mesh.nextras; i++) { vertex3.attributes[i] = vertex.attributes[i] + num * (vertex1.attributes[i] - vertex.attributes[i]) + num1 * (vertex2.attributes[i] - vertex.attributes[i]); } if (num1 < num) { otri.LprevSelf(); } Osub osub = new Osub(); InsertVertexResult insertVertexResult = this.mesh.InsertVertex(vertex3, ref otri, ref osub, true, true); if (insertVertexResult == InsertVertexResult.Successful) { Mesh mesh = this.mesh; int hashVtx = mesh.hash_vtx; mesh.hash_vtx = hashVtx + 1; vertex3.hash = hashVtx; vertex3.id = vertex3.hash; this.mesh.vertices.Add(vertex3.hash, vertex3); if (this.mesh.steinerleft > 0) { Mesh mesh1 = this.mesh; mesh1.steinerleft = mesh1.steinerleft - 1; } } else if (insertVertexResult == InsertVertexResult.Encroaching) { this.mesh.UndoVertex(); } else if (insertVertexResult != InsertVertexResult.Violating && Behavior.Verbose) { this.logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()"); flag = true; } } else if (Behavior.Verbose) { this.logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()"); flag = true; } if (flag) { this.logger.Error("The new vertex is at the circumcenter of triangle: This probably means that I am trying to refine triangles to a smaller size than can be accommodated by the finite precision of floating point arithmetic.", "Quality.SplitTriangle()"); throw new Exception("The new vertex is at the circumcenter of triangle."); } } }
private void SplitEncSegs(bool triflaws) { Vertex vertex; double num; Otri otri = new Otri(); Otri otri1 = new Otri(); Osub osub = new Osub(); Osub osub1 = new Osub(); while (this.badsubsegs.Count > 0 && this.mesh.steinerleft != 0) { BadSubseg badSubseg = this.badsubsegs.Dequeue(); osub1 = badSubseg.encsubseg; Vertex vertex1 = osub1.Org(); Vertex vertex2 = osub1.Dest(); if (!Osub.IsDead(osub1.seg) && vertex1 == badSubseg.subsegorg && vertex2 == badSubseg.subsegdest) { osub1.TriPivot(ref otri); otri.Lnext(ref otri1); otri1.SegPivot(ref osub); bool flag = osub.seg != Mesh.dummysub; otri1.LnextSelf(); otri1.SegPivot(ref osub); bool flag1 = osub.seg != Mesh.dummysub; if (!this.behavior.ConformingDelaunay && !flag && !flag1) { vertex = otri.Apex(); while (vertex.type == VertexType.FreeVertex && (vertex1.x - vertex.x) * (vertex2.x - vertex.x) + (vertex1.y - vertex.y) * (vertex2.y - vertex.y) < 0) { this.mesh.DeleteVertex(ref otri1); osub1.TriPivot(ref otri); vertex = otri.Apex(); otri.Lprev(ref otri1); } } otri.Sym(ref otri1); if (otri1.triangle != Mesh.dummytri) { otri1.LnextSelf(); otri1.SegPivot(ref osub); bool flag2 = osub.seg != Mesh.dummysub; flag1 = flag1 | flag2; otri1.LnextSelf(); otri1.SegPivot(ref osub); bool flag3 = osub.seg != Mesh.dummysub; flag = flag | flag3; if (!this.behavior.ConformingDelaunay && !flag3 && !flag2) { vertex = otri1.Org(); while (vertex.type == VertexType.FreeVertex && (vertex1.x - vertex.x) * (vertex2.x - vertex.x) + (vertex1.y - vertex.y) * (vertex2.y - vertex.y) < 0) { this.mesh.DeleteVertex(ref otri1); otri.Sym(ref otri1); vertex = otri1.Apex(); otri1.LprevSelf(); } } } if (!(flag | flag1)) { num = 0.5; } else { double num1 = Math.Sqrt((vertex2.x - vertex1.x) * (vertex2.x - vertex1.x) + (vertex2.y - vertex1.y) * (vertex2.y - vertex1.y)); double num2 = 1; while (num1 > 3 * num2) { num2 = num2 * 2; } while (num1 < 1.5 * num2) { num2 = num2 * 0.5; } num = num2 / num1; if (flag1) { num = 1 - num; } } Vertex vertex3 = new Vertex(vertex1.x + num * (vertex2.x - vertex1.x), vertex1.y + num * (vertex2.y - vertex1.y), osub1.Mark(), this.mesh.nextras) { type = VertexType.SegmentVertex }; Mesh mesh = this.mesh; int hashVtx = mesh.hash_vtx; mesh.hash_vtx = hashVtx + 1; vertex3.hash = hashVtx; vertex3.id = vertex3.hash; this.mesh.vertices.Add(vertex3.hash, vertex3); for (int i = 0; i < this.mesh.nextras; i++) { vertex3.attributes[i] = vertex1.attributes[i] + num * (vertex2.attributes[i] - vertex1.attributes[i]); } if (!Behavior.NoExact) { double num3 = Primitives.CounterClockwise(vertex1, vertex2, vertex3); double num4 = (vertex1.x - vertex2.x) * (vertex1.x - vertex2.x) + (vertex1.y - vertex2.y) * (vertex1.y - vertex2.y); if (num3 != 0 && num4 != 0) { num3 = num3 / num4; if (!double.IsNaN(num3)) { Vertex vertex4 = vertex3; vertex4.x = vertex4.x + num3 * (vertex2.y - vertex1.y); Vertex vertex5 = vertex3; vertex5.y = vertex5.y + num3 * (vertex1.x - vertex2.x); } } } if (vertex3.x == vertex1.x && vertex3.y == vertex1.y || vertex3.x == vertex2.x && vertex3.y == vertex2.y) { this.logger.Error("Ran out of precision: I attempted to split a segment to a smaller size than can be accommodated by the finite precision of floating point arithmetic.", "Quality.SplitEncSegs()"); throw new Exception("Ran out of precision"); } InsertVertexResult insertVertexResult = this.mesh.InsertVertex(vertex3, ref otri, ref osub1, true, triflaws); if (insertVertexResult != InsertVertexResult.Successful && insertVertexResult != InsertVertexResult.Encroaching) { this.logger.Error("Failure to split a segment.", "Quality.SplitEncSegs()"); throw new Exception("Failure to split a segment."); } if (this.mesh.steinerleft > 0) { Mesh mesh1 = this.mesh; mesh1.steinerleft = mesh1.steinerleft - 1; } this.CheckSeg4Encroach(ref osub1); osub1.NextSelf(); this.CheckSeg4Encroach(ref osub1); } badSubseg.subsegorg = null; } }
public LocateResult PreciseLocate(Point searchpoint, ref Otri searchtri, bool stopatsubsegment) { bool flag; Otri otri = new Otri(); Osub osub = new Osub(); Vertex vertex = searchtri.Org(); Vertex vertex1 = searchtri.Dest(); for (Vertex i = searchtri.Apex(); i.x != searchpoint.X || i.y != searchpoint.Y; i = searchtri.Apex()) { double num = Primitives.CounterClockwise(vertex, i, searchpoint); double num1 = Primitives.CounterClockwise(i, vertex1, searchpoint); if (num <= 0) { if (num1 <= 0) { if (num == 0) { searchtri.LprevSelf(); return(LocateResult.OnEdge); } if (num1 != 0) { return(LocateResult.InTriangle); } searchtri.LnextSelf(); return(LocateResult.OnEdge); } flag = false; } else { flag = (num1 <= 0 ? true : (i.x - searchpoint.X) * (vertex1.x - vertex.x) + (i.y - searchpoint.Y) * (vertex1.y - vertex.y) > 0); } if (!flag) { searchtri.Lnext(ref otri); vertex = i; } else { searchtri.Lprev(ref otri); vertex1 = i; } otri.Sym(ref searchtri); if (this.mesh.checksegments & stopatsubsegment) { otri.SegPivot(ref osub); if (osub.seg != Mesh.dummysub) { otri.Copy(ref searchtri); return(LocateResult.Outside); } } if (searchtri.triangle == Mesh.dummytri) { otri.Copy(ref searchtri); return(LocateResult.Outside); } } searchtri.LprevSelf(); return(LocateResult.OnVertex); }
private void DivconqRecurse(int left, int right, int axis, ref Otri farleft, ref Otri farright) { Otri otri = new Otri(); Otri otri1 = new Otri(); Otri otri2 = new Otri(); Otri otri3 = new Otri(); Otri otri4 = new Otri(); Otri otri5 = new Otri(); int num = right - left + 1; if (num == 2) { this.mesh.MakeTriangle(ref farleft); farleft.SetOrg(this.sortarray[left]); farleft.SetDest(this.sortarray[left + 1]); this.mesh.MakeTriangle(ref farright); farright.SetOrg(this.sortarray[left + 1]); farright.SetDest(this.sortarray[left]); farleft.Bond(ref farright); farleft.LprevSelf(); farright.LnextSelf(); farleft.Bond(ref farright); farleft.LprevSelf(); farright.LnextSelf(); farleft.Bond(ref farright); farright.Lprev(ref farleft); return; } if (num != 3) { int num1 = num >> 1; this.DivconqRecurse(left, left + num1 - 1, 1 - axis, ref farleft, ref otri4); this.DivconqRecurse(left + num1, right, 1 - axis, ref otri5, ref farright); this.MergeHulls(ref farleft, ref otri4, ref otri5, ref farright, axis); return; } this.mesh.MakeTriangle(ref otri); this.mesh.MakeTriangle(ref otri1); this.mesh.MakeTriangle(ref otri2); this.mesh.MakeTriangle(ref otri3); double num2 = Primitives.CounterClockwise(this.sortarray[left], this.sortarray[left + 1], this.sortarray[left + 2]); if (num2 == 0) { otri.SetOrg(this.sortarray[left]); otri.SetDest(this.sortarray[left + 1]); otri1.SetOrg(this.sortarray[left + 1]); otri1.SetDest(this.sortarray[left]); otri2.SetOrg(this.sortarray[left + 2]); otri2.SetDest(this.sortarray[left + 1]); otri3.SetOrg(this.sortarray[left + 1]); otri3.SetDest(this.sortarray[left + 2]); otri.Bond(ref otri1); otri2.Bond(ref otri3); otri.LnextSelf(); otri1.LprevSelf(); otri2.LnextSelf(); otri3.LprevSelf(); otri.Bond(ref otri3); otri1.Bond(ref otri2); otri.LnextSelf(); otri1.LprevSelf(); otri2.LnextSelf(); otri3.LprevSelf(); otri.Bond(ref otri1); otri2.Bond(ref otri3); otri1.Copy(ref farleft); otri2.Copy(ref farright); return; } otri.SetOrg(this.sortarray[left]); otri1.SetDest(this.sortarray[left]); otri3.SetOrg(this.sortarray[left]); if (num2 <= 0) { otri.SetDest(this.sortarray[left + 2]); otri1.SetOrg(this.sortarray[left + 2]); otri2.SetDest(this.sortarray[left + 2]); otri.SetApex(this.sortarray[left + 1]); otri2.SetOrg(this.sortarray[left + 1]); otri3.SetDest(this.sortarray[left + 1]); } else { otri.SetDest(this.sortarray[left + 1]); otri1.SetOrg(this.sortarray[left + 1]); otri2.SetDest(this.sortarray[left + 1]); otri.SetApex(this.sortarray[left + 2]); otri2.SetOrg(this.sortarray[left + 2]); otri3.SetDest(this.sortarray[left + 2]); } otri.Bond(ref otri1); otri.LnextSelf(); otri.Bond(ref otri2); otri.LnextSelf(); otri.Bond(ref otri3); otri1.LprevSelf(); otri2.LnextSelf(); otri1.Bond(ref otri2); otri1.LprevSelf(); otri3.LprevSelf(); otri1.Bond(ref otri3); otri2.LnextSelf(); otri3.LprevSelf(); otri2.Bond(ref otri3); otri1.Copy(ref farleft); if (num2 > 0) { otri2.Copy(ref farright); return; } farleft.Lnext(ref farright); }
private void MergeHulls(ref Otri farleft, ref Otri innerleft, ref Otri innerright, ref Otri farright, int axis) { Vertex vertex; Vertex vertex1; Vertex vertex2; Vertex vertex3; Vertex vertex4; Vertex i; bool flag; bool flag1; Otri otri = new Otri(); Otri otri1 = new Otri(); Otri otri2 = new Otri(); Otri otri3 = new Otri(); Otri otri4 = new Otri(); Otri otri5 = new Otri(); Otri otri6 = new Otri(); Otri otri7 = new Otri(); Vertex vertex5 = innerleft.Dest(); Vertex vertex6 = innerleft.Apex(); Vertex vertex7 = innerright.Org(); Vertex vertex8 = innerright.Apex(); if (this.useDwyer && axis == 1) { vertex = farleft.Org(); vertex2 = farleft.Apex(); vertex1 = farright.Dest(); vertex3 = farright.Apex(); while (vertex2.y < vertex.y) { farleft.LnextSelf(); farleft.SymSelf(); vertex = vertex2; vertex2 = farleft.Apex(); } innerleft.Sym(ref otri6); for (i = otri6.Apex(); i.y > vertex5.y; i = otri6.Apex()) { otri6.Lnext(ref innerleft); vertex6 = vertex5; vertex5 = i; innerleft.Sym(ref otri6); } while (vertex8.y < vertex7.y) { innerright.LnextSelf(); innerright.SymSelf(); vertex7 = vertex8; vertex8 = innerright.Apex(); } farright.Sym(ref otri6); for (i = otri6.Apex(); i.y > vertex1.y; i = otri6.Apex()) { otri6.Lnext(ref farright); vertex3 = vertex1; vertex1 = i; farright.Sym(ref otri6); } } do { flag = false; if (Primitives.CounterClockwise(vertex5, vertex6, vertex7) > 0) { innerleft.LprevSelf(); innerleft.SymSelf(); vertex5 = vertex6; vertex6 = innerleft.Apex(); flag = true; } if (Primitives.CounterClockwise(vertex8, vertex7, vertex5) <= 0) { continue; } innerright.LnextSelf(); innerright.SymSelf(); vertex7 = vertex8; vertex8 = innerright.Apex(); flag = true; }while (flag); innerleft.Sym(ref otri); innerright.Sym(ref otri1); this.mesh.MakeTriangle(ref otri7); otri7.Bond(ref innerleft); otri7.LnextSelf(); otri7.Bond(ref innerright); otri7.LnextSelf(); otri7.SetOrg(vertex7); otri7.SetDest(vertex5); vertex = farleft.Org(); if (vertex5 == vertex) { otri7.Lnext(ref farleft); } vertex1 = farright.Dest(); if (vertex7 == vertex1) { otri7.Lprev(ref farright); } Vertex vertex9 = vertex5; Vertex vertex10 = vertex7; Vertex vertex11 = otri.Apex(); Vertex vertex12 = otri1.Apex(); while (true) { bool flag2 = Primitives.CounterClockwise(vertex11, vertex9, vertex10) <= 0; bool flag3 = Primitives.CounterClockwise(vertex12, vertex9, vertex10) <= 0; if (flag2 & flag3) { break; } if (!flag2) { otri.Lprev(ref otri2); otri2.SymSelf(); vertex4 = otri2.Apex(); if (vertex4 != null) { flag1 = Primitives.InCircle(vertex9, vertex10, vertex11, vertex4) > 0; while (flag1) { otri2.LnextSelf(); otri2.Sym(ref otri4); otri2.LnextSelf(); otri2.Sym(ref otri3); otri2.Bond(ref otri4); otri.Bond(ref otri3); otri.LnextSelf(); otri.Sym(ref otri5); otri2.LprevSelf(); otri2.Bond(ref otri5); otri.SetOrg(vertex9); otri.SetDest(null); otri.SetApex(vertex4); otri2.SetOrg(null); otri2.SetDest(vertex11); otri2.SetApex(vertex4); vertex11 = vertex4; otri3.Copy(ref otri2); vertex4 = otri2.Apex(); flag1 = (vertex4 == null ? false : Primitives.InCircle(vertex9, vertex10, vertex11, vertex4) > 0); } } } if (!flag3) { otri1.Lnext(ref otri2); otri2.SymSelf(); vertex4 = otri2.Apex(); if (vertex4 != null) { flag1 = Primitives.InCircle(vertex9, vertex10, vertex12, vertex4) > 0; while (flag1) { otri2.LprevSelf(); otri2.Sym(ref otri4); otri2.LprevSelf(); otri2.Sym(ref otri3); otri2.Bond(ref otri4); otri1.Bond(ref otri3); otri1.LprevSelf(); otri1.Sym(ref otri5); otri2.LnextSelf(); otri2.Bond(ref otri5); otri1.SetOrg(null); otri1.SetDest(vertex10); otri1.SetApex(vertex4); otri2.SetOrg(vertex12); otri2.SetDest(null); otri2.SetApex(vertex4); vertex12 = vertex4; otri3.Copy(ref otri2); vertex4 = otri2.Apex(); flag1 = (vertex4 == null ? false : Primitives.InCircle(vertex9, vertex10, vertex12, vertex4) > 0); } } } if (flag2 || !flag3 && Primitives.InCircle(vertex11, vertex9, vertex10, vertex12) > 0) { otri7.Bond(ref otri1); otri1.Lprev(ref otri7); otri7.SetDest(vertex9); vertex10 = vertex12; otri7.Sym(ref otri1); vertex12 = otri1.Apex(); } else { otri7.Bond(ref otri); otri.Lnext(ref otri7); otri7.SetOrg(vertex10); vertex9 = vertex11; otri7.Sym(ref otri); vertex11 = otri.Apex(); } } this.mesh.MakeTriangle(ref otri2); otri2.SetOrg(vertex9); otri2.SetDest(vertex10); otri2.Bond(ref otri7); otri2.LnextSelf(); otri2.Bond(ref otri1); otri2.LnextSelf(); otri2.Bond(ref otri); if (this.useDwyer && axis == 1) { vertex = farleft.Org(); vertex2 = farleft.Apex(); vertex1 = farright.Dest(); vertex3 = farright.Apex(); farleft.Sym(ref otri6); for (i = otri6.Apex(); i.x < vertex.x; i = otri6.Apex()) { otri6.Lprev(ref farleft); vertex2 = vertex; vertex = i; farleft.Sym(ref otri6); } while (vertex3.x > vertex1.x) { farright.LprevSelf(); farright.SymSelf(); vertex1 = vertex3; vertex3 = farright.Apex(); } } }