/// <summary> /// Check if given triangle is blinded by given segment. /// </summary> /// <param name="tri">Triangle.</param> /// <param name="seg">Segments</param> /// <returns>Returns true, if the triangle is blinded.</returns> private bool TriangleIsBlinded(ref Otri tri, ref Osub seg) { Point c, pt; Vertex torg = tri.Org(); Vertex tdest = tri.Dest(); Vertex tapex = tri.Apex(); Vertex sorg = seg.Org(); Vertex sdest = seg.Dest(); c = points[tri.tri.id]; if (SegmentsIntersect(sorg, sdest, c, torg, out pt, true)) { return(true); } if (SegmentsIntersect(sorg, sdest, c, tdest, out pt, true)) { return(true); } if (SegmentsIntersect(sorg, sdest, c, tapex, out pt, true)) { return(true); } return(false); }
public int CheckSeg4Encroach(ref Osub testsubseg) { double num; Vertex vertex; Otri otri = new Otri(); Osub osub = new Osub(); int num1 = 0; int num2 = 0; Vertex vertex1 = testsubseg.Org(); Vertex vertex2 = testsubseg.Dest(); testsubseg.TriPivot(ref otri); if (otri.triangle != Mesh.dummytri) { num2++; vertex = otri.Apex(); num = (vertex1.x - vertex.x) * (vertex2.x - vertex.x) + (vertex1.y - vertex.y) * (vertex2.y - vertex.y); if (num < 0 && (this.behavior.ConformingDelaunay || num * num >= (2 * this.behavior.goodAngle - 1) * (2 * this.behavior.goodAngle - 1) * ((vertex1.x - vertex.x) * (vertex1.x - vertex.x) + (vertex1.y - vertex.y) * (vertex1.y - vertex.y)) * ((vertex2.x - vertex.x) * (vertex2.x - vertex.x) + (vertex2.y - vertex.y) * (vertex2.y - vertex.y)))) { num1 = 1; } } testsubseg.Sym(ref osub); osub.TriPivot(ref otri); if (otri.triangle != Mesh.dummytri) { num2++; vertex = otri.Apex(); num = (vertex1.x - vertex.x) * (vertex2.x - vertex.x) + (vertex1.y - vertex.y) * (vertex2.y - vertex.y); if (num < 0 && (this.behavior.ConformingDelaunay || num * num >= (2 * this.behavior.goodAngle - 1) * (2 * this.behavior.goodAngle - 1) * ((vertex1.x - vertex.x) * (vertex1.x - vertex.x) + (vertex1.y - vertex.y) * (vertex1.y - vertex.y)) * ((vertex2.x - vertex.x) * (vertex2.x - vertex.x) + (vertex2.y - vertex.y) * (vertex2.y - vertex.y)))) { num1 = num1 + 2; } } if (num1 > 0 && (this.behavior.NoBisect == 0 || this.behavior.NoBisect == 1 && num2 == 2)) { BadSubseg badSubseg = new BadSubseg(); if (num1 != 1) { badSubseg.encsubseg = osub; badSubseg.subsegorg = vertex2; badSubseg.subsegdest = vertex1; } else { badSubseg.encsubseg = testsubseg; badSubseg.subsegorg = vertex1; badSubseg.subsegdest = vertex2; } this.badsubsegs.Enqueue(badSubseg); } return(num1); }
private bool TriangleIsBlinded(ref Otri tri, ref Osub seg) { Point point; Vertex vertex = tri.Org(); Vertex vertex1 = tri.Dest(); Vertex vertex2 = tri.Apex(); Vertex vertex3 = seg.Org(); Vertex vertex4 = seg.Dest(); Point point1 = this.points[tri.triangle.id]; if (this.SegmentsIntersect(vertex3, vertex4, point1, vertex, out point, true)) { return(true); } if (this.SegmentsIntersect(vertex3, vertex4, point1, vertex1, out point, true)) { return(true); } if (this.SegmentsIntersect(vertex3, vertex4, point1, vertex2, out point, true)) { return(true); } return(false); }
private void ConstructBoundaryCell(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 = _TriangleNetMesh.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("ConstructBoundaryCell: 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.tri.id != TriangleNetMesh.DUMMY) { // Go clockwise until we reach the border (or the initial triangle) while (f_prev.tri.id != TriangleNetMesh.DUMMY && !f_prev.Equals(f_init)) { f_prev.Copy(ref f); f_prev.Oprev(); } f.Copy(ref f_init); f.Onext(ref f_next); } if (f_prev.tri.id == TriangleNetMesh.DUMMY) { // 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++; segPoints.Add(p); 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++; segPoints.Add(p); vpoints.Add(p); // repeat ... until f = f_init do { // Call Lffnext the line going through the circumcenters of f and f_next cc_f = points[f.tri.id]; if (f_next.tri.id == TriangleNetMesh.DUMMY) { if (!f.tri.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++; segPoints.Add(p); vpoints.Add(p); break; } cc_f_next = points[f_next.tri.id]; // if f is tagged non-blind then if (!f.tri.infected) { // Insert the circumcenter of f into P vpoints.Add(cc_f); if (f_next.tri.infected) { // Call S_fnext the constrained edge blinding f_next sfn.seg = subsegMap[f_next.tri.hash]; // Insert point Lf,f_next /\ Sf_next into P if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } } } else { // Call Sf the constrained edge blinding f sf.seg = subsegMap[f.tri.hash]; sorg = sf.Org(); sdest = sf.Dest(); // if f_next is tagged non-blind then if (!f_next.tri.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++; segPoints.Add(p); 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++; segPoints.Add(p); vpoints.Add(p); } } else { // Call Sf_next the constrained edge blinding f_next sfn.seg = subsegMap[f_next.tri.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++; segPoints.Add(p); vpoints.Add(p); } if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); 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++; segPoints.Add(p); vpoints.Add(p); } } } } // f <- f_next f_next.Copy(ref f); // Call f_next the next triangle counterclockwise around x f_next.Onext(); }while (!f.Equals(f_init)); // Output: Bounded Voronoi cell of x in counterclockwise order. region.Add(vpoints); }
private void ConstructCell(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 = _TriangleNetMesh.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("ConstructCell: 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 = points[f.tri.id]; cc_f_next = points[f_next.tri.id]; // if f is tagged non-blind then if (!f.tri.infected) { // Insert the circumcenter of f into P vpoints.Add(cc_f); if (f_next.tri.infected) { // Call S_fnext the constrained edge blinding f_next sfn.seg = subsegMap[f_next.tri.hash]; // Insert point Lf,f_next /\ Sf_next into P if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } } } else { // Call Sf the constrained edge blinding f sf.seg = subsegMap[f.tri.hash]; // if f_next is tagged non-blind then if (!f_next.tri.infected) { // Insert point Lf,f_next /\ Sf into P if (SegmentsIntersect(sf.Org(), sf.Dest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } } else { // Call Sf_next the constrained edge blinding f_next sfn.seg = subsegMap[f_next.tri.hash]; // if Sf != Sf_next then if (!sf.Equal(sfn)) { // Insert Lf,fnext /\ Sf and Lf,fnext /\ Sfnext into P if (SegmentsIntersect(sf.Org(), sf.Dest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } } } } // f <- f_next f_next.Copy(ref f); // Call f_next the next triangle counterclockwise around x f_next.Onext(); }while (!f.Equals(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; float segmentlength, nearestpoweroftwo; float split; float multiplier, divisor; bool acuteorg, acuteorg2, acutedest, acutedest2; // Note that steinerleft == -1 if an unlimited number // of Steiner points is allowed. while (badsubsegs.Count > 0) { if (mesh.steinerleft == 0) { break; } seg = badsubsegs.Dequeue(); currentenc = seg.encsubseg; eorg = currentenc.Org(); edest = currentenc.Dest(); // Make sure that this segment is still the same segment it was // when it was determined to be encroached. If the segment was // enqueued multiple times (because several newly inserted // vertices encroached it), it may have already been split. if (!Osub.IsDead(currentenc.seg) && (eorg == seg.subsegorg) && (edest == seg.subsegdest)) { // To decide where to split a segment, we need to know if the // segment shares an endpoint with an adjacent segment. // The concern is that, if we simply split every encroached // segment in its center, two adjacent segments with a small // angle between them might lead to an infinite loop; each // vertex added to split one segment will encroach upon the // other segment, which must then be split with a vertex that // will encroach upon the first segment, and so on forever. // To avoid this, imagine a set of concentric circles, whose // radii are powers of two, about each segment endpoint. // These concentric circles determine where the segment is // split. (If both endpoints are shared with adjacent // segments, split the segment in the middle, and apply the // concentric circles for later splittings.) // Is the origin shared with another segment? currentenc.TriPivot(ref enctri); enctri.Lnext(ref testtri); testtri.SegPivot(ref testsh); acuteorg = testsh.seg != Mesh.dummysub; // Is the destination shared with another segment? testtri.LnextSelf(); testtri.SegPivot(ref testsh); acutedest = testsh.seg != Mesh.dummysub; // If we're using Chew's algorithm (rather than Ruppert's) // to define encroachment, delete free vertices from the // subsegment's diametral circle. if (!behavior.ConformingDelaunay && !acuteorg && !acutedest) { eapex = enctri.Apex(); while ((eapex.type == VertexType.FreeVertex) && ((eorg.x - eapex.x) * (edest.x - eapex.x) + (eorg.y - eapex.y) * (edest.y - eapex.y) < 0.0)) { mesh.DeleteVertex(ref testtri); currentenc.TriPivot(ref enctri); eapex = enctri.Apex(); enctri.Lprev(ref testtri); } } // Now, check the other side of the segment, if there's a triangle there. enctri.Sym(ref testtri); if (testtri.triangle != Mesh.dummytri) { // Is the destination shared with another segment? testtri.LnextSelf(); testtri.SegPivot(ref testsh); acutedest2 = testsh.seg != Mesh.dummysub; acutedest = acutedest || acutedest2; // Is the origin shared with another segment? testtri.LnextSelf(); testtri.SegPivot(ref testsh); acuteorg2 = testsh.seg != Mesh.dummysub; acuteorg = acuteorg || acuteorg2; // Delete free vertices from the subsegment's diametral circle. if (!behavior.ConformingDelaunay && !acuteorg2 && !acutedest2) { eapex = testtri.Org(); while ((eapex.type == VertexType.FreeVertex) && ((eorg.x - eapex.x) * (edest.x - eapex.x) + (eorg.y - eapex.y) * (edest.y - eapex.y) < 0.0)) { mesh.DeleteVertex(ref testtri); enctri.Sym(ref testtri); eapex = testtri.Apex(); testtri.LprevSelf(); } } } // Use the concentric circles if exactly one endpoint is shared // with another adjacent segment. if (acuteorg || acutedest) { segmentlength = UnityEngine.Mathf.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.0f; while (segmentlength > 3.0f * nearestpoweroftwo) { nearestpoweroftwo *= 2.0f; } while (segmentlength < 1.5f * nearestpoweroftwo) { nearestpoweroftwo *= 0.5f; } // Where do we split the segment? split = nearestpoweroftwo / segmentlength; if (acutedest) { split = 1.0f - split; } } else { // If we're not worried about adjacent segments, split // this segment in the middle. split = 0.5f; } // Create the new vertex (interpolate coordinates). newvertex = new Vertex( eorg.x + split * (edest.x - eorg.x), eorg.y + split * (edest.y - eorg.y), currentenc.Mark(), mesh.nextras); newvertex.type = VertexType.SegmentVertex; newvertex.hash = mesh.hash_vtx++; newvertex.id = newvertex.hash; mesh.vertices.Add(newvertex.hash, newvertex); // Interpolate attributes. for (int i = 0; i < mesh.nextras; i++) { newvertex.attributes[i] = eorg.attributes[i] + split * (edest.attributes[i] - eorg.attributes[i]); } if (!Behavior.NoExact) { // Roundoff in the above calculation may yield a 'newvertex' // that is not precisely collinear with 'eorg' and 'edest'. // Improve collinearity by one step of iterative refinement. multiplier = Primitives.CounterClockwise(eorg, edest, newvertex); divisor = ((eorg.x - edest.x) * (eorg.x - edest.x) + (eorg.y - edest.y) * (eorg.y - edest.y)); if ((multiplier != 0.0) && (divisor != 0.0)) { multiplier = multiplier / divisor; // Watch out for NANs. if (!float.IsNaN(multiplier)) { newvertex.x += multiplier * (edest.y - eorg.y); newvertex.y += multiplier * (eorg.x - edest.x); } } } // Check whether the new vertex lies on an endpoint. if (((newvertex.x == eorg.x) && (newvertex.y == eorg.y)) || ((newvertex.x == edest.x) && (newvertex.y == edest.y))) { logger.Error("Ran out of precision: I attempted to split a" + " segment to a smaller size than can be accommodated by" + " the finite precision of floating point arithmetic.", "Quality.SplitEncSegs()"); throw new Exception("Ran out of precision"); } // Insert the splitting vertex. This should always succeed. success = mesh.InsertVertex(newvertex, ref enctri, ref currentenc, true, triflaws); if ((success != InsertVertexResult.Successful) && (success != InsertVertexResult.Encroaching)) { logger.Error("Failure to split a segment.", "Quality.SplitEncSegs()"); throw new Exception("Failure to split a segment."); } if (mesh.steinerleft > 0) { mesh.steinerleft--; } // Check the two new subsegments to see if they're encroached. CheckSeg4Encroach(ref currentenc); currentenc.NextSelf(); CheckSeg4Encroach(ref currentenc); } // Set subsegment's origin to NULL. This makes it possible to detect dead // badsubsegs when traversing the list of all badsubsegs. seg.subsegorg = null; } }
/// <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; float 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.TriPivot(ref neighbortri); // Does the neighbor exist, or is this a boundary edge? if (neighbortri.triangle != Mesh.dummytri) { 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.TriPivot(ref neighbortri); // Does the neighbor exist, or is this a boundary edge? if (neighbortri.triangle != Mesh.dummytri) { 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.encsubseg = testsubseg; encroachedseg.subsegorg = eorg; encroachedseg.subsegdest = edest; } else { encroachedseg.encsubseg = testsym; encroachedseg.subsegorg = edest; encroachedseg.subsegdest = eorg; } badsubsegs.Enqueue(encroachedseg); } return(encroached); }
private void WriteMesh(TriangleNetMesh triangleNetMesh, bool skip) { // Mesh may have changed, but we choose to skip if (triangles == triangleNetMesh.triangles.Count && skip) { return; } // Header line stream.WriteLine("#!M{0}", iteration++); Vertex p1, p2, p3; if (VerticesChanged(triangleNetMesh)) { HashVertices(triangleNetMesh); // Number of vertices. stream.WriteLine("{0}", triangleNetMesh.vertices.Count); foreach (var v in triangleNetMesh.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}", triangleNetMesh.subsegs.Count); Osub subseg = default(Osub); subseg.orient = 0; foreach (var item in triangleNetMesh.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}", triangleNetMesh.triangles.Count); foreach (var item in triangleNetMesh.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); } }
/// <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, mesh.nextras, 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++; } } } }
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); } }
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; } }
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++; } } } }