/// <summary> /// Write the segments and holes to a .poly file. /// </summary> /// <param name="mesh">Data source.</param> /// <param name="filename">File name.</param> /// <param name="writeNodes">Write nodes into this file.</param> /// <remarks>If the nodes should not be written into this file, /// make sure a .node file was written before, so that the nodes /// are numbered right.</remarks> public void WritePoly(Mesh mesh, string filename, bool writeNodes) { Osub subseg = default(Osub); Vertex pt1, pt2; bool useBoundaryMarkers = mesh.behavior.UseBoundaryMarkers; using (var writer = new StreamWriter(filename)) { if (writeNodes) { // Write nodes to this file. WriteNodes(writer, mesh); } else { // The zero indicates that the vertices are in a separate .node file. // Followed by number of dimensions, number of vertex attributes, // and number of boundary markers (zero or one). writer.WriteLine("0 {0} {1} {2}", mesh.mesh_dim, 0, useBoundaryMarkers ? "1" : "0"); } // Number of segments, number of boundary markers (zero or one). writer.WriteLine("{0} {1}", mesh.subsegs.Count, useBoundaryMarkers ? "1" : "0"); subseg.orient = 0; int j = 0; foreach (var item in mesh.subsegs.Values) { subseg.seg = item; pt1 = subseg.Org(); pt2 = subseg.Dest(); // Segment number, indices of its two endpoints, and possibly a marker. if (useBoundaryMarkers) { writer.WriteLine("{0} {1} {2} {3}", j, pt1.id, pt2.id, subseg.seg.boundary); } else { writer.WriteLine("{0} {1} {2}", j, pt1.id, pt2.id); } j++; } // Holes j = 0; writer.WriteLine("{0}", mesh.holes.Count); foreach (var hole in mesh.holes) { writer.WriteLine("{0} {1} {2}", j++, hole.X.ToString(nfi), hole.Y.ToString(nfi)); } // Regions if (mesh.regions.Count > 0) { j = 0; writer.WriteLine("{0}", mesh.regions.Count); foreach (var region in mesh.regions) { writer.WriteLine("{0} {1} {2} {3}", j, region.point.X.ToString(nfi), region.point.Y.ToString(nfi), region.id); j++; } } } }
/// <summary> /// Construct Voronoi region for given vertex. /// </summary> /// <param name="region"></param> private void ConstructCell(VoronoiRegion region) { var vertex = region.Generator as Vertex; var vpoints = new List <Point>(); Otri f = default(Otri); Otri f_init = default(Otri); Otri f_next = default(Otri); Otri f_prev = default(Otri); Osub sub = default(Osub); // Call f_init a triangle incident to x vertex.tri.Copy(ref f_init); f_init.Copy(ref f); f_init.Onext(ref f_next); // Check if f_init lies on the boundary of the triangulation. if (f_next.tri.id == Mesh.DUMMY) { f_init.Oprev(ref f_prev); if (f_prev.tri.id != Mesh.DUMMY) { f_init.Copy(ref f_next); // Move one triangle clockwise f_init.Oprev(); f_init.Copy(ref f); } } // Go counterclockwise until we reach the border or the initial triangle. while (f_next.tri.id != Mesh.DUMMY) { // Add circumcenter of current triangle vpoints.Add(points[f.tri.id]); region.AddNeighbor(f.tri.id, regions[f.Apex().id]); if (f_next.Equals(f_init)) { // Voronoi cell is complete (bounded case). region.Add(vpoints); return; } f_next.Copy(ref f); f_next.Onext(); } // Voronoi cell is unbounded region.Bounded = false; Vertex torg, tdest, tapex; Point intersection; int sid, n = mesh.triangles.Count; // Find the boundary segment id (we use this id to number the endpoints of infinit rays). f.Lprev(ref f_next); f_next.Pivot(ref sub); sid = sub.seg.hash; // Last valid f lies at the boundary. Add the circumcenter. vpoints.Add(points[f.tri.id]); region.AddNeighbor(f.tri.id, regions[f.Apex().id]); // Check if the intersection with the bounding box has already been computed. if (!rayPoints.TryGetValue(sid, out intersection)) { torg = f.Org(); tapex = f.Apex(); intersection = IntersectionHelper.BoxRayIntersection(bounds, points[f.tri.id], torg.y - tapex.y, tapex.x - torg.x); // Set the correct id for the vertex intersection.id = n + rayIndex; points[n + rayIndex] = intersection; rayIndex++; rayPoints.Add(sid, intersection); } vpoints.Add(intersection); // Now walk from f_init clockwise till we reach the boundary. vpoints.Reverse(); f_init.Copy(ref f); f.Oprev(ref f_prev); while (f_prev.tri.id != Mesh.DUMMY) { vpoints.Add(points[f_prev.tri.id]); region.AddNeighbor(f_prev.tri.id, regions[f_prev.Apex().id]); f_prev.Copy(ref f); f_prev.Oprev(); } // Find the boundary segment id. f.Pivot(ref sub); sid = sub.seg.hash; if (!rayPoints.TryGetValue(sid, out intersection)) { // Intersection has not been computed yet. torg = f.Org(); tdest = f.Dest(); intersection = IntersectionHelper.BoxRayIntersection(bounds, points[f.tri.id], tdest.y - torg.y, torg.x - tdest.x); // Set the correct id for the vertex intersection.id = n + rayIndex; rayPoints.Add(sid, intersection); points[n + rayIndex] = intersection; rayIndex++; } vpoints.Add(intersection); region.AddNeighbor(intersection.id, regions[f.Dest().id]); // Add the new points to the region (in counter-clockwise order) vpoints.Reverse(); region.Add(vpoints); }
private void WriteMesh(Mesh mesh, bool skip) { Vertex vertex; Vertex vertex1; if (this.triangles == mesh.triangles.Count & skip) { return; } StreamWriter streamWriter = this.stream; int num = this.iteration; this.iteration = num + 1; streamWriter.WriteLine("#!M{0}", num); if (!this.VerticesChanged(mesh)) { this.stream.WriteLine("0"); } else { this.HashVertices(mesh); this.stream.WriteLine("{0}", mesh.vertices.Count); foreach (Vertex value in mesh.vertices.Values) { this.stream.WriteLine("{0} {1} {2} {3}", new object[] { value.hash, value.x.ToString(DebugWriter.nfi), value.y.ToString(DebugWriter.nfi), value.mark }); } } this.stream.WriteLine("{0}", mesh.subsegs.Count); Osub osub = new Osub() { orient = 0 }; foreach (Segment segment in mesh.subsegs.Values) { if (segment.hash <= 0) { continue; } osub.seg = segment; vertex = osub.Org(); vertex1 = osub.Dest(); this.stream.WriteLine("{0} {1} {2} {3}", new object[] { osub.seg.hash, vertex.hash, vertex1.hash, osub.seg.boundary }); } Otri otri = new Otri(); Otri otri1 = new Otri(); otri.orient = 0; this.stream.WriteLine("{0}", mesh.triangles.Count); foreach (Triangle triangle in mesh.triangles.Values) { otri.triangle = triangle; vertex = otri.Org(); vertex1 = otri.Dest(); Vertex vertex2 = otri.Apex(); int num1 = (vertex == null ? -1 : vertex.hash); int num2 = (vertex1 == null ? -1 : vertex1.hash); int num3 = (vertex2 == null ? -1 : vertex2.hash); this.stream.Write("{0} {1} {2} {3}", new object[] { otri.triangle.hash, num1, num2, num3 }); otri.orient = 1; otri.Sym(ref otri1); int num4 = otri1.triangle.hash; otri.orient = 2; otri.Sym(ref otri1); int num5 = otri1.triangle.hash; otri.orient = 0; otri.Sym(ref otri1); int num6 = otri1.triangle.hash; this.stream.WriteLine(" {0} {1} {2}", num4, num5, num6); } }
/// <summary> /// Inserts a vertex at the circumcenter of a triangle. Deletes /// the newly inserted vertex if it encroaches upon a segment. /// </summary> /// <param name="badtri"></param> private void SplitTriangle(BadTriangle badtri) { Otri badotri = default(Otri); Vertex borg, bdest, bapex; Point newloc; // Location of the new vertex double xi = 0, eta = 0; InsertVertexResult success; bool errorflag; badotri = badtri.poortri; borg = badotri.Org(); bdest = badotri.Dest(); bapex = badotri.Apex(); // Make sure that this triangle is still the same triangle it was // when it was tested and determined to be of bad quality. // Subsequent transformations may have made it a different triangle. if (!Otri.IsDead(badotri.tri) && (borg == badtri.org) && (bdest == badtri.dest) && (bapex == badtri.apex)) { errorflag = false; // Create a new vertex at the triangle's circumcenter. // Using the original (simpler) Steiner point location method // for mesh refinement. // TODO: NewLocation doesn't work for refinement. Why? Maybe // reset VertexType? if (behavior.fixedArea || behavior.VarArea) { newloc = predicates.FindCircumcenter(borg, bdest, bapex, ref xi, ref eta, behavior.offconstant); } else { newloc = newLocation.FindLocation(borg, bdest, bapex, ref xi, ref eta, true, badotri); } // Check whether the new vertex lies on a triangle vertex. if (((newloc.x == borg.x) && (newloc.y == borg.y)) || ((newloc.x == bdest.x) && (newloc.y == bdest.y)) || ((newloc.x == bapex.x) && (newloc.y == bapex.y))) { if (Log.Verbose) { logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()"); errorflag = true; } } else { // The new vertex must be in the interior, and therefore is a // free vertex with a marker of zero. Vertex newvertex = new Vertex(newloc.x, newloc.y, 0 #if USE_ATTRIBS , mesh.nextras #endif ); newvertex.type = VertexType.FreeVertex; // Ensure that the handle 'badotri' does not represent the longest // edge of the triangle. This ensures that the circumcenter must // fall to the left of this edge, so point location will work. // (If the angle org-apex-dest exceeds 90 degrees, then the // circumcenter lies outside the org-dest edge, and eta is // negative. Roundoff error might prevent eta from being // negative when it should be, so I test eta against xi.) if (eta < xi) { badotri.Lprev(); } // Assign triangle for attributes interpolation. newvertex.tri.tri = newvertex_tri; // Insert the circumcenter, searching from the edge of the triangle, // and maintain the Delaunay property of the triangulation. Osub tmp = default(Osub); success = mesh.InsertVertex(newvertex, ref badotri, ref tmp, true, true); if (success == InsertVertexResult.Successful) { newvertex.hash = mesh.hash_vtx++; newvertex.id = newvertex.hash; #if USE_ATTRIBS if (mesh.nextras > 0) { Interpolation.InterpolateAttributes(newvertex, newvertex.tri.tri, mesh.nextras); } #endif #if USE_Z Interpolation.InterpolateZ(newvertex, newvertex.tri.tri); #endif mesh.vertices.Add(newvertex.hash, newvertex); if (mesh.steinerleft > 0) { mesh.steinerleft--; } } else if (success == InsertVertexResult.Encroaching) { // If the newly inserted vertex encroaches upon a subsegment, // delete the new vertex. mesh.UndoVertex(); } else if (success == InsertVertexResult.Violating) { // Failed to insert the new vertex, but some subsegment was // marked as being encroached. } else { // success == DUPLICATEVERTEX // Couldn't insert the new vertex because a vertex is already there. if (Log.Verbose) { logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()"); errorflag = true; } } } if (errorflag) { logger.Error("The new vertex is at the circumcenter of triangle: This probably " + "means that I am trying to refine triangles to a smaller size than can be " + "accommodated by the finite precision of floating point arithmetic.", "Quality.SplitTriangle()"); throw new Exception("The new vertex is at the circumcenter of triangle."); } } }
private void SplitTriangle(BadTriangle badtri) { Point point; Otri otri = new Otri(); double num = 0; double num1 = 0; otri = badtri.poortri; Vertex vertex = otri.Org(); Vertex vertex1 = otri.Dest(); Vertex vertex2 = otri.Apex(); if (!Otri.IsDead(otri.triangle) && vertex == badtri.triangorg && vertex1 == badtri.triangdest && vertex2 == badtri.triangapex) { bool flag = false; point = (this.behavior.fixedArea || this.behavior.VarArea ? Primitives.FindCircumcenter(vertex, vertex1, vertex2, ref num, ref num1, this.behavior.offconstant) : this.newLocation.FindLocation(vertex, vertex1, vertex2, ref num, ref num1, true, otri)); if ((point.x != vertex.x || point.y != vertex.y) && (point.x != vertex1.x || point.y != vertex1.y) && (point.x != vertex2.x || point.y != vertex2.y)) { Vertex vertex3 = new Vertex(point.x, point.y, 0, this.mesh.nextras) { type = VertexType.FreeVertex }; for (int i = 0; i < this.mesh.nextras; i++) { vertex3.attributes[i] = vertex.attributes[i] + num * (vertex1.attributes[i] - vertex.attributes[i]) + num1 * (vertex2.attributes[i] - vertex.attributes[i]); } if (num1 < num) { otri.LprevSelf(); } Osub osub = new Osub(); InsertVertexResult insertVertexResult = this.mesh.InsertVertex(vertex3, ref otri, ref osub, true, true); if (insertVertexResult == InsertVertexResult.Successful) { 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); if (this.mesh.steinerleft > 0) { Mesh mesh1 = this.mesh; mesh1.steinerleft = mesh1.steinerleft - 1; } } else if (insertVertexResult == InsertVertexResult.Encroaching) { this.mesh.UndoVertex(); } else if (insertVertexResult != InsertVertexResult.Violating && Behavior.Verbose) { this.logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()"); flag = true; } } else if (Behavior.Verbose) { this.logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()"); flag = true; } if (flag) { this.logger.Error("The new vertex is at the circumcenter of triangle: This probably means that I am trying to refine triangles to a smaller size than can be accommodated by the finite precision of floating point arithmetic.", "Quality.SplitTriangle()"); throw new Exception("The new vertex is at the circumcenter of triangle."); } } }
/// <summary> /// Ensure that the mesh is (constrained) Delaunay. /// </summary> public bool CheckDelaunay() { Otri loop = default(Otri); Otri oppotri = default(Otri); Osub opposubseg = default(Osub); Vertex triorg, tridest, triapex; Vertex oppoapex; bool shouldbedelaunay; int horrors; bool saveexact; // Temporarily turn on exact arithmetic if it's off. saveexact = Behavior.NoExact; Behavior.NoExact = false; horrors = 0; // Run through the list of triangles, checking each one. foreach (var tri in mesh.triangles.Values) { loop.triangle = tri; // Check all three edges of the triangle. for (loop.orient = 0; loop.orient < 3; loop.orient++) { triorg = loop.Org(); tridest = loop.Dest(); triapex = loop.Apex(); loop.Sym(ref oppotri); oppoapex = oppotri.Apex(); // Only test that the edge is locally Delaunay if there is an // adjoining triangle whose pointer is larger (to ensure that // each pair isn't tested twice). shouldbedelaunay = (oppotri.triangle != Mesh.dummytri) && !Otri.IsDead(oppotri.triangle) && loop.triangle.id < oppotri.triangle.id && (triorg != mesh.infvertex1) && (triorg != mesh.infvertex2) && (triorg != mesh.infvertex3) && (tridest != mesh.infvertex1) && (tridest != mesh.infvertex2) && (tridest != mesh.infvertex3) && (triapex != mesh.infvertex1) && (triapex != mesh.infvertex2) && (triapex != mesh.infvertex3) && (oppoapex != mesh.infvertex1) && (oppoapex != mesh.infvertex2) && (oppoapex != mesh.infvertex3); if (mesh.checksegments && shouldbedelaunay) { // If a subsegment separates the triangles, then the edge is // constrained, so no local Delaunay test should be done. loop.SegPivot(ref opposubseg); if (opposubseg.seg != Mesh.dummysub) { shouldbedelaunay = false; } } if (shouldbedelaunay) { if (Primitives.NonRegular(triorg, tridest, triapex, oppoapex) > 0.0) { logger.Warning(String.Format("Non-regular pair of triangles found (IDs {0}/{1}).", loop.triangle.id, oppotri.triangle.id), "Quality.CheckDelaunay()"); horrors++; } } } } if (horrors == 0) // && Behavior.Verbose { logger.Info("Mesh is Delaunay."); } // Restore the status of exact arithmetic. Behavior.NoExact = saveexact; return(horrors == 0); }
/// <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 != null)) { // 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.tri.area) && (testtri.tri.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 != null) && behavior.UserTest(testtri.tri, 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.Pivot(ref testsub); if (testsub.seg.hash == Mesh.DUMMY) { // No common segment. Find a subsegment that contains 'torg'. tri1.Copy(ref tri2); do { tri1.Oprev(); tri1.Pivot(ref testsub); }while (testsub.seg.hash == Mesh.DUMMY); // Find the endpoints of the containing segment. org1 = testsub.SegOrg(); dest1 = testsub.SegDest(); // Find a subsegment that contains 'tdest'. do { tri2.Dnext(); tri2.Pivot(ref testsub); }while (testsub.seg.hash == Mesh.DUMMY); // 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); } }
/// <summary> /// Reconstruct a triangulation from its raw data representation. /// </summary> /// <param name="mesh"></param> /// <param name="input"></param> /// <returns></returns> /// <remarks> /// Reads an .ele file and reconstructs the original mesh. If the -p switch /// is used, this procedure will also read a .poly file and reconstruct the /// subsegments of the original mesh. If the -a switch is used, this /// procedure will also read an .area file and set a maximum area constraint /// on each triangle. /// /// Vertices that are not corners of triangles, such as nodes on edges of /// subparametric elements, are discarded. /// /// This routine finds the adjacencies between triangles (and subsegments) /// by forming one stack of triangles for each vertex. Each triangle is on /// three different stacks simultaneously. Each triangle's subsegment /// pointers are used to link the items in each stack. This memory-saving /// feature makes the code harder to read. The most important thing to keep /// in mind is that each triangle is removed from a stack precisely when /// the corresponding pointer is adjusted to refer to a subsegment rather /// than the next triangle of the stack. /// </remarks> public static int Reconstruct(Mesh mesh, InputGeometry input, ITriangle[] triangles) { int hullsize = 0; Otri tri = default(Otri); Otri triangleleft = default(Otri); Otri checktri = default(Otri); Otri checkleft = default(Otri); Otri checkneighbor = default(Otri); Osub subseg = default(Osub); List <Otri>[] vertexarray; // Triangle Otri prevlink; // Triangle Otri nexttri; // Triangle Vertex tdest, tapex; Vertex checkdest, checkapex; Vertex shorg; Vertex segmentorg, segmentdest; int[] corner = new int[3]; int[] end = new int[2]; //bool segmentmarkers = false; int boundmarker; int aroundvertex; bool notfound; int i = 0; int elements = triangles == null ? 0 : triangles.Length; int numberofsegments = input.segments.Count; mesh.inelements = elements; mesh.regions.AddRange(input.regions); // Create the triangles. for (i = 0; i < mesh.inelements; i++) { mesh.MakeTriangle(ref tri); // Mark the triangle as living. //tri.triangle.neighbors[0].triangle = tri.triangle; } if (mesh.behavior.Poly) { mesh.insegments = numberofsegments; // Create the subsegments. for (i = 0; i < mesh.insegments; i++) { mesh.MakeSegment(ref subseg); // Mark the subsegment as living. //subseg.ss.subsegs[0].ss = subseg.ss; } } // Allocate a temporary array that maps each vertex to some adjacent // triangle. I took care to allocate all the permanent memory for // triangles and subsegments first. vertexarray = new List <Otri> [mesh.vertices.Count]; // Each vertex is initially unrepresented. for (i = 0; i < mesh.vertices.Count; i++) { Otri tmp = default(Otri); tmp.triangle = Mesh.dummytri; vertexarray[i] = new List <Otri>(3); vertexarray[i].Add(tmp); } i = 0; // Read the triangles from the .ele file, and link // together those that share an edge. foreach (var item in mesh.triangles.Values) { tri.triangle = item; corner[0] = triangles[i].P0; corner[1] = triangles[i].P1; corner[2] = triangles[i].P2; // Copy the triangle's three corners. for (int j = 0; j < 3; j++) { if ((corner[j] < 0) || (corner[j] >= mesh.invertices)) { SimpleLog.Instance.Error("Triangle has an invalid vertex index.", "MeshReader.Reconstruct()"); throw new Exception("Triangle has an invalid vertex index."); } } // Read the triangle's attributes. tri.triangle.region = triangles[i].Region; // TODO: VarArea if (mesh.behavior.VarArea) { tri.triangle.area = triangles[i].Area; } // Set the triangle's vertices. tri.orient = 0; tri.SetOrg(mesh.vertices[corner[0]]); tri.SetDest(mesh.vertices[corner[1]]); tri.SetApex(mesh.vertices[corner[2]]); // Try linking the triangle to others that share these vertices. for (tri.orient = 0; tri.orient < 3; tri.orient++) { // Take the number for the origin of triangleloop. aroundvertex = corner[tri.orient]; int index = vertexarray[aroundvertex].Count - 1; // Look for other triangles having this vertex. nexttri = vertexarray[aroundvertex][index]; // Link the current triangle to the next one in the stack. //tri.triangle.neighbors[tri.orient] = nexttri; // Push the current triangle onto the stack. vertexarray[aroundvertex].Add(tri); checktri = nexttri; if (checktri.triangle != Mesh.dummytri) { tdest = tri.Dest(); tapex = tri.Apex(); // Look for other triangles that share an edge. do { checkdest = checktri.Dest(); checkapex = checktri.Apex(); if (tapex == checkdest) { // The two triangles share an edge; bond them together. tri.Lprev(ref triangleleft); triangleleft.Bond(ref checktri); } if (tdest == checkapex) { // The two triangles share an edge; bond them together. checktri.Lprev(ref checkleft); tri.Bond(ref checkleft); } // Find the next triangle in the stack. index--; nexttri = vertexarray[aroundvertex][index]; checktri = nexttri; } while (checktri.triangle != Mesh.dummytri); } } i++; } // Prepare to count the boundary edges. hullsize = 0; if (mesh.behavior.Poly) { // Read the segments from the .poly file, and link them // to their neighboring triangles. boundmarker = 0; i = 0; foreach (var item in mesh.subsegs.Values) { subseg.seg = item; end[0] = input.segments[i].P0; end[1] = input.segments[i].P1; boundmarker = input.segments[i].Boundary; for (int j = 0; j < 2; j++) { if ((end[j] < 0) || (end[j] >= mesh.invertices)) { SimpleLog.Instance.Error("Segment has an invalid vertex index.", "MeshReader.Reconstruct()"); throw new Exception("Segment has an invalid vertex index."); } } // set the subsegment's vertices. subseg.orient = 0; segmentorg = mesh.vertices[end[0]]; segmentdest = mesh.vertices[end[1]]; subseg.SetOrg(segmentorg); subseg.SetDest(segmentdest); subseg.SetSegOrg(segmentorg); subseg.SetSegDest(segmentdest); subseg.seg.boundary = boundmarker; // Try linking the subsegment to triangles that share these vertices. for (subseg.orient = 0; subseg.orient < 2; subseg.orient++) { // Take the number for the destination of subsegloop. aroundvertex = end[1 - subseg.orient]; int index = vertexarray[aroundvertex].Count - 1; // Look for triangles having this vertex. prevlink = vertexarray[aroundvertex][index]; nexttri = vertexarray[aroundvertex][index]; checktri = nexttri; shorg = subseg.Org(); notfound = true; // Look for triangles having this edge. Note that I'm only // comparing each triangle's destination with the subsegment; // each triangle's apex is handled through a different vertex. // Because each triangle appears on three vertices' lists, each // occurrence of a triangle on a list can (and does) represent // an edge. In this way, most edges are represented twice, and // every triangle-subsegment bond is represented once. while (notfound && (checktri.triangle != Mesh.dummytri)) { checkdest = checktri.Dest(); if (shorg == checkdest) { // We have a match. Remove this triangle from the list. //prevlink = vertexarray[aroundvertex][index]; vertexarray[aroundvertex].Remove(prevlink); // Bond the subsegment to the triangle. checktri.SegBond(ref subseg); // Check if this is a boundary edge. checktri.Sym(ref checkneighbor); if (checkneighbor.triangle == Mesh.dummytri) { // The next line doesn't insert a subsegment (because there's // already one there), but it sets the boundary markers of // the existing subsegment and its vertices. mesh.InsertSubseg(ref checktri, 1); hullsize++; } notfound = false; } index--; // Find the next triangle in the stack. prevlink = vertexarray[aroundvertex][index]; nexttri = vertexarray[aroundvertex][index]; checktri = nexttri; } } i++; } } // Mark the remaining edges as not being attached to any subsegment. // Also, count the (yet uncounted) boundary edges. for (i = 0; i < mesh.vertices.Count; i++) { // Search the stack of triangles adjacent to a vertex. int index = vertexarray[i].Count - 1; nexttri = vertexarray[i][index]; checktri = nexttri; while (checktri.triangle != Mesh.dummytri) { // Find the next triangle in the stack before this // information gets overwritten. index--; nexttri = vertexarray[i][index]; // No adjacent subsegment. (This overwrites the stack info.) checktri.SegDissolve(); checktri.Sym(ref checkneighbor); if (checkneighbor.triangle == Mesh.dummytri) { mesh.InsertSubseg(ref checktri, 1); hullsize++; } checktri = nexttri; } } return(hullsize); }
/// <summary> /// Inserts a vertex at the circumcenter of a triangle. Deletes /// the newly inserted vertex if it encroaches upon a segment. /// </summary> /// <param name="badtri"></param> private void SplitTriangle(BadTriangle badtri) { Otri badotri = default(Otri); Vertex borg, bdest, bapex; Point newloc; // Location of the new vertex double xi = 0, eta = 0; InsertVertexResult success; bool errorflag; badotri = badtri.poortri; borg = badotri.Org(); bdest = badotri.Dest(); bapex = badotri.Apex(); // Make sure that this triangle is still the same triangle it was // when it was tested and determined to be of bad quality. // Subsequent transformations may have made it a different triangle. if (!Otri.IsDead(badotri.tri) && (borg == badtri.org) && (bdest == badtri.dest) && (bapex == badtri.apex)) { errorflag = false; // Create a new vertex at the triangle's circumcenter. // Using the original (simpler) Steiner point location method // for mesh refinement. // TODO: NewLocation doesn't work for refinement. Why? Maybe // reset VertexType? newloc = RobustPredicates.FindCircumcenter(borg, bdest, bapex, ref xi, ref eta, behavior.offconstant); // Check whether the new vertex lies on a triangle vertex. if (((newloc.X == borg.X) && (newloc.Y == borg.Y)) || ((newloc.X == bdest.X) && (newloc.Y == bdest.Y)) || ((newloc.X == bapex.X) && (newloc.Y == bapex.Y))) { //errorflag = true; } else { // The new vertex must be in the interior, and therefore is a // free vertex with a marker of zero. Vertex newvertex = new Vertex(newloc.X, newloc.Y, 0); newvertex.type = VertexType.FreeVertex; // Ensure that the handle 'badotri' does not represent the longest // edge of the triangle. This ensures that the circumcenter must // fall to the left of this edge, so point location will work. // (If the angle org-apex-dest exceeds 90 degrees, then the // circumcenter lies outside the org-dest edge, and eta is // negative. Roundoff error might prevent eta from being // negative when it should be, so I test eta against xi.) if (eta < xi) { badotri.Lprev(); } // Insert the circumcenter, searching from the edge of the triangle, // and maintain the Delaunay property of the triangulation. Osub tmp = default(Osub); success = mesh.InsertVertex(newvertex, ref badotri, ref tmp, true, true); if (success == InsertVertexResult.Successful) { newvertex.Id = mesh.hash_vtx++; mesh.vertices.Add(newvertex.Id, newvertex); if (mesh.steinerleft > 0) { mesh.steinerleft--; } } else if (success == InsertVertexResult.Encroaching) { // If the newly inserted vertex encroaches upon a subsegment, // delete the new vertex. mesh.UndoVertex(); } else if (success == InsertVertexResult.Violating) { // Failed to insert the new vertex, but some subsegment was // marked as being encroached. } else { //errorflag = true; } } if (errorflag) { throw new Exception("The new vertex is at the circumcenter of triangle."); } } }
public LocateResult PreciseLocate(Point searchpoint, ref Otri searchtri, bool stopatsubsegment) { bool flag; Otri otri = new Otri(); Osub osub = new Osub(); Vertex vertex = searchtri.Org(); Vertex vertex1 = searchtri.Dest(); for (Vertex i = searchtri.Apex(); i.x != searchpoint.X || i.y != searchpoint.Y; i = searchtri.Apex()) { double num = Primitives.CounterClockwise(vertex, i, searchpoint); double num1 = Primitives.CounterClockwise(i, vertex1, searchpoint); if (num <= 0) { if (num1 <= 0) { if (num == 0) { searchtri.LprevSelf(); return(LocateResult.OnEdge); } if (num1 != 0) { return(LocateResult.InTriangle); } searchtri.LnextSelf(); return(LocateResult.OnEdge); } flag = false; } else { flag = (num1 <= 0 ? true : (i.x - searchpoint.X) * (vertex1.x - vertex.x) + (i.y - searchpoint.Y) * (vertex1.y - vertex.y) > 0); } if (!flag) { searchtri.Lnext(ref otri); vertex = i; } else { searchtri.Lprev(ref otri); vertex1 = i; } otri.Sym(ref searchtri); if (this.mesh.checksegments & stopatsubsegment) { otri.SegPivot(ref osub); if (osub.seg != Mesh.dummysub) { otri.Copy(ref searchtri); return(LocateResult.Outside); } } if (searchtri.triangle == Mesh.dummytri) { otri.Copy(ref searchtri); return(LocateResult.Outside); } } searchtri.LprevSelf(); return(LocateResult.OnVertex); }
/// <summary> /// Ensure that the mesh is (constrained) Delaunay. /// </summary> private static bool IsDelaunay(Mesh mesh, bool constrained) { Otri loop = default(Otri); Otri oppotri = default(Otri); Osub opposubseg = default(Osub); Vertex org, dest, apex; Vertex oppoapex; bool shouldbedelaunay; var logger = Log.Instance; // Temporarily turn on exact arithmetic if it's off. bool saveexact = Behavior.NoExact; Behavior.NoExact = false; int horrors = 0; var inf1 = mesh.infvertex1; var inf2 = mesh.infvertex2; var inf3 = mesh.infvertex3; // Run through the list of triangles, checking each one. foreach (var tri in mesh.triangles) { loop.tri = tri; // Check all three edges of the triangle. for (loop.orient = 0; loop.orient < 3; loop.orient++) { org = loop.Org(); dest = loop.Dest(); apex = loop.Apex(); loop.Sym(ref oppotri); oppoapex = oppotri.Apex(); // Only test that the edge is locally Delaunay if there is an // adjoining triangle whose pointer is larger (to ensure that // each pair isn't tested twice). shouldbedelaunay = (loop.tri.id < oppotri.tri.id) && !Otri.IsDead(oppotri.tri) && (oppotri.tri.id != Mesh.DUMMY) && (org != inf1) && (org != inf2) && (org != inf3) && (dest != inf1) && (dest != inf2) && (dest != inf3) && (apex != inf1) && (apex != inf2) && (apex != inf3) && (oppoapex != inf1) && (oppoapex != inf2) && (oppoapex != inf3); if (constrained && mesh.checksegments && shouldbedelaunay) { // If a subsegment separates the triangles, then the edge is // constrained, so no local Delaunay test should be done. loop.Pivot(ref opposubseg); if (opposubseg.seg.hash != Mesh.DUMMY) { shouldbedelaunay = false; } } if (shouldbedelaunay) { if (predicates.NonRegular(org, dest, apex, oppoapex) > 0.0) { if (Log.Verbose) { logger.Warning(String.Format("Non-regular pair of triangles found (IDs {0}/{1}).", loop.tri.id, oppotri.tri.id), "MeshValidator.IsDelaunay()"); } horrors++; } } } } // Restore the status of exact arithmetic. Behavior.NoExact = saveexact; return(horrors == 0); }
/// <summary> /// Virally infect all of the triangles of the convex hull that are not /// protected by subsegments. Where there are subsegments, set boundary /// markers as appropriate. /// </summary> private void InfectHull() { Otri hulltri = default(Otri); Otri nexttri = default(Otri); Otri starttri = default(Otri); Osub hullsubseg = default(Osub); Vertex horg, hdest; // Find a triangle handle on the hull. hulltri.triangle = Mesh.dummytri; hulltri.orient = 0; hulltri.SymSelf(); // Remember where we started so we know when to stop. hulltri.Copy(ref starttri); // Go once counterclockwise around the convex hull. do { // Ignore triangles that are already infected. if (!hulltri.IsInfected()) { // Is the triangle protected by a subsegment? hulltri.SegPivot(ref hullsubseg); if (hullsubseg.seg == Mesh.dummysub) { // The triangle is not protected; infect it. if (!hulltri.IsInfected()) { hulltri.Infect(); viri.Add(hulltri.triangle); } } else { // The triangle is protected; set boundary markers if appropriate. if (hullsubseg.seg.boundary == 0) { hullsubseg.seg.boundary = 1; horg = hulltri.Org(); hdest = hulltri.Dest(); if (horg.mark == 0) { horg.mark = 1; } if (hdest.mark == 0) { hdest.mark = 1; } } } } // To find the next hull edge, go clockwise around the next vertex. hulltri.LnextSelf(); hulltri.Oprev(ref nexttri); while (nexttri.triangle != Mesh.dummytri) { nexttri.Copy(ref hulltri); hulltri.Oprev(ref nexttri); } } while (!hulltri.Equal(starttri)); }
private void WriteMesh(Mesh mesh, bool skip) { // Mesh may have changed, but we choose to skip if (triangles == mesh.triangles.Count && skip) { return; } // Header line stream.WriteLine("#!M{0}", this.iteration++); Vertex p1, p2, p3; if (VerticesChanged(mesh)) { HashVertices(mesh); // Number of vertices. stream.WriteLine("{0}", mesh.vertices.Count); foreach (var v in mesh.vertices.Values) { // Vertex number, x and y coordinates and marker. stream.WriteLine("{0} {1} {2} {3}", v.id, v.x.ToString(nfi), v.y.ToString(nfi), v.label); } } else { stream.WriteLine("0"); } // Number of segments. stream.WriteLine("{0}", mesh.subsegs.Count); Osub subseg = default(Osub); subseg.orient = 0; foreach (var item in mesh.subsegs.Values) { if (item.hash <= 0) { continue; } subseg.seg = item; p1 = subseg.Org(); p2 = subseg.Dest(); // Segment number, indices of its two endpoints, and marker. stream.WriteLine("{0} {1} {2} {3}", subseg.seg.hash, p1.id, p2.id, subseg.seg.boundary); } Otri tri = default(Otri), trisym = default(Otri); tri.orient = 0; int n1, n2, n3, h1, h2, h3; // Number of triangles. stream.WriteLine("{0}", mesh.triangles.Count); foreach (var item in mesh.triangles) { tri.tri = item; p1 = tri.Org(); p2 = tri.Dest(); p3 = tri.Apex(); h1 = (p1 == null) ? -1 : p1.id; h2 = (p2 == null) ? -1 : p2.id; h3 = (p3 == null) ? -1 : p3.id; // Triangle number, indices for three vertices. stream.Write("{0} {1} {2} {3}", tri.tri.hash, h1, h2, h3); tri.orient = 1; tri.Sym(ref trisym); n1 = trisym.tri.hash; tri.orient = 2; tri.Sym(ref trisym); n2 = trisym.tri.hash; tri.orient = 0; tri.Sym(ref trisym); n3 = trisym.tri.hash; // Neighboring triangle numbers. stream.WriteLine(" {0} {1} {2}", n1, n2, n3); } }
private void Plague() { Otri item = new Otri(); Otri otri = new Otri(); Osub osub = new Osub(); for (int i = 0; i < this.viri.Count; i++) { item.triangle = this.viri[i]; item.Uninfect(); item.orient = 0; while (item.orient < 3) { item.Sym(ref otri); item.SegPivot(ref osub); if (otri.triangle != Mesh.dummytri && !otri.IsInfected()) { if (osub.seg != Mesh.dummysub) { osub.TriDissolve(); if (osub.seg.boundary == 0) { osub.seg.boundary = 1; } Vertex vertex = otri.Org(); Vertex vertex1 = otri.Dest(); if (vertex.mark == 0) { vertex.mark = 1; } if (vertex1.mark == 0) { vertex1.mark = 1; } } else { otri.Infect(); this.viri.Add(otri.triangle); } } else if (osub.seg != Mesh.dummysub) { this.mesh.SubsegDealloc(osub.seg); if (otri.triangle != Mesh.dummytri) { otri.Uninfect(); otri.SegDissolve(); otri.Infect(); } } item.orient = item.orient + 1; } item.Infect(); } foreach (Triangle virus in this.viri) { item.triangle = virus; item.orient = 0; while (item.orient < 3) { Vertex vertex2 = item.Org(); if (vertex2 != null) { bool flag = true; item.SetOrg(null); item.Onext(ref otri); while (otri.triangle != Mesh.dummytri && !otri.Equal(item)) { if (!otri.IsInfected()) { flag = false; } else { otri.SetOrg(null); } otri.OnextSelf(); } if (otri.triangle == Mesh.dummytri) { item.Oprev(ref otri); while (otri.triangle != Mesh.dummytri) { if (!otri.IsInfected()) { flag = false; } else { otri.SetOrg(null); } otri.OprevSelf(); } } if (flag) { vertex2.type = VertexType.UndeadVertex; Mesh mesh = this.mesh; mesh.undeads = mesh.undeads + 1; } } item.orient = item.orient + 1; } item.orient = 0; while (item.orient < 3) { item.Sym(ref otri); if (otri.triangle != Mesh.dummytri) { otri.Dissolve(); Mesh mesh1 = this.mesh; mesh1.hullsize = mesh1.hullsize + 1; } else { Mesh mesh2 = this.mesh; mesh2.hullsize = mesh2.hullsize - 1; } item.orient = item.orient + 1; } this.mesh.TriangleDealloc(item.triangle); } this.viri.Clear(); }
/// <summary> /// Write the edges to an .edge file. /// </summary> /// <param name="mesh"></param> /// <param name="filename"></param> public void WriteEdges(Mesh mesh, string filename) { Otri tri = default(Otri), trisym = default(Otri); Osub checkmark = default(Osub); Vertex p1, p2; Behavior behavior = mesh.behavior; using (var writer = new StreamWriter(filename)) { // Number of edges, number of boundary markers (zero or one). writer.WriteLine("{0} {1}", mesh.NumberOfEdges, behavior.UseBoundaryMarkers ? "1" : "0"); long index = 0; // To loop over the set of edges, loop over all triangles, and look at // the three edges of each triangle. If there isn't another triangle // adjacent to the edge, operate on the edge. If there is another // adjacent triangle, operate on the edge only if the current triangle // has a smaller pointer than its neighbor. This way, each edge is // considered only once. foreach (var item in mesh.triangles) { tri.tri = item; for (tri.orient = 0; tri.orient < 3; tri.orient++) { tri.Sym(ref trisym); if ((tri.tri.id < trisym.tri.id) || (trisym.tri.id == Mesh.DUMMY)) { p1 = tri.Org(); p2 = tri.Dest(); if (behavior.UseBoundaryMarkers) { // Edge number, indices of two endpoints, and a boundary marker. // If there's no subsegment, the boundary marker is zero. if (behavior.useSegments) { tri.Pivot(ref checkmark); if (checkmark.seg.hash == Mesh.DUMMY) { writer.WriteLine("{0} {1} {2} {3}", index, p1.id, p2.id, 0); } else { writer.WriteLine("{0} {1} {2} {3}", index, p1.id, p2.id, checkmark.seg.boundary); } } else { writer.WriteLine("{0} {1} {2} {3}", index, p1.id, p2.id, trisym.tri.id == Mesh.DUMMY ? "1" : "0"); } } else { // Edge number, indices of two endpoints. writer.WriteLine("{0} {1} {2}", index, p1.id, p2.id); } index++; } } } } }
/// <summary> /// Construct Voronoi region for given vertex. /// </summary> /// <param name="vertex"></param> /// <returns>The circumcenter indices which make up the cell.</returns> private void ConstructVoronoiRegion(Vertex vertex) { VoronoiRegion region = new VoronoiRegion(vertex); regions.Add(region); List <Point> vpoints = new List <Point>(); Otri f = default(Otri); Otri f_init = default(Otri); Otri f_next = default(Otri); Otri f_prev = default(Otri); Osub sub = default(Osub); // Call f_init a triangle incident to x vertex.tri.Copy(ref f_init); f_init.Copy(ref f); f_init.Onext(ref f_next); // Check if f_init lies on the boundary of the triangulation. if (f_next.triangle == Mesh.dummytri) { f_init.Oprev(ref f_prev); if (f_prev.triangle != Mesh.dummytri) { f_init.Copy(ref f_next); // Move one triangle clockwise f_init.OprevSelf(); f_init.Copy(ref f); } } // Go counterclockwise until we reach the border or the initial triangle. while (f_next.triangle != Mesh.dummytri) { // Add circumcenter of current triangle vpoints.Add(points[f.triangle.id]); if (f_next.Equal(f_init)) { // Voronoi cell is complete (bounded case). region.Add(vpoints); return; } f_next.Copy(ref f); f_next.OnextSelf(); } // Voronoi cell is unbounded region.Bounded = false; Vertex torg, tdest, tapex, intersection; int sid, n = mesh.triangles.Count; // Find the boundary segment id. f.Lprev(ref f_next); f_next.SegPivot(ref sub); sid = sub.seg.hash; // Last valid f lies at the boundary. Add the circumcenter. vpoints.Add(points[f.triangle.id]); // Check if the intersection with the bounding box has already been computed. if (rayPoints.ContainsKey(sid)) { vpoints.Add(rayPoints[sid]); } else { torg = f.Org(); tapex = f.Apex(); BoxRayIntersection(points[f.triangle.id], torg.y - tapex.y, tapex.x - torg.x, out intersection); // Set the correct id for the vertex intersection.id = n + rayIndex; points[n + rayIndex] = intersection; rayIndex++; vpoints.Add(intersection); rayPoints.Add(sid, intersection); } // Now walk from f_init clockwise till we reach the boundary. vpoints.Reverse(); f_init.Copy(ref f); f.Oprev(ref f_prev); while (f_prev.triangle != Mesh.dummytri) { vpoints.Add(points[f_prev.triangle.id]); f_prev.Copy(ref f); f_prev.OprevSelf(); } // Find the boundary segment id. f.SegPivot(ref sub); sid = sub.seg.hash; if (rayPoints.ContainsKey(sid)) { vpoints.Add(rayPoints[sid]); } else { // Intersection has not been computed yet. torg = f.Org(); tdest = f.Dest(); BoxRayIntersection(points[f.triangle.id], tdest.y - torg.y, torg.x - tdest.x, out intersection); // Set the correct id for the vertex intersection.id = n + rayIndex; points[n + rayIndex] = intersection; rayIndex++; vpoints.Add(intersection); rayPoints.Add(sid, intersection); } // Add the new points to the region (in counter-clockwise order) vpoints.Reverse(); region.Add(vpoints); }
public static void WritePoly(Mesh mesh, string filename, bool writeNodes) { double x; Osub osub = new Osub(); bool useBoundaryMarkers = mesh.behavior.UseBoundaryMarkers; using (StreamWriter streamWriter = new StreamWriter(new FileStream(filename, FileMode.Create))) { if (!writeNodes) { streamWriter.WriteLine("0 {0} {1} {2}", mesh.mesh_dim, mesh.nextras, (useBoundaryMarkers ? "1" : "0")); } else { FileWriter.WriteNodes(streamWriter, mesh); } streamWriter.WriteLine("{0} {1}", mesh.subsegs.Count, (useBoundaryMarkers ? "1" : "0")); osub.orient = 0; int num = 0; foreach (Segment value in mesh.subsegs.Values) { osub.seg = value; Vertex vertex = osub.Org(); Vertex vertex1 = osub.Dest(); if (!useBoundaryMarkers) { streamWriter.WriteLine("{0} {1} {2}", num, vertex.id, vertex1.id); } else { streamWriter.WriteLine("{0} {1} {2} {3}", new object[] { num, vertex.id, vertex1.id, osub.seg.boundary }); } num++; } num = 0; streamWriter.WriteLine("{0}", mesh.holes.Count); foreach (Point hole in mesh.holes) { int num1 = num; num = num1 + 1; object obj = num1; x = hole.X; string str = x.ToString(FileWriter.nfi); x = hole.Y; streamWriter.WriteLine("{0} {1} {2}", obj, str, x.ToString(FileWriter.nfi)); } if (mesh.regions.Count > 0) { num = 0; streamWriter.WriteLine("{0}", mesh.regions.Count); foreach (RegionPointer region in mesh.regions) { object[] objArray = new object[] { num, null, null, null }; x = region.point.X; objArray[1] = x.ToString(FileWriter.nfi); x = region.point.Y; objArray[2] = x.ToString(FileWriter.nfi); objArray[3] = region.id; streamWriter.WriteLine("{0} {1} {2} {3}", objArray); num++; } } } }
/// <summary> /// Tag all blind triangles. /// </summary> /// <remarks> /// A triangle is said to be blind if the triangle and its circumcenter /// lie on two different sides of a constrained edge. /// </remarks> private void TagBlindTriangles() { int blinded = 0; Stack <Triangle> triangles; subsegMap = new Dictionary <int, Segment>(); Otri f = default(Otri); Otri f0 = default(Otri); Osub e = default(Osub); Osub sub1 = default(Osub); // Tag all triangles non-blind foreach (var t in mesh.triangles.Values) { // Use the infected flag for 'blinded' attribute. t.infected = false; } // for each constrained edge e of cdt do foreach (var ss in mesh.subsegs.Values) { // Create a stack: triangles triangles = new Stack <Triangle>(); // for both adjacent triangles fe to e tagged non-blind do // Push fe into triangles e.seg = ss; e.orient = 0; e.TriPivot(ref f); if (f.triangle != Mesh.dummytri && !f.triangle.infected) { triangles.Push(f.triangle); } e.SymSelf(); e.TriPivot(ref f); if (f.triangle != Mesh.dummytri && !f.triangle.infected) { triangles.Push(f.triangle); } // while triangles is non-empty while (triangles.Count > 0) { // Pop f from stack triangles f.triangle = triangles.Pop(); f.orient = 0; // if f is blinded by e (use P) then if (TriangleIsBlinded(ref f, ref e)) { // Tag f as blinded by e f.triangle.infected = true; blinded++; // Store association triangle -> subseg subsegMap.Add(f.triangle.hash, e.seg); // for each adjacent triangle f0 to f do for (f.orient = 0; f.orient < 3; f.orient++) { f.Sym(ref f0); f0.SegPivot(ref sub1); // if f0 is finite and tagged non-blind & the common edge // between f and f0 is unconstrained then if (f0.triangle != Mesh.dummytri && !f0.triangle.infected && sub1.seg == Mesh.dummysub) { // Push f0 into triangles. triangles.Push(f0.triangle); } } } } } blinded = 0; }
/// <summary> /// Check a subsegment to see if it is encroached; add it to the list if it is. /// </summary> /// <param name="testsubseg">The subsegment to check.</param> /// <returns>Returns a nonzero value if the subsegment is encroached.</returns> /// <remarks> /// A subsegment is encroached if there is a vertex in its diametral lens. /// For Ruppert's algorithm (-D switch), the "diametral lens" is the /// diametral circle. For Chew's algorithm (default), the diametral lens is /// just big enough to enclose two isosceles triangles whose bases are the /// subsegment. Each of the two isosceles triangles has two angles equal /// to 'b.minangle'. /// /// Chew's algorithm does not require diametral lenses at all--but they save /// time. Any vertex inside a subsegment's diametral lens implies that the /// triangle adjoining the subsegment will be too skinny, so it's only a /// matter of time before the encroaching vertex is deleted by Chew's /// algorithm. It's faster to simply not insert the doomed vertex in the /// first place, which is why I use diametral lenses with Chew's algorithm. /// </remarks> public int CheckSeg4Encroach(ref Osub testsubseg) { Otri neighbortri = default(Otri); Osub testsym = default(Osub); BadSubseg encroachedseg; double dotproduct; int encroached; int sides; Vertex eorg, edest, eapex; encroached = 0; sides = 0; eorg = testsubseg.Org(); edest = testsubseg.Dest(); // Check one neighbor of the subsegment. testsubseg.Pivot(ref neighbortri); // Does the neighbor exist, or is this a boundary edge? if (neighbortri.tri.id != Mesh.DUMMY) { sides++; // Find a vertex opposite this subsegment. eapex = neighbortri.Apex(); // Check whether the apex is in the diametral lens of the subsegment // (the diametral circle if 'conformdel' is set). A dot product // of two sides of the triangle is used to check whether the angle // at the apex is greater than (180 - 2 'minangle') degrees (for // lenses; 90 degrees for diametral circles). dotproduct = (eorg.x - eapex.x) * (edest.x - eapex.x) + (eorg.y - eapex.y) * (edest.y - eapex.y); if (dotproduct < 0.0) { if (behavior.ConformingDelaunay || (dotproduct * dotproduct >= (2.0 * behavior.goodAngle - 1.0) * (2.0 * behavior.goodAngle - 1.0) * ((eorg.x - eapex.x) * (eorg.x - eapex.x) + (eorg.y - eapex.y) * (eorg.y - eapex.y)) * ((edest.x - eapex.x) * (edest.x - eapex.x) + (edest.y - eapex.y) * (edest.y - eapex.y)))) { encroached = 1; } } } // Check the other neighbor of the subsegment. testsubseg.Sym(ref testsym); testsym.Pivot(ref neighbortri); // Does the neighbor exist, or is this a boundary edge? if (neighbortri.tri.id != Mesh.DUMMY) { sides++; // Find the other vertex opposite this subsegment. eapex = neighbortri.Apex(); // Check whether the apex is in the diametral lens of the subsegment // (or the diametral circle, if 'conformdel' is set). dotproduct = (eorg.x - eapex.x) * (edest.x - eapex.x) + (eorg.y - eapex.y) * (edest.y - eapex.y); if (dotproduct < 0.0) { if (behavior.ConformingDelaunay || (dotproduct * dotproduct >= (2.0 * behavior.goodAngle - 1.0) * (2.0 * behavior.goodAngle - 1.0) * ((eorg.x - eapex.x) * (eorg.x - eapex.x) + (eorg.y - eapex.y) * (eorg.y - eapex.y)) * ((edest.x - eapex.x) * (edest.x - eapex.x) + (edest.y - eapex.y) * (edest.y - eapex.y)))) { encroached += 2; } } } if (encroached > 0 && (behavior.NoBisect == 0 || ((behavior.NoBisect == 1) && (sides == 2)))) { // Add the subsegment to the list of encroached subsegments. // Be sure to get the orientation right. encroachedseg = new BadSubseg(); if (encroached == 1) { encroachedseg.subseg = testsubseg; encroachedseg.org = eorg; encroachedseg.dest = edest; } else { encroachedseg.subseg = testsym; encroachedseg.org = edest; encroachedseg.dest = eorg; } badsubsegs.Enqueue(encroachedseg); } return(encroached); }
private void ConstructBvdCell(Vertex vertex) { VoronoiRegion region = new VoronoiRegion(vertex); regions.Add(region); Otri f = default(Otri); Otri f_init = default(Otri); Otri f_next = default(Otri); Osub sf = default(Osub); Osub sfn = default(Osub); Point cc_f, cc_f_next, p; int n = mesh.triangles.Count; // Call P the polygon (cell) in construction List <Point> vpoints = new List <Point>(); // Call f_init a triangle incident to x vertex.tri.Copy(ref f_init); if (f_init.Org() != vertex) { throw new Exception("ConstructBvdCell: inconsistent topology."); } // Let f be initialized to f_init f_init.Copy(ref f); // Call f_next the next triangle counterclockwise around x f_init.Onext(ref f_next); // repeat ... until f = f_init do { // Call Lffnext the line going through the circumcenters of f and f_next cc_f = this.points[f.triangle.id]; cc_f_next = this.points[f_next.triangle.id]; // if f is tagged non-blind then if (!f.triangle.infected) { // Insert the circumcenter of f into P vpoints.Add(cc_f); if (f_next.triangle.infected) { // Call S_fnext the constrained edge blinding f_next sfn.seg = subsegMap[f_next.triangle.hash]; // Insert point Lf,f_next /\ Sf_next into P if (SegmentsIntersect(sfn.SegOrg(), sfn.SegDest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); } } } else { // Call Sf the constrained edge blinding f sf.seg = subsegMap[f.triangle.hash]; // if f_next is tagged non-blind then if (!f_next.triangle.infected) { // Insert point Lf,f_next /\ Sf into P if (SegmentsIntersect(sf.SegOrg(), sf.SegDest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); } } else { // Call Sf_next the constrained edge blinding f_next sfn.seg = subsegMap[f_next.triangle.hash]; // if Sf != Sf_next then if (!sf.Equal(sfn)) { // Insert Lf,fnext /\ Sf and Lf,fnext /\ Sfnext into P if (SegmentsIntersect(sf.SegOrg(), sf.SegDest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); } if (SegmentsIntersect(sfn.SegOrg(), sfn.SegDest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); } } } } // f <- f_next f_next.Copy(ref f); // Call f_next the next triangle counterclockwise around x f_next.OnextSelf(); } while (!f.Equal(f_init)); // Output: Bounded Voronoi cell of x in counterclockwise order. region.Add(vpoints); }
/// <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; 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.subseg; 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.org) && (edest == seg.dest)) { // 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.Pivot(ref enctri); enctri.Lnext(ref testtri); testtri.Pivot(ref testsh); acuteorg = testsh.seg.hash != Mesh.DUMMY; // Is the destination shared with another segment? testtri.Lnext(); testtri.Pivot(ref testsh); acutedest = testsh.seg.hash != Mesh.DUMMY; // 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.Pivot(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.tri.id != Mesh.DUMMY) { // Is the destination shared with another segment? testtri.Lnext(); testtri.Pivot(ref testsh); acutedest2 = testsh.seg.hash != Mesh.DUMMY; acutedest = acutedest || acutedest2; // Is the origin shared with another segment? testtri.Lnext(); testtri.Pivot(ref testsh); acuteorg2 = testsh.seg.hash != Mesh.DUMMY; 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.Lprev(); } } } // 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.seg.boundary #if USE_ATTRIBS , mesh.nextras #endif ); newvertex.type = VertexType.SegmentVertex; newvertex.hash = mesh.hash_vtx++; newvertex.id = newvertex.hash; mesh.vertices.Add(newvertex.hash, newvertex); #if USE_ATTRIBS // Interpolate attributes. for (int i = 0; i < mesh.nextras; i++) { newvertex.attributes[i] = eorg.attributes[i] + split * (edest.attributes[i] - eorg.attributes[i]); } #endif #if USE_Z newvertex.z = eorg.z + split * (edest.z - eorg.z); #endif 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 = predicates.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.Next(); 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.org = null; } }
private void ConstructBoundaryBvdCell(Vertex vertex) { VoronoiRegion region = new VoronoiRegion(vertex); regions.Add(region); Otri f = default(Otri); Otri f_init = default(Otri); Otri f_next = default(Otri); Otri f_prev = default(Otri); Osub sf = default(Osub); Osub sfn = default(Osub); Vertex torg, tdest, tapex, sorg, sdest; Point cc_f, cc_f_next, p; int n = mesh.triangles.Count; // Call P the polygon (cell) in construction List <Point> vpoints = new List <Point>(); // Call f_init a triangle incident to x vertex.tri.Copy(ref f_init); if (f_init.Org() != vertex) { throw new Exception("ConstructBoundaryBvdCell: inconsistent topology."); } // Let f be initialized to f_init f_init.Copy(ref f); // Call f_next the next triangle counterclockwise around x f_init.Onext(ref f_next); f_init.Oprev(ref f_prev); // Is the border to the left? if (f_prev.triangle != Mesh.dummytri) { // Go clockwise until we reach the border (or the initial triangle) while (f_prev.triangle != Mesh.dummytri && !f_prev.Equal(f_init)) { f_prev.Copy(ref f); f_prev.OprevSelf(); } f.Copy(ref f_init); f.Onext(ref f_next); } if (f_prev.triangle == Mesh.dummytri) { // For vertices on the domain boundaray, add the vertex. For // internal boundaries don't add it. p = new Point(vertex.x, vertex.y); p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); } // Add midpoint of start triangles' edge. torg = f.Org(); tdest = f.Dest(); p = new Point((torg.X + tdest.X) / 2, (torg.Y + tdest.Y) / 2); p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); // repeat ... until f = f_init do { // Call Lffnext the line going through the circumcenters of f and f_next cc_f = this.points[f.triangle.id]; if (f_next.triangle == Mesh.dummytri) { if (!f.triangle.infected) { // Add last circumcenter vpoints.Add(cc_f); } // Add midpoint of last triangles' edge (chances are it has already // been added, so post process cell to remove duplicates???) torg = f.Org(); tapex = f.Apex(); p = new Point((torg.X + tapex.X) / 2, (torg.Y + tapex.Y) / 2); p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); break; } cc_f_next = this.points[f_next.triangle.id]; // if f is tagged non-blind then if (!f.triangle.infected) { // Insert the circumcenter of f into P vpoints.Add(cc_f); if (f_next.triangle.infected) { // Call S_fnext the constrained edge blinding f_next sfn.seg = subsegMap[f_next.triangle.hash]; // Insert point Lf,f_next /\ Sf_next into P if (SegmentsIntersect(sfn.SegOrg(), sfn.SegDest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); } } } else { // Call Sf the constrained edge blinding f sf.seg = subsegMap[f.triangle.hash]; sorg = sf.SegOrg(); sdest = sf.SegDest(); // if f_next is tagged non-blind then if (!f_next.triangle.infected) { tdest = f.Dest(); tapex = f.Apex(); // Both circumcenters lie on the blinded side, but we // have to add the intersection with the segment. // Center of f edge dest->apex Point bisec = new Point((tdest.X + tapex.X) / 2, (tdest.Y + tapex.Y) / 2); // Find intersection of seg with line through f's bisector and circumcenter if (SegmentsIntersect(sorg, sdest, bisec, cc_f, out p, false)) { p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); } // Insert point Lf,f_next /\ Sf into P if (SegmentsIntersect(sorg, sdest, cc_f, cc_f_next, out p, true)) { p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); } } else { // Call Sf_next the constrained edge blinding f_next sfn.seg = subsegMap[f_next.triangle.hash]; // if Sf != Sf_next then if (!sf.Equal(sfn)) { // Insert Lf,fnext /\ Sf and Lf,fnext /\ Sfnext into P if (SegmentsIntersect(sorg, sdest, cc_f, cc_f_next, out p, true)) { p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); } if (SegmentsIntersect(sfn.SegOrg(), sfn.SegDest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); } } else { // Both circumcenters lie on the blinded side, but we // have to add the intersection with the segment. // Center of f_next edge org->dest Point bisec = new Point((torg.X + tdest.X) / 2, (torg.Y + tdest.Y) / 2); // Find intersection of seg with line through f_next's bisector and circumcenter if (SegmentsIntersect(sorg, sdest, bisec, cc_f_next, out p, false)) { p.id = n + segIndex; points[n + segIndex] = p; segIndex++; vpoints.Add(p); } } } } // f <- f_next f_next.Copy(ref f); // Call f_next the next triangle counterclockwise around x f_next.OnextSelf(); } while (!f.Equal(f_init)); // Output: Bounded Voronoi cell of x in counterclockwise order. region.Add(vpoints); }
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; } }
/// <summary> /// Finds the adjacencies between triangles and subsegments. /// </summary> private static void SetSegments(Mesh mesh, Polygon polygon, List <Otri>[] vertexarray) { Otri checktri = default(Otri); Otri nexttri; // Triangle TVertex checkdest; Otri checkneighbor = default(Otri); Osub subseg = default(Osub); Otri prevlink; // Triangle TVertex tmp; TVertex sorg, sdest; bool notfound; //bool segmentmarkers = false; int boundmarker; int aroundvertex; int i; int hullsize = 0; // Prepare to count the boundary edges. if (mesh.behavior.Poly) { // Link the segments to their neighboring triangles. boundmarker = 0; i = 0; foreach (var item in mesh.subsegs.Values) { subseg.seg = item; sorg = polygon.Segments[i].GetVertex(0); sdest = polygon.Segments[i].GetVertex(1); boundmarker = polygon.Segments[i].Label; if ((sorg.id < 0 || sorg.id >= mesh.invertices) || (sdest.id < 0 || sdest.id >= mesh.invertices)) { Log.Instance.Error("Segment has an invalid vertex index.", "MeshReader.Reconstruct()"); throw new Exception("Segment has an invalid vertex index."); } // set the subsegment's vertices. subseg.orient = 0; subseg.SetOrg(sorg); subseg.SetDest(sdest); subseg.SetSegOrg(sorg); subseg.SetSegDest(sdest); subseg.seg.boundary = boundmarker; // Try linking the subsegment to triangles that share these vertices. for (subseg.orient = 0; subseg.orient < 2; subseg.orient++) { // Take the number for the destination of subsegloop. aroundvertex = subseg.orient == 1 ? sorg.id : sdest.id; int index = vertexarray[aroundvertex].Count - 1; // Look for triangles having this vertex. prevlink = vertexarray[aroundvertex][index]; nexttri = vertexarray[aroundvertex][index]; checktri = nexttri; tmp = subseg.Org(); notfound = true; // Look for triangles having this edge. Note that I'm only // comparing each triangle's destination with the subsegment; // each triangle's apex is handled through a different vertex. // Because each triangle appears on three vertices' lists, each // occurrence of a triangle on a list can (and does) represent // an edge. In this way, most edges are represented twice, and // every triangle-subsegment bond is represented once. while (notfound && (checktri.tri.id != Mesh.DUMMY)) { checkdest = checktri.Dest(); if (tmp == checkdest) { // We have a match. Remove this triangle from the list. //prevlink = vertexarray[aroundvertex][index]; vertexarray[aroundvertex].Remove(prevlink); // Bond the subsegment to the triangle. checktri.SegBond(ref subseg); // Check if this is a boundary edge. checktri.Sym(ref checkneighbor); if (checkneighbor.tri.id == Mesh.DUMMY) { // The next line doesn't insert a subsegment (because there's // already one there), but it sets the boundary markers of // the existing subsegment and its vertices. mesh.InsertSubseg(ref checktri, 1); hullsize++; } notfound = false; } index--; // Find the next triangle in the stack. prevlink = vertexarray[aroundvertex][index]; nexttri = vertexarray[aroundvertex][index]; checktri = nexttri; } } i++; } } // Mark the remaining edges as not being attached to any subsegment. // Also, count the (yet uncounted) boundary edges. for (i = 0; i < mesh.vertices.Count; i++) { // Search the stack of triangles adjacent to a vertex. int index = vertexarray[i].Count - 1; nexttri = vertexarray[i][index]; checktri = nexttri; while (checktri.tri.id != Mesh.DUMMY) { // Find the next triangle in the stack before this // information gets overwritten. index--; nexttri = vertexarray[i][index]; // No adjacent subsegment. (This overwrites the stack info.) checktri.SegDissolve(mesh.dummysub); checktri.Sym(ref checkneighbor); if (checkneighbor.tri.id == Mesh.DUMMY) { mesh.InsertSubseg(ref checktri, 1); hullsize++; } checktri = nexttri; } } mesh.hullsize = hullsize; }
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); } }
/// <summary> /// Find a triangle or edge containing a given point. /// </summary> /// <param name="searchpoint">The point to locate.</param> /// <param name="searchtri">The triangle to start the search at.</param> /// <param name="stopatsubsegment"> If 'stopatsubsegment' is set, the search /// will stop if it tries to walk through a subsegment, and will return OUTSIDE.</param> /// <returns>Location information.</returns> /// <remarks> /// Begins its search from 'searchtri'. It is important that 'searchtri' /// be a handle with the property that 'searchpoint' is strictly to the left /// of the edge denoted by 'searchtri', or is collinear with that edge and /// does not intersect that edge. (In particular, 'searchpoint' should not /// be the origin or destination of that edge.) /// /// These conditions are imposed because preciselocate() is normally used in /// one of two situations: /// /// (1) To try to find the location to insert a new point. Normally, we /// know an edge that the point is strictly to the left of. In the /// incremental Delaunay algorithm, that edge is a bounding box edge. /// In Ruppert's Delaunay refinement algorithm for quality meshing, /// that edge is the shortest edge of the triangle whose circumcenter /// is being inserted. /// /// (2) To try to find an existing point. In this case, any edge on the /// convex hull is a good starting edge. You must screen out the /// possibility that the vertex sought is an endpoint of the starting /// edge before you call preciselocate(). /// /// On completion, 'searchtri' is a triangle that contains 'searchpoint'. /// /// This implementation differs from that given by Guibas and Stolfi. It /// walks from triangle to triangle, crossing an edge only if 'searchpoint' /// is on the other side of the line containing that edge. After entering /// a triangle, there are two edges by which one can leave that triangle. /// If both edges are valid ('searchpoint' is on the other side of both /// edges), one of the two is chosen by drawing a line perpendicular to /// the entry edge (whose endpoints are 'forg' and 'fdest') passing through /// 'fapex'. Depending on which side of this perpendicular 'searchpoint' /// falls on, an exit edge is chosen. /// /// This implementation is empirically faster than the Guibas and Stolfi /// point location routine (which I originally used), which tends to spiral /// in toward its target. /// /// Returns ONVERTEX if the point lies on an existing vertex. 'searchtri' /// is a handle whose origin is the existing vertex. /// /// Returns ONEDGE if the point lies on a mesh edge. 'searchtri' is a /// handle whose primary edge is the edge on which the point lies. /// /// Returns INTRIANGLE if the point lies strictly within a triangle. /// 'searchtri' is a handle on the triangle that contains the point. /// /// Returns OUTSIDE if the point lies outside the mesh. 'searchtri' is a /// handle whose primary edge the point is to the right of. This might /// occur when the circumcenter of a triangle falls just slightly outside /// the mesh due to floating-point roundoff error. It also occurs when /// seeking a hole or region point that a foolish user has placed outside /// the mesh. /// /// WARNING: This routine is designed for convex triangulations, and will /// not generally work after the holes and concavities have been carved. /// However, it can still be used to find the circumcenter of a triangle, as /// long as the search is begun from the triangle in question.</remarks> public LocateResult PreciseLocate(Point searchpoint, ref Otri searchtri, bool stopatsubsegment) { Otri backtracktri = default(Otri); Osub checkedge = default(Osub); Vertex forg, fdest, fapex; double orgorient, destorient; bool moveleft; // Where are we? forg = searchtri.Org(); fdest = searchtri.Dest(); fapex = searchtri.Apex(); while (true) { // Check whether the apex is the point we seek. if ((fapex.x == searchpoint.x) && (fapex.y == searchpoint.y)) { searchtri.Lprev(); return(LocateResult.OnVertex); } // Does the point lie on the other side of the line defined by the // triangle edge opposite the triangle's destination? destorient = predicates.CounterClockwise(forg, fapex, searchpoint); // Does the point lie on the other side of the line defined by the // triangle edge opposite the triangle's origin? orgorient = predicates.CounterClockwise(fapex, fdest, searchpoint); if (destorient > 0.0) { if (orgorient > 0.0) { // Move left if the inner product of (fapex - searchpoint) and // (fdest - forg) is positive. This is equivalent to drawing // a line perpendicular to the line (forg, fdest) and passing // through 'fapex', and determining which side of this line // 'searchpoint' falls on. moveleft = (fapex.x - searchpoint.x) * (fdest.x - forg.x) + (fapex.y - searchpoint.y) * (fdest.y - forg.y) > 0.0; } else { moveleft = true; } } else { if (orgorient > 0.0) { moveleft = false; } else { // The point we seek must be on the boundary of or inside this // triangle. if (destorient == 0.0) { searchtri.Lprev(); return(LocateResult.OnEdge); } if (orgorient == 0.0) { searchtri.Lnext(); return(LocateResult.OnEdge); } return(LocateResult.InTriangle); } } // Move to another triangle. Leave a trace 'backtracktri' in case // floating-point roundoff or some such bogey causes us to walk // off a boundary of the triangulation. if (moveleft) { searchtri.Lprev(ref backtracktri); fdest = fapex; } else { searchtri.Lnext(ref backtracktri); forg = fapex; } backtracktri.Sym(ref searchtri); if (mesh.checksegments && stopatsubsegment) { // Check for walking through a subsegment. backtracktri.Pivot(ref checkedge); if (checkedge.seg.hash != Mesh.DUMMY) { // Go back to the last triangle. backtracktri.Copy(ref searchtri); return(LocateResult.Outside); } } // Check for walking right out of the triangulation. if (searchtri.tri.id == Mesh.DUMMY) { // Go back to the last triangle. backtracktri.Copy(ref searchtri); return(LocateResult.Outside); } fapex = searchtri.Apex(); } }
/// <summary> /// Spread the virus from all infected triangles to any neighbors not /// protected by subsegments. Delete all infected triangles. /// </summary> /// <remarks> /// This is the procedure that actually creates holes and concavities. /// /// This procedure operates in two phases. The first phase identifies all /// the triangles that will die, and marks them as infected. They are /// marked to ensure that each triangle is added to the virus pool only /// once, so the procedure will terminate. /// /// The second phase actually eliminates the infected triangles. It also /// eliminates orphaned vertices. /// </remarks> void Plague() { Otri testtri = default(Otri); Otri neighbor = default(Otri); Osub neighborsubseg = default(Osub); Vertex testvertex; Vertex norg, ndest; bool killorg; // Loop through all the infected triangles, spreading the virus to // their neighbors, then to their neighbors' neighbors. for (int i = 0; i < viri.Count; i++) { // WARNING: Don't use foreach, mesh.viri list may get modified. testtri.triangle = viri[i]; // A triangle is marked as infected by messing with one of its pointers // to subsegments, setting it to an illegal value. Hence, we have to // temporarily uninfect this triangle so that we can examine its // adjacent subsegments. // TODO: Not true in the C# version (so we could skip this). testtri.Uninfect(); // Check each of the triangle's three neighbors. for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { // Find the neighbor. testtri.Sym(ref neighbor); // Check for a subsegment between the triangle and its neighbor. testtri.SegPivot(ref neighborsubseg); // Check if the neighbor is nonexistent or already infected. if ((neighbor.triangle == Mesh.dummytri) || neighbor.IsInfected()) { if (neighborsubseg.seg != Mesh.dummysub) { // There is a subsegment separating the triangle from its // neighbor, but both triangles are dying, so the subsegment // dies too. mesh.SubsegDealloc(neighborsubseg.seg); if (neighbor.triangle != Mesh.dummytri) { // Make sure the subsegment doesn't get deallocated again // later when the infected neighbor is visited. neighbor.Uninfect(); neighbor.SegDissolve(); neighbor.Infect(); } } } else { // The neighbor exists and is not infected. if (neighborsubseg.seg == Mesh.dummysub) { // There is no subsegment protecting the neighbor, so // the neighbor becomes infected. neighbor.Infect(); // Ensure that the neighbor's neighbors will be infected. viri.Add(neighbor.triangle); } else { // The neighbor is protected by a subsegment. // Remove this triangle from the subsegment. neighborsubseg.TriDissolve(); // The subsegment becomes a boundary. Set markers accordingly. if (neighborsubseg.seg.boundary == 0) { neighborsubseg.seg.boundary = 1; } norg = neighbor.Org(); ndest = neighbor.Dest(); if (norg.mark == 0) { norg.mark = 1; } if (ndest.mark == 0) { ndest.mark = 1; } } } } // Remark the triangle as infected, so it doesn't get added to the // virus pool again. testtri.Infect(); } foreach (var virus in viri) { testtri.triangle = virus; // Check each of the three corners of the triangle for elimination. // This is done by walking around each vertex, checking if it is // still connected to at least one live triangle. for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { testvertex = testtri.Org(); // Check if the vertex has already been tested. if (testvertex != null) { killorg = true; // Mark the corner of the triangle as having been tested. testtri.SetOrg(null); // Walk counterclockwise about the vertex. testtri.Onext(ref neighbor); // Stop upon reaching a boundary or the starting triangle. while ((neighbor.triangle != Mesh.dummytri) && (!neighbor.Equal(testtri))) { if (neighbor.IsInfected()) { // Mark the corner of this triangle as having been tested. neighbor.SetOrg(null); } else { // A live triangle. The vertex survives. killorg = false; } // Walk counterclockwise about the vertex. neighbor.OnextSelf(); } // If we reached a boundary, we must walk clockwise as well. if (neighbor.triangle == Mesh.dummytri) { // Walk clockwise about the vertex. testtri.Oprev(ref neighbor); // Stop upon reaching a boundary. while (neighbor.triangle != Mesh.dummytri) { if (neighbor.IsInfected()) { // Mark the corner of this triangle as having been tested. neighbor.SetOrg(null); } else { // A live triangle. The vertex survives. killorg = false; } // Walk clockwise about the vertex. neighbor.OprevSelf(); } } if (killorg) { // Deleting vertex testvertex.type = VertexType.UndeadVertex; mesh.undeads++; } } } // Record changes in the number of boundary edges, and disconnect // dead triangles from their neighbors. for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { testtri.Sym(ref neighbor); if (neighbor.triangle == Mesh.dummytri) { // There is no neighboring triangle on this edge, so this edge // is a boundary edge. This triangle is being deleted, so this // boundary edge is deleted. mesh.hullsize--; } else { // Disconnect the triangle from its neighbor. neighbor.Dissolve(); // There is a neighboring triangle on this edge, so this edge // becomes a boundary edge when this triangle is deleted. mesh.hullsize++; } } // Return the dead triangle to the pool of triangles. mesh.TriangleDealloc(testtri.triangle); } // Empty the virus pool. viri.Clear(); }
public static int Reconstruct(Mesh mesh, InputGeometry input, ITriangle[] triangles) { Otri item; int num; int num1 = 0; Otri region = new Otri(); Otri otri = new Otri(); Otri l = new Otri(); Otri otri1 = new Otri(); Otri otri2 = new Otri(); Osub osub = new Osub(); int[] p0 = new int[3]; int[] p1 = new int[2]; int i = 0; int num2 = (triangles == null ? 0 : (int)triangles.Length); int count = input.segments.Count; mesh.inelements = num2; mesh.regions.AddRange(input.regions); for (i = 0; i < mesh.inelements; i++) { mesh.MakeTriangle(ref region); } if (mesh.behavior.Poly) { mesh.insegments = count; for (i = 0; i < mesh.insegments; i++) { mesh.MakeSegment(ref osub); } } List <Otri>[] otris = new List <Otri> [mesh.vertices.Count]; for (i = 0; i < mesh.vertices.Count; i++) { Otri otri3 = new Otri() { triangle = Mesh.dummytri }; otris[i] = new List <Otri>(3); otris[i].Add(otri3); } i = 0; foreach (Triangle value in mesh.triangles.Values) { region.triangle = value; p0[0] = triangles[i].P0; p0[1] = triangles[i].P1; p0[2] = triangles[i].P2; for (int j = 0; j < 3; j++) { if (p0[j] < 0 || p0[j] >= mesh.invertices) { SimpleLog.Instance.Error("Triangle has an invalid vertex index.", "MeshReader.Reconstruct()"); throw new Exception("Triangle has an invalid vertex index."); } } region.triangle.region = triangles[i].Region; if (mesh.behavior.VarArea) { region.triangle.area = triangles[i].Area; } region.orient = 0; region.SetOrg(mesh.vertices[p0[0]]); region.SetDest(mesh.vertices[p0[1]]); region.SetApex(mesh.vertices[p0[2]]); region.orient = 0; while (region.orient < 3) { num = p0[region.orient]; int count1 = otris[num].Count - 1; item = otris[num][count1]; otris[num].Add(region); l = item; if (l.triangle != Mesh.dummytri) { Vertex vertex = region.Dest(); Vertex vertex1 = region.Apex(); do { Vertex vertex2 = l.Dest(); Vertex vertex3 = l.Apex(); if (vertex1 == vertex2) { region.Lprev(ref otri); otri.Bond(ref l); } if (vertex == vertex3) { l.Lprev(ref otri1); region.Bond(ref otri1); } count1--; item = otris[num][count1]; l = item; }while (l.triangle != Mesh.dummytri); } region.orient = region.orient + 1; } i++; } num1 = 0; if (mesh.behavior.Poly) { int boundary = 0; i = 0; foreach (Segment segment in mesh.subsegs.Values) { osub.seg = segment; p1[0] = input.segments[i].P0; p1[1] = input.segments[i].P1; boundary = input.segments[i].Boundary; for (int k = 0; k < 2; k++) { if (p1[k] < 0 || p1[k] >= mesh.invertices) { SimpleLog.Instance.Error("Segment has an invalid vertex index.", "MeshReader.Reconstruct()"); throw new Exception("Segment has an invalid vertex index."); } } osub.orient = 0; Vertex item1 = mesh.vertices[p1[0]]; Vertex item2 = mesh.vertices[p1[1]]; osub.SetOrg(item1); osub.SetDest(item2); osub.SetSegOrg(item1); osub.SetSegDest(item2); osub.seg.boundary = boundary; osub.orient = 0; while (osub.orient < 2) { num = p1[1 - osub.orient]; int count2 = otris[num].Count - 1; Otri item3 = otris[num][count2]; item = otris[num][count2]; l = item; Vertex vertex4 = osub.Org(); bool flag = true; while (flag && l.triangle != Mesh.dummytri) { if (vertex4 == l.Dest()) { otris[num].Remove(item3); l.SegBond(ref osub); l.Sym(ref otri2); if (otri2.triangle == Mesh.dummytri) { mesh.InsertSubseg(ref l, 1); num1++; } flag = false; } count2--; item3 = otris[num][count2]; item = otris[num][count2]; l = item; } osub.orient = osub.orient + 1; } i++; } } for (i = 0; i < mesh.vertices.Count; i++) { int count3 = otris[i].Count - 1; item = otris[i][count3]; for (l = item; l.triangle != Mesh.dummytri; l = item) { count3--; item = otris[i][count3]; l.SegDissolve(); l.Sym(ref otri2); if (otri2.triangle == Mesh.dummytri) { mesh.InsertSubseg(ref l, 1); num1++; } } } return(num1); }