public void TestTriangle(ref Otri testtri)
{
Otri tri1 = default(Otri), tri2 = default(Otri);
Osub testsub = default(Osub);
Vertex torg, tdest, tapex;
Vertex base1, base2;
Vertex org1, dest1, org2, dest2;
Vertex joinvertex;
double dxod, dyod, dxda, dyda, dxao, dyao;
double dxod2, dyod2, dxda2, dyda2, dxao2, dyao2;
double apexlen, orglen, destlen, minedge;
double angle;
double area;
double dist1, dist2;
double maxangle;
torg = testtri.Org();
tdest = testtri.Dest();
tapex = testtri.Apex();
dxod = torg.x - tdest.x;
dyod = torg.y - tdest.y;
dxda = tdest.x - tapex.x;
dyda = tdest.y - tapex.y;
dxao = tapex.x - torg.x;
dyao = tapex.y - torg.y;
dxod2 = dxod * dxod;
dyod2 = dyod * dyod;
dxda2 = dxda * dxda;
dyda2 = dyda * dyda;
dxao2 = dxao * dxao;
dyao2 = dyao * dyao;
// Find the lengths of the triangle's three edges.
apexlen = dxod2 + dyod2;
orglen = dxda2 + dyda2;
destlen = dxao2 + dyao2;
if ((apexlen < orglen) && (apexlen < destlen))
{
// The edge opposite the apex is shortest.
minedge = apexlen;
// Find the square of the cosine of the angle at the apex.
angle = dxda * dxao + dyda * dyao;
angle = angle * angle / (orglen * destlen);
base1 = torg;
base2 = tdest;
testtri.Copy(ref tri1);
}
else if (orglen < destlen)
{
// The edge opposite the origin is shortest.
minedge = orglen;
// Find the square of the cosine of the angle at the origin.
angle = dxod * dxao + dyod * dyao;
angle = angle * angle / (apexlen * destlen);
base1 = tdest;
base2 = tapex;
testtri.Lnext(ref tri1);
}
else
{
// The edge opposite the destination is shortest.
minedge = destlen;
// Find the square of the cosine of the angle at the destination.
angle = dxod * dxda + dyod * dyda;
angle = angle * angle / (apexlen * orglen);
base1 = tapex;
base2 = torg;
testtri.Lprev(ref tri1);
}
if (behavior.VarArea || behavior.fixedArea || behavior.Usertest)
{
// Check whether the area is larger than permitted.
area = 0.5 * (dxod * dyda - dyod * dxda);
if (behavior.fixedArea && (area > behavior.MaxArea))
{
// Add this triangle to the list of bad triangles.
queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
return;
}
// Nonpositive area constraints are treated as unconstrained.
if ((behavior.VarArea) && (area > testtri.triangle.area) && (testtri.triangle.area > 0.0))
{
// Add this triangle to the list of bad triangles.
queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
return;
}
// Check whether the user thinks this triangle is too large.
if (behavior.Usertest && userTest != null)
{
if (userTest(torg, tdest, tapex, area))
{
queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
return;
}
}
}
// find the maximum edge and accordingly the pqr orientation
if ((apexlen > orglen) && (apexlen > destlen))
{
// The edge opposite the apex is longest.
// maxedge = apexlen;
// Find the cosine of the angle at the apex.
maxangle = (orglen + destlen - apexlen) / (2 * Math.Sqrt(orglen * destlen));
}
else if (orglen > destlen)
{
// The edge opposite the origin is longest.
// maxedge = orglen;
// Find the cosine of the angle at the origin.
maxangle = (apexlen + destlen - orglen) / (2 * Math.Sqrt(apexlen * destlen));
}
else
{
// The edge opposite the destination is longest.
// maxedge = destlen;
// Find the cosine of the angle at the destination.
maxangle = (apexlen + orglen - destlen) / (2 * Math.Sqrt(apexlen * orglen));
}
// Check whether the angle is smaller than permitted.
if ((angle > behavior.goodAngle) || (maxangle < behavior.maxGoodAngle && behavior.MaxAngle != 0.0))
{
// Use the rules of Miller, Pav, and Walkington to decide that certain
// triangles should not be split, even if they have bad angles.
// A skinny triangle is not split if its shortest edge subtends a
// small input angle, and both endpoints of the edge lie on a
// concentric circular shell. For convenience, I make a small
// adjustment to that rule: I check if the endpoints of the edge
// both lie in segment interiors, equidistant from the apex where
// the two segments meet.
// First, check if both points lie in segment interiors.
if ((base1.type == VertexType.SegmentVertex) &&
(base2.type == VertexType.SegmentVertex))
{
// Check if both points lie in a common segment. If they do, the
// skinny triangle is enqueued to be split as usual.
tri1.SegPivot(ref testsub);
if (testsub.seg == Mesh.dummysub)
{
// No common segment. Find a subsegment that contains 'torg'.
tri1.Copy(ref tri2);
do
{
tri1.OprevSelf();
tri1.SegPivot(ref testsub);
} while (testsub.seg == Mesh.dummysub);
// Find the endpoints of the containing segment.
org1 = testsub.SegOrg();
dest1 = testsub.SegDest();
// Find a subsegment that contains 'tdest'.
do
{
tri2.DnextSelf();
tri2.SegPivot(ref testsub);
} while (testsub.seg == Mesh.dummysub);
// Find the endpoints of the containing segment.
org2 = testsub.SegOrg();
dest2 = testsub.SegDest();
// Check if the two containing segments have an endpoint in common.
joinvertex = null;
if ((dest1.x == org2.x) && (dest1.y == org2.y))
{
joinvertex = dest1;
}
else if ((org1.x == dest2.x) && (org1.y == dest2.y))
{
joinvertex = org1;
}
if (joinvertex != null)
{
// Compute the distance from the common endpoint (of the two
// segments) to each of the endpoints of the shortest edge.
dist1 = ((base1.x - joinvertex.x) * (base1.x - joinvertex.x) +
(base1.y - joinvertex.y) * (base1.y - joinvertex.y));
dist2 = ((base2.x - joinvertex.x) * (base2.x - joinvertex.x) +
(base2.y - joinvertex.y) * (base2.y - joinvertex.y));
// If the two distances are equal, don't split the triangle.
if ((dist1 < 1.001 * dist2) && (dist1 > 0.999 * dist2))
{
// Return now to avoid enqueueing the bad triangle.
return;
}
}
}
}
// Add this triangle to the list of bad triangles.
queue.Enqueue(ref testtri, minedge, tapex, torg, tdest);
}
}