internal InsertVertexResult InsertVertex(Vertex newvertex, ref Otri searchtri,
ref Osub splitseg, bool segmentflaws, bool triflaws)
{
Otri horiz = default(Otri);
Otri top = default(Otri);
Otri botleft = default(Otri), botright = default(Otri);
Otri topleft = default(Otri), topright = default(Otri);
Otri newbotleft = default(Otri), newbotright = default(Otri);
Otri newtopright = default(Otri);
Otri botlcasing = default(Otri), botrcasing = default(Otri);
Otri toplcasing = default(Otri), toprcasing = default(Otri);
Otri testtri = default(Otri);
Osub botlsubseg = default(Osub), botrsubseg = default(Osub);
Osub toplsubseg = default(Osub), toprsubseg = default(Osub);
Osub brokensubseg = default(Osub);
Osub checksubseg = default(Osub);
Osub rightsubseg = default(Osub);
Osub newsubseg = default(Osub);
BadSubseg encroached;
//FlipStacker newflip;
Vertex first;
Vertex leftvertex, rightvertex, botvertex, topvertex, farvertex;
Vertex segmentorg, segmentdest;
int region;
double area;
InsertVertexResult success;
LocateResult intersect;
bool doflip;
bool mirrorflag;
bool enq;
if (splitseg.seg == null)
{
// Find the location of the vertex to be inserted. Check if a good
// starting triangle has already been provided by the caller.
if (searchtri.triangle == dummytri)
{
// Find a boundary triangle.
horiz.triangle = dummytri;
horiz.orient = 0;
horiz.SymSelf();
// Search for a triangle containing 'newvertex'.
intersect = locator.Locate(newvertex, ref horiz);
}
else
{
// Start searching from the triangle provided by the caller.
searchtri.Copy(ref horiz);
intersect = locator.PreciseLocate(newvertex, ref horiz, true);
}
}
else
{
// The calling routine provides the subsegment in which
// the vertex is inserted.
searchtri.Copy(ref horiz);
intersect = LocateResult.OnEdge;
}
if (intersect == LocateResult.OnVertex)
{
// There's already a vertex there. Return in 'searchtri' a triangle
// whose origin is the existing vertex.
horiz.Copy(ref searchtri);
locator.Update(ref horiz);
return InsertVertexResult.Duplicate;
}
if ((intersect == LocateResult.OnEdge) || (intersect == LocateResult.Outside))
{
// The vertex falls on an edge or boundary.
if (checksegments && (splitseg.seg == null))
{
// Check whether the vertex falls on a subsegment.
horiz.SegPivot(ref brokensubseg);
if (brokensubseg.seg != dummysub)
{
// The vertex falls on a subsegment, and hence will not be inserted.
if (segmentflaws)
{
enq = behavior.NoBisect != 2;
if (enq && (behavior.NoBisect == 1))
{
// This subsegment may be split only if it is an
// internal boundary.
horiz.Sym(ref testtri);
enq = testtri.triangle != dummytri;
}
if (enq)
{
// Add the subsegment to the list of encroached subsegments.
encroached = new BadSubseg();
encroached.encsubseg = brokensubseg;
encroached.subsegorg = brokensubseg.Org();
encroached.subsegdest = brokensubseg.Dest();
quality.AddBadSubseg(encroached);
}
}
// Return a handle whose primary edge contains the vertex,
// which has not been inserted.
horiz.Copy(ref searchtri);
locator.Update(ref horiz);
return InsertVertexResult.Violating;
}
}
// Insert the vertex on an edge, dividing one triangle into two (if
// the edge lies on a boundary) or two triangles into four.
horiz.Lprev(ref botright);
botright.Sym(ref botrcasing);
horiz.Sym(ref topright);
// Is there a second triangle? (Or does this edge lie on a boundary?)
mirrorflag = topright.triangle != dummytri;
if (mirrorflag)
{
topright.LnextSelf();
topright.Sym(ref toprcasing);
MakeTriangle(ref newtopright);
}
else
{
// Splitting a boundary edge increases the number of boundary edges.
hullsize++;
}
MakeTriangle(ref newbotright);
// Set the vertices of changed and new triangles.
rightvertex = horiz.Org();
leftvertex = horiz.Dest();
botvertex = horiz.Apex();
newbotright.SetOrg(botvertex);
newbotright.SetDest(rightvertex);
newbotright.SetApex(newvertex);
horiz.SetOrg(newvertex);
// Set the region of a new triangle.
newbotright.triangle.region = botright.triangle.region;
if (behavior.VarArea)
{
// Set the area constraint of a new triangle.
newbotright.triangle.area = botright.triangle.area;
}
if (mirrorflag)
{
topvertex = topright.Dest();
newtopright.SetOrg(rightvertex);
newtopright.SetDest(topvertex);
newtopright.SetApex(newvertex);
topright.SetOrg(newvertex);
// Set the region of another new triangle.
newtopright.triangle.region = topright.triangle.region;
if (behavior.VarArea)
{
// Set the area constraint of another new triangle.
newtopright.triangle.area = topright.triangle.area;
}
}
// There may be subsegments that need to be bonded
// to the new triangle(s).
if (checksegments)
{
botright.SegPivot(ref botrsubseg);
if (botrsubseg.seg != dummysub)
{
botright.SegDissolve();
newbotright.SegBond(ref botrsubseg);
}
if (mirrorflag)
{
topright.SegPivot(ref toprsubseg);
if (toprsubseg.seg != dummysub)
{
topright.SegDissolve();
newtopright.SegBond(ref toprsubseg);
}
}
}
// Bond the new triangle(s) to the surrounding triangles.
newbotright.Bond(ref botrcasing);
newbotright.LprevSelf();
newbotright.Bond(ref botright);
newbotright.LprevSelf();
if (mirrorflag)
{
newtopright.Bond(ref toprcasing);
newtopright.LnextSelf();
newtopright.Bond(ref topright);
newtopright.LnextSelf();
newtopright.Bond(ref newbotright);
}
if (splitseg.seg != null)
{
// Split the subsegment into two.
splitseg.SetDest(newvertex);
segmentorg = splitseg.SegOrg();
segmentdest = splitseg.SegDest();
splitseg.SymSelf();
splitseg.Pivot(ref rightsubseg);
InsertSubseg(ref newbotright, splitseg.seg.boundary);
newbotright.SegPivot(ref newsubseg);
newsubseg.SetSegOrg(segmentorg);
newsubseg.SetSegDest(segmentdest);
splitseg.Bond(ref newsubseg);
newsubseg.SymSelf();
newsubseg.Bond(ref rightsubseg);
splitseg.SymSelf();
// Transfer the subsegment's boundary marker to the vertex if required.
if (newvertex.mark == 0)
{
newvertex.mark = splitseg.seg.boundary;
}
}
if (checkquality)
{
flipstack.Clear();
flipstack.Push(default(Otri)); // Dummy flip (see UndoVertex)
flipstack.Push(horiz);
}
// Position 'horiz' on the first edge to check for
// the Delaunay property.
horiz.LnextSelf();
}
else
{
// Insert the vertex in a triangle, splitting it into three.
horiz.Lnext(ref botleft);
horiz.Lprev(ref botright);
botleft.Sym(ref botlcasing);
botright.Sym(ref botrcasing);
MakeTriangle(ref newbotleft);
MakeTriangle(ref newbotright);
// Set the vertices of changed and new triangles.
rightvertex = horiz.Org();
leftvertex = horiz.Dest();
botvertex = horiz.Apex();
newbotleft.SetOrg(leftvertex);
newbotleft.SetDest(botvertex);
newbotleft.SetApex(newvertex);
newbotright.SetOrg(botvertex);
newbotright.SetDest(rightvertex);
newbotright.SetApex(newvertex);
horiz.SetApex(newvertex);
// Set the region of the new triangles.
newbotleft.triangle.region = horiz.triangle.region;
newbotright.triangle.region = horiz.triangle.region;
if (behavior.VarArea)
{
// Set the area constraint of the new triangles.
area = horiz.triangle.area;
newbotleft.triangle.area = area;
newbotright.triangle.area = area;
}
// There may be subsegments that need to be bonded
// to the new triangles.
if (checksegments)
{
botleft.SegPivot(ref botlsubseg);
if (botlsubseg.seg != dummysub)
{
botleft.SegDissolve();
newbotleft.SegBond(ref botlsubseg);
}
botright.SegPivot(ref botrsubseg);
if (botrsubseg.seg != dummysub)
{
botright.SegDissolve();
newbotright.SegBond(ref botrsubseg);
}
}
// Bond the new triangles to the surrounding triangles.
newbotleft.Bond(ref botlcasing);
newbotright.Bond(ref botrcasing);
newbotleft.LnextSelf();
newbotright.LprevSelf();
newbotleft.Bond(ref newbotright);
newbotleft.LnextSelf();
botleft.Bond(ref newbotleft);
newbotright.LprevSelf();
botright.Bond(ref newbotright);
if (checkquality)
{
flipstack.Clear();
flipstack.Push(horiz);
}
}
// The insertion is successful by default, unless an encroached
// subsegment is found.
success = InsertVertexResult.Successful;
// Circle around the newly inserted vertex, checking each edge opposite it
// for the Delaunay property. Non-Delaunay edges are flipped. 'horiz' is
// always the edge being checked. 'first' marks where to stop circling.
first = horiz.Org();
rightvertex = first;
leftvertex = horiz.Dest();
// Circle until finished.
while (true)
{
// By default, the edge will be flipped.
doflip = true;
if (checksegments)
{
// Check for a subsegment, which cannot be flipped.
horiz.SegPivot(ref checksubseg);
if (checksubseg.seg != dummysub)
{
// The edge is a subsegment and cannot be flipped.
doflip = false;
if (segmentflaws)
{
// Does the new vertex encroach upon this subsegment?
if (quality.CheckSeg4Encroach(ref checksubseg) > 0)
{
success = InsertVertexResult.Encroaching;
}
}
}
}
if (doflip)
{
// Check if the edge is a boundary edge.
horiz.Sym(ref top);
if (top.triangle == dummytri)
{
// The edge is a boundary edge and cannot be flipped.
doflip = false;
}
else
{
// Find the vertex on the other side of the edge.
farvertex = top.Apex();
// In the incremental Delaunay triangulation algorithm, any of
// 'leftvertex', 'rightvertex', and 'farvertex' could be vertices
// of the triangular bounding box. These vertices must be
// treated as if they are infinitely distant, even though their
// "coordinates" are not.
if ((leftvertex == infvertex1) || (leftvertex == infvertex2) ||
(leftvertex == infvertex3))
{
// 'leftvertex' is infinitely distant. Check the convexity of
// the boundary of the triangulation. 'farvertex' might be
// infinite as well, but trust me, this same condition should
// be applied.
doflip = Primitives.CounterClockwise(newvertex, rightvertex, farvertex) > 0.0;
}
else if ((rightvertex == infvertex1) ||
(rightvertex == infvertex2) ||
(rightvertex == infvertex3))
{
// 'rightvertex' is infinitely distant. Check the convexity of
// the boundary of the triangulation. 'farvertex' might be
// infinite as well, but trust me, this same condition should
// be applied.
doflip = Primitives.CounterClockwise(farvertex, leftvertex, newvertex) > 0.0;
}
else if ((farvertex == infvertex1) ||
(farvertex == infvertex2) ||
(farvertex == infvertex3))
{
// 'farvertex' is infinitely distant and cannot be inside
// the circumcircle of the triangle 'horiz'.
doflip = false;
}
else
{
// Test whether the edge is locally Delaunay.
doflip = Primitives.InCircle(leftvertex, newvertex, rightvertex, farvertex) > 0.0;
}
if (doflip)
{
// We made it! Flip the edge 'horiz' by rotating its containing
// quadrilateral (the two triangles adjacent to 'horiz').
// Identify the casing of the quadrilateral.
top.Lprev(ref topleft);
topleft.Sym(ref toplcasing);
top.Lnext(ref topright);
topright.Sym(ref toprcasing);
horiz.Lnext(ref botleft);
botleft.Sym(ref botlcasing);
horiz.Lprev(ref botright);
botright.Sym(ref botrcasing);
// Rotate the quadrilateral one-quarter turn counterclockwise.
topleft.Bond(ref botlcasing);
botleft.Bond(ref botrcasing);
botright.Bond(ref toprcasing);
topright.Bond(ref toplcasing);
if (checksegments)
{
// Check for subsegments and rebond them to the quadrilateral.
topleft.SegPivot(ref toplsubseg);
botleft.SegPivot(ref botlsubseg);
botright.SegPivot(ref botrsubseg);
topright.SegPivot(ref toprsubseg);
if (toplsubseg.seg == dummysub)
{
topright.SegDissolve();
}
else
{
topright.SegBond(ref toplsubseg);
}
if (botlsubseg.seg == dummysub)
{
topleft.SegDissolve();
}
else
{
topleft.SegBond(ref botlsubseg);
}
if (botrsubseg.seg == dummysub)
{
botleft.SegDissolve();
}
else
{
botleft.SegBond(ref botrsubseg);
}
if (toprsubseg.seg == dummysub)
{
botright.SegDissolve();
}
else
{
botright.SegBond(ref toprsubseg);
}
}
// New vertex assignments for the rotated quadrilateral.
horiz.SetOrg(farvertex);
horiz.SetDest(newvertex);
horiz.SetApex(rightvertex);
top.SetOrg(newvertex);
top.SetDest(farvertex);
top.SetApex(leftvertex);
// Assign region.
// TODO: check region ok (no Math.Min necessary)
region = Math.Min(top.triangle.region, horiz.triangle.region);
top.triangle.region = region;
horiz.triangle.region = region;
if (behavior.VarArea)
{
if ((top.triangle.area <= 0.0) || (horiz.triangle.area <= 0.0))
{
area = -1.0;
}
else
{
// Take the average of the two triangles' area constraints.
// This prevents small area constraints from migrating a
// long, long way from their original location due to flips.
area = 0.5 * (top.triangle.area + horiz.triangle.area);
}
top.triangle.area = area;
horiz.triangle.area = area;
}
if (checkquality)
{
flipstack.Push(horiz);
}
// On the next iterations, consider the two edges that were exposed (this
// is, are now visible to the newly inserted vertex) by the edge flip.
horiz.LprevSelf();
leftvertex = farvertex;
}
}
}
if (!doflip)
{
// The handle 'horiz' is accepted as locally Delaunay.
if (triflaws)
{
// Check the triangle 'horiz' for quality.
quality.TestTriangle(ref horiz);
}
// Look for the next edge around the newly inserted vertex.
horiz.LnextSelf();
horiz.Sym(ref testtri);
// Check for finishing a complete revolution about the new vertex, or
// falling outside of the triangulation. The latter will happen when
// a vertex is inserted at a boundary.
if ((leftvertex == first) || (testtri.triangle == dummytri))
{
// We're done. Return a triangle whose origin is the new vertex.
horiz.Lnext(ref searchtri);
Otri recenttri = default(Otri);
horiz.Lnext(ref recenttri);
locator.Update(ref recenttri);
return success;
}
// Finish finding the next edge around the newly inserted vertex.
testtri.Lnext(ref horiz);
rightvertex = leftvertex;
leftvertex = horiz.Dest();
}
}
}