public int CheckSeg4Encroach(ref Osub testsubseg) { double num; Vertex vertex; Otri otri = new Otri(); Osub osub = new Osub(); int num1 = 0; int num2 = 0; Vertex vertex1 = testsubseg.Org(); Vertex vertex2 = testsubseg.Dest(); testsubseg.TriPivot(ref otri); if (otri.triangle != Mesh.dummytri) { num2++; vertex = otri.Apex(); num = (vertex1.x - vertex.x) * (vertex2.x - vertex.x) + (vertex1.y - vertex.y) * (vertex2.y - vertex.y); if (num < 0 && (this.behavior.ConformingDelaunay || num * num >= (2 * this.behavior.goodAngle - 1) * (2 * this.behavior.goodAngle - 1) * ((vertex1.x - vertex.x) * (vertex1.x - vertex.x) + (vertex1.y - vertex.y) * (vertex1.y - vertex.y)) * ((vertex2.x - vertex.x) * (vertex2.x - vertex.x) + (vertex2.y - vertex.y) * (vertex2.y - vertex.y)))) { num1 = 1; } } testsubseg.Sym(ref osub); osub.TriPivot(ref otri); if (otri.triangle != Mesh.dummytri) { num2++; vertex = otri.Apex(); num = (vertex1.x - vertex.x) * (vertex2.x - vertex.x) + (vertex1.y - vertex.y) * (vertex2.y - vertex.y); if (num < 0 && (this.behavior.ConformingDelaunay || num * num >= (2 * this.behavior.goodAngle - 1) * (2 * this.behavior.goodAngle - 1) * ((vertex1.x - vertex.x) * (vertex1.x - vertex.x) + (vertex1.y - vertex.y) * (vertex1.y - vertex.y)) * ((vertex2.x - vertex.x) * (vertex2.x - vertex.x) + (vertex2.y - vertex.y) * (vertex2.y - vertex.y)))) { num1 = num1 + 2; } } if (num1 > 0 && (this.behavior.NoBisect == 0 || this.behavior.NoBisect == 1 && num2 == 2)) { BadSubseg badSubseg = new BadSubseg(); if (num1 != 1) { badSubseg.encsubseg = osub; badSubseg.subsegorg = vertex2; badSubseg.subsegdest = vertex1; } else { badSubseg.encsubseg = testsubseg; badSubseg.subsegorg = vertex1; badSubseg.subsegdest = vertex2; } this.badsubsegs.Enqueue(badSubseg); } return(num1); }
/// <summary> /// Tag all blind triangles. /// </summary> /// <remarks> /// A triangle is said to be blind if the triangle and its circumcenter /// lie on two different sides of a constrained edge. /// </remarks> private void TagBlindTriangles() { int blinded = 0; Stack <Triangle> triangles; subsegMap = new Dictionary <int, SubSegment>(); Otri f = default(Otri); Otri f0 = default(Otri); Osub e = default(Osub); Osub sub1 = default(Osub); // Tag all triangles non-blind foreach (var t in _TriangleNetMesh.triangles) { // Use the infected flag for 'blinded' attribute. t.infected = false; } // for each constrained edge e of cdt do foreach (var ss in _TriangleNetMesh.subsegs.Values) { // Create a stack: triangles triangles = new Stack <Triangle>(); // for both adjacent triangles fe to e tagged non-blind do // Push fe into triangles e.seg = ss; e.orient = 0; e.Pivot(ref f); if (f.tri.id != TriangleNetMesh.DUMMY && !f.tri.infected) { triangles.Push(f.tri); } e.Sym(); e.Pivot(ref f); if (f.tri.id != TriangleNetMesh.DUMMY && !f.tri.infected) { triangles.Push(f.tri); } // while triangles is non-empty while (triangles.Count > 0) { // Pop f from stack triangles f.tri = triangles.Pop(); f.orient = 0; // if f is blinded by e (use P) then if (TriangleIsBlinded(ref f, ref e)) { // Tag f as blinded by e f.tri.infected = true; blinded++; // Store association triangle -> subseg subsegMap.Add(f.tri.hash, e.seg); // for each adjacent triangle f0 to f do for (f.orient = 0; f.orient < 3; f.orient++) { f.Sym(ref f0); f0.Pivot(ref sub1); // if f0 is finite and tagged non-blind & the common edge // between f and f0 is unconstrained then if (f0.tri.id != TriangleNetMesh.DUMMY && !f0.tri.infected && sub1.seg.hash == TriangleNetMesh.DUMMY) { // Push f0 into triangles. triangles.Push(f0.tri); } } } } } blinded = 0; }
/// <summary> /// Check a subsegment to see if it is encroached; add it to the list if it is. /// </summary> /// <param name="testsubseg">The subsegment to check.</param> /// <returns>Returns a nonzero value if the subsegment is encroached.</returns> /// <remarks> /// A subsegment is encroached if there is a vertex in its diametral lens. /// For Ruppert's algorithm (-D switch), the "diametral lens" is the /// diametral circle. For Chew's algorithm (default), the diametral lens is /// just big enough to enclose two isosceles triangles whose bases are the /// subsegment. Each of the two isosceles triangles has two angles equal /// to 'b.minangle'. /// /// Chew's algorithm does not require diametral lenses at all--but they save /// time. Any vertex inside a subsegment's diametral lens implies that the /// triangle adjoining the subsegment will be too skinny, so it's only a /// matter of time before the encroaching vertex is deleted by Chew's /// algorithm. It's faster to simply not insert the doomed vertex in the /// first place, which is why I use diametral lenses with Chew's algorithm. /// </remarks> public int CheckSeg4Encroach(ref Osub testsubseg) { Otri neighbortri = default(Otri); Osub testsym = default(Osub); BadSubseg encroachedseg; float dotproduct; int encroached; int sides; Vertex eorg, edest, eapex; encroached = 0; sides = 0; eorg = testsubseg.Org(); edest = testsubseg.Dest(); // Check one neighbor of the subsegment. testsubseg.TriPivot(ref neighbortri); // Does the neighbor exist, or is this a boundary edge? if (neighbortri.triangle != Mesh.dummytri) { sides++; // Find a vertex opposite this subsegment. eapex = neighbortri.Apex(); // Check whether the apex is in the diametral lens of the subsegment // (the diametral circle if 'conformdel' is set). A dot product // of two sides of the triangle is used to check whether the angle // at the apex is greater than (180 - 2 'minangle') degrees (for // lenses; 90 degrees for diametral circles). dotproduct = (eorg.x - eapex.x) * (edest.x - eapex.x) + (eorg.y - eapex.y) * (edest.y - eapex.y); if (dotproduct < 0.0) { if (behavior.ConformingDelaunay || (dotproduct * dotproduct >= (2.0 * behavior.goodAngle - 1.0) * (2.0 * behavior.goodAngle - 1.0) * ((eorg.x - eapex.x) * (eorg.x - eapex.x) + (eorg.y - eapex.y) * (eorg.y - eapex.y)) * ((edest.x - eapex.x) * (edest.x - eapex.x) + (edest.y - eapex.y) * (edest.y - eapex.y)))) { encroached = 1; } } } // Check the other neighbor of the subsegment. testsubseg.Sym(ref testsym); testsym.TriPivot(ref neighbortri); // Does the neighbor exist, or is this a boundary edge? if (neighbortri.triangle != Mesh.dummytri) { sides++; // Find the other vertex opposite this subsegment. eapex = neighbortri.Apex(); // Check whether the apex is in the diametral lens of the subsegment // (or the diametral circle, if 'conformdel' is set). dotproduct = (eorg.x - eapex.x) * (edest.x - eapex.x) + (eorg.y - eapex.y) * (edest.y - eapex.y); if (dotproduct < 0.0) { if (behavior.ConformingDelaunay || (dotproduct * dotproduct >= (2.0 * behavior.goodAngle - 1.0) * (2.0 * behavior.goodAngle - 1.0) * ((eorg.x - eapex.x) * (eorg.x - eapex.x) + (eorg.y - eapex.y) * (eorg.y - eapex.y)) * ((edest.x - eapex.x) * (edest.x - eapex.x) + (edest.y - eapex.y) * (edest.y - eapex.y)))) { encroached += 2; } } } if (encroached > 0 && (behavior.NoBisect == 0 || ((behavior.NoBisect == 1) && (sides == 2)))) { // Add the subsegment to the list of encroached subsegments. // Be sure to get the orientation right. encroachedseg = new BadSubseg(); if (encroached == 1) { encroachedseg.encsubseg = testsubseg; encroachedseg.subsegorg = eorg; encroachedseg.subsegdest = edest; } else { encroachedseg.encsubseg = testsym; encroachedseg.subsegorg = edest; encroachedseg.subsegdest = eorg; } badsubsegs.Enqueue(encroachedseg); } return(encroached); }
/// <summary> /// Find the intersection of an existing segment and a segment that is being /// inserted. Insert a vertex at the intersection, splitting an existing subsegment. /// </summary> /// <param name="splittri"></param> /// <param name="splitsubseg"></param> /// <param name="endpoint2"></param> /// <remarks> /// The segment being inserted connects the apex of splittri to endpoint2. /// splitsubseg is the subsegment being split, and MUST adjoin splittri. /// Hence, endpoints of the subsegment being split are the origin and /// destination of splittri. /// On completion, splittri is a handle having the newly inserted /// intersection point as its origin, and endpoint1 as its destination. /// </remarks> private void SegmentIntersection(ref Otri splittri, ref Osub splitsubseg, Vertex endpoint2) { Osub opposubseg = default(Osub); Vertex endpoint1; Vertex torg, tdest; Vertex leftvertex, rightvertex; Vertex newvertex; InsertVertexResult success; var dummysub = mesh.dummysub; double ex, ey; double tx, ty; double etx, ety; double split, denom; // Find the other three segment endpoints. endpoint1 = splittri.Apex(); torg = splittri.Org(); tdest = splittri.Dest(); // Segment intersection formulae; see the Antonio reference. tx = tdest.X - torg.X; ty = tdest.Y - torg.Y; ex = endpoint2.X - endpoint1.X; ey = endpoint2.Y - endpoint1.Y; etx = torg.X - endpoint2.X; ety = torg.Y - endpoint2.Y; denom = ty * ex - tx * ey; if (denom == 0.0) { throw new Exception("Attempt to find intersection of parallel segments."); } split = (ey * etx - ex * ety) / denom; // Create the new vertex. newvertex = new Vertex( torg.X + split * (tdest.X - torg.X), torg.Y + split * (tdest.Y - torg.Y), splitsubseg.seg.boundary); newvertex.Id = mesh.hash_vtx++; mesh.vertices.Add(newvertex.Id, newvertex); // Insert the intersection vertex. This should always succeed. success = mesh.InsertVertex(newvertex, ref splittri, ref splitsubseg, false, false); if (success != InsertVertexResult.Successful) { throw new Exception("Failure to split a segment."); } // Record a triangle whose origin is the new vertex. newvertex.tri = splittri; if (mesh.steinerleft > 0) { mesh.steinerleft--; } // Divide the segment into two, and correct the segment endpoints. splitsubseg.Sym(); splitsubseg.Pivot(ref opposubseg); splitsubseg.Dissolve(dummysub); opposubseg.Dissolve(dummysub); do { splitsubseg.SetSegOrg(newvertex); splitsubseg.Next(); } while (splitsubseg.seg.hash != Mesh.DUMMY); do { opposubseg.SetSegOrg(newvertex); opposubseg.Next(); } while (opposubseg.seg.hash != Mesh.DUMMY); // Inserting the vertex may have caused edge flips. We wish to rediscover // the edge connecting endpoint1 to the new intersection vertex. FindDirection(ref splittri, endpoint1); rightvertex = splittri.Dest(); leftvertex = splittri.Apex(); if ((leftvertex.X == endpoint1.X) && (leftvertex.Y == endpoint1.Y)) { splittri.Onext(); } else if ((rightvertex.X != endpoint1.X) || (rightvertex.Y != endpoint1.Y)) { throw new Exception("Topological inconsistency after splitting a segment."); } // 'splittri' should have destination endpoint1. }