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;
double segmentlength, nearestpoweroftwo;
double split;
double 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 = Math.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.0;
while (segmentlength > 3.0 * nearestpoweroftwo)
{
nearestpoweroftwo *= 2.0;
}
while (segmentlength < 1.5 * nearestpoweroftwo)
{
nearestpoweroftwo *= 0.5;
}
// Where do we split the segment?
split = nearestpoweroftwo / segmentlength;
if (acutedest)
{
split = 1.0 - split;
}
}
else
{
// If we're not worried about adjacent segments, split
// this segment in the middle.
split = 0.5;
}
// 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 (!double.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;
}
}