/// <summary> /// Test a triangle for quality and size. /// </summary> /// <param name="testtri">Triangle to check.</param> /// <remarks> /// Tests a triangle to see if it satisfies the minimum angle condition and /// the maximum area condition. Triangles that aren't up to spec are added /// to the bad triangle queue. /// </remarks> 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); } }
public void TestTriangle(ref Otri testtri) { Vertex vertex; Vertex vertex1; double num; double num1; double num2; Otri otri = new Otri(); Otri otri1 = new Otri(); Osub osub = new Osub(); Vertex vertex2 = testtri.Org(); Vertex vertex3 = testtri.Dest(); Vertex vertex4 = testtri.Apex(); double num3 = vertex2.x - vertex3.x; double num4 = vertex2.y - vertex3.y; double num5 = vertex3.x - vertex4.x; double num6 = vertex3.y - vertex4.y; double num7 = vertex4.x - vertex2.x; double num8 = vertex4.y - vertex2.y; double num9 = num3 * num3; double num10 = num4 * num4; double num11 = num5 * num5; double num12 = num6 * num6; double num13 = num8 * num8; double num14 = num9 + num10; double num15 = num11 + num12; double num16 = num7 * num7 + num13; if (num14 < num15 && num14 < num16) { num = num14; num1 = num5 * num7 + num6 * num8; num1 = num1 * num1 / (num15 * num16); vertex = vertex2; vertex1 = vertex3; testtri.Copy(ref otri); } else if (num15 >= num16) { num = num16; num1 = num3 * num5 + num4 * num6; num1 = num1 * num1 / (num14 * num15); vertex = vertex4; vertex1 = vertex2; testtri.Lprev(ref otri); } else { num = num15; num1 = num3 * num7 + num4 * num8; num1 = num1 * num1 / (num14 * num16); vertex = vertex3; vertex1 = vertex4; testtri.Lnext(ref otri); } if (this.behavior.VarArea || this.behavior.fixedArea || this.behavior.Usertest) { double num17 = 0.5 * (num3 * num6 - num4 * num5); if (this.behavior.fixedArea && num17 > this.behavior.MaxArea) { this.queue.Enqueue(ref testtri, num, vertex4, vertex2, vertex3); return; } if (this.behavior.VarArea && num17 > testtri.triangle.area && testtri.triangle.area > 0) { this.queue.Enqueue(ref testtri, num, vertex4, vertex2, vertex3); return; } if (this.behavior.Usertest && this.userTest != null && this.userTest(vertex2, vertex3, vertex4, num17)) { this.queue.Enqueue(ref testtri, num, vertex4, vertex2, vertex3); return; } } if (num14 <= num15 || num14 <= num16) { num2 = (num15 <= num16 ? (num14 + num15 - num16) / (2 * Math.Sqrt(num14 * num15)) : (num14 + num16 - num15) / (2 * Math.Sqrt(num14 * num16))); } else { num2 = (num15 + num16 - num14) / (2 * Math.Sqrt(num15 * num16)); } if (num1 > this.behavior.goodAngle || num2 < this.behavior.maxGoodAngle && this.behavior.MaxAngle != 0) { if (vertex.type == VertexType.SegmentVertex && vertex1.type == VertexType.SegmentVertex) { otri.SegPivot(ref osub); if (osub.seg == Mesh.dummysub) { otri.Copy(ref otri1); do { otri.OprevSelf(); otri.SegPivot(ref osub); }while (osub.seg == Mesh.dummysub); Vertex vertex5 = osub.SegOrg(); Vertex vertex6 = osub.SegDest(); do { otri1.DnextSelf(); otri1.SegPivot(ref osub); }while (osub.seg == Mesh.dummysub); Vertex vertex7 = osub.SegOrg(); Vertex vertex8 = osub.SegDest(); Vertex vertex9 = null; if (vertex6.x == vertex7.x && vertex6.y == vertex7.y) { vertex9 = vertex6; } else if (vertex5.x == vertex8.x && vertex5.y == vertex8.y) { vertex9 = vertex5; } if (vertex9 != null) { double num18 = (vertex.x - vertex9.x) * (vertex.x - vertex9.x) + (vertex.y - vertex9.y) * (vertex.y - vertex9.y); double num19 = (vertex1.x - vertex9.x) * (vertex1.x - vertex9.x) + (vertex1.y - vertex9.y) * (vertex1.y - vertex9.y); if (num18 < 1.001 * num19 && num18 > 0.999 * num19) { return; } } } } this.queue.Enqueue(ref testtri, num, vertex4, vertex2, vertex3); } }