/// <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; } }
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; } }