/// <summary> /// Process all trianlges connected to given triangle and apply given action. /// </summary> /// <param name="triangle">The seeding triangle.</param> /// <param name="action">The action to apply to each triangle.</param> /// <param name="boundary">If non-zero, process all triangles of the /// region that is enclosed by segments with given boundary label.</param> public void Process(Triangle triangle, Action <Triangle> action, int boundary = 0) { // Make sure the triangle under consideration still exists. // It may have been eaten by the virus. if (triangle.id == TriangleNetMesh.DUMMY || Otri.IsDead(triangle)) { return; } // Add the seeding triangle to the region. region.Add(triangle); triangle.infected = true; if (boundary == 0) { // Stop at any subsegment. ProcessRegion(action, seg => seg.hash == TriangleNetMesh.DUMMY); } else { // Stop at segments that have the given boundary label. ProcessRegion(action, seg => seg.boundary != boundary); } // Free up memory (virus pool should be empty anyway). region.Clear(); }
public void Process(Triangle triangle, Action <Triangle> func) { if (triangle != Mesh.dummytri && !Otri.IsDead(triangle)) { triangle.infected = true; this.viri.Add(triangle); this.ProcessRegion(func); } this.viri.Clear(); }
public bool CheckDelaunay() { Otri otri = new Otri(); Otri otri1 = new Otri(); Osub osub = new Osub(); bool noExact = Behavior.NoExact; Behavior.NoExact = false; int num = 0; foreach (Triangle value in this.mesh.triangles.Values) { otri.triangle = value; otri.orient = 0; while (otri.orient < 3) { Vertex vertex = otri.Org(); Vertex vertex1 = otri.Dest(); Vertex vertex2 = otri.Apex(); otri.Sym(ref otri1); Vertex vertex3 = otri1.Apex(); bool flag = (otri1.triangle == Mesh.dummytri || Otri.IsDead(otri1.triangle) || otri.triangle.id >= otri1.triangle.id || !(vertex != this.mesh.infvertex1) || !(vertex != this.mesh.infvertex2) || !(vertex != this.mesh.infvertex3) || !(vertex1 != this.mesh.infvertex1) || !(vertex1 != this.mesh.infvertex2) || !(vertex1 != this.mesh.infvertex3) || !(vertex2 != this.mesh.infvertex1) || !(vertex2 != this.mesh.infvertex2) || !(vertex2 != this.mesh.infvertex3) || !(vertex3 != this.mesh.infvertex1) || !(vertex3 != this.mesh.infvertex2) ? false : vertex3 != this.mesh.infvertex3); if (this.mesh.checksegments & flag) { otri.SegPivot(ref osub); if (osub.seg != Mesh.dummysub) { flag = false; } } if (flag && Primitives.NonRegular(vertex, vertex1, vertex2, vertex3) > 0) { this.logger.Warning(string.Format("Non-regular pair of triangles found (IDs {0}/{1}).", otri.triangle.id, otri1.triangle.id), "Quality.CheckDelaunay()"); num++; } otri.orient = otri.orient + 1; } } if (num == 0) { this.logger.Info("Mesh is Delaunay."); } Behavior.NoExact = noExact; return(num == 0); }
/// <summary> /// Process all trianlges connected to given triangle and apply given action. /// </summary> public void Process(Triangle triangle, Action <Triangle> func) { if (triangle != Mesh.dummytri) { // Make sure the triangle under consideration still exists. // It may have been eaten by the virus. if (!Otri.IsDead(triangle)) { // Put one triangle in the virus pool. triangle.infected = true; viri.Add(triangle); // Apply one region's attribute and/or area constraint. ProcessRegion(func); // The virus pool should be empty now. } } // Free up memory (virus pool should be empty anyway). viri.Clear(); }
/// <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 float 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.triangle) && (borg == badtri.triangorg) && (bdest == badtri.triangdest) && (bapex == badtri.triangapex)) { 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 = Primitives.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 (Behavior.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, mesh.nextras); newvertex.type = VertexType.FreeVertex; for (int i = 0; i < mesh.nextras; i++) { // Interpolate the vertex attributes at the circumcenter. newvertex.attributes[i] = borg.attributes[i] + xi * (bdest.attributes[i] - borg.attributes[i]) + eta * (bapex.attributes[i] - borg.attributes[i]); } // 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.LprevSelf(); } // 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; 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 (Behavior.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."); } } }
/// <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> /// 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> /// <returns>Location information.</returns> /// <remarks> /// Searching begins from one of: the input 'searchtri', a recently /// encountered triangle 'recenttri', or from a triangle chosen from a /// random sample. The choice is made by determining which triangle's /// origin is closest to the point we are searching for. Normally, /// 'searchtri' should be a handle on the convex hull of the triangulation. /// /// Details on the random sampling method can be found in the Mucke, Saias, /// and Zhu paper cited in the header of this code. /// /// On completion, 'searchtri' is a triangle that contains 'searchpoint'. /// /// 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. /// </remarks> public LocateResult Locate(Point searchpoint, ref Otri searchtri) { Otri sampletri = default(Otri); Vertex torg, tdest; double searchdist, dist; double ahead; // Record the distance from the suggested starting triangle to the // point we seek. torg = searchtri.Org(); searchdist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) + (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y); // If a recently encountered triangle has been recorded and has not been // deallocated, test it as a good starting point. if (recenttri.triangle != null) { if (!Otri.IsDead(recenttri.triangle)) { torg = recenttri.Org(); if ((torg.x == searchpoint.X) && (torg.y == searchpoint.Y)) { recenttri.Copy(ref searchtri); return(LocateResult.OnVertex); } dist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) + (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y); if (dist < searchdist) { recenttri.Copy(ref searchtri); searchdist = dist; } } } // TODO: Improve sampling. sampler.Update(mesh); int[] samples = sampler.GetSamples(mesh); foreach (var key in samples) { sampletri.triangle = mesh.triangles[key]; if (!Otri.IsDead(sampletri.triangle)) { torg = sampletri.Org(); dist = (searchpoint.X - torg.x) * (searchpoint.X - torg.x) + (searchpoint.Y - torg.y) * (searchpoint.Y - torg.y); if (dist < searchdist) { sampletri.Copy(ref searchtri); searchdist = dist; } } } // Where are we? torg = searchtri.Org(); tdest = searchtri.Dest(); // Check the starting triangle's vertices. if ((torg.x == searchpoint.X) && (torg.y == searchpoint.Y)) { return(LocateResult.OnVertex); } if ((tdest.x == searchpoint.X) && (tdest.y == searchpoint.Y)) { searchtri.LnextSelf(); return(LocateResult.OnVertex); } // Orient 'searchtri' to fit the preconditions of calling preciselocate(). ahead = Primitives.CounterClockwise(torg, tdest, searchpoint); if (ahead < 0.0) { // Turn around so that 'searchpoint' is to the left of the // edge specified by 'searchtri'. searchtri.SymSelf(); } else if (ahead == 0.0) { // Check if 'searchpoint' is between 'torg' and 'tdest'. if (((torg.x < searchpoint.X) == (searchpoint.X < tdest.x)) && ((torg.y < searchpoint.Y) == (searchpoint.Y < tdest.y))) { return(LocateResult.OnEdge); } } return(PreciseLocate(searchpoint, ref searchtri, false)); }
/// <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> /// Find the holes and infect them. Find the area constraints and infect /// them. Infect the convex hull. Spread the infection and kill triangles. /// Spread the area constraints. /// </summary> public void CarveHoles() { Otri searchtri = default(Otri); Vertex searchorg, searchdest; LocateResult intersect; Triangle[] regionTris = null; if (!mesh.behavior.Convex) { // Mark as infected any unprotected triangles on the boundary. // This is one way by which concavities are created. InfectHull(); } if (!mesh.behavior.NoHoles) { // Infect each triangle in which a hole lies. foreach (var hole in mesh.holes) { // Ignore holes that aren't within the bounds of the mesh. if (mesh.bounds.Contains(hole)) { // Start searching from some triangle on the outer boundary. searchtri.triangle = Mesh.dummytri; searchtri.orient = 0; searchtri.SymSelf(); // Ensure that the hole is to the left of this boundary edge; // otherwise, locate() will falsely report that the hole // falls within the starting triangle. searchorg = searchtri.Org(); searchdest = searchtri.Dest(); if (Primitives.CounterClockwise(searchorg, searchdest, hole) > 0.0) { // Find a triangle that contains the hole. intersect = mesh.locator.Locate(hole, ref searchtri); if ((intersect != LocateResult.Outside) && (!searchtri.IsInfected())) { // Infect the triangle. This is done by marking the triangle // as infected and including the triangle in the virus pool. searchtri.Infect(); viri.Add(searchtri.triangle); } } } } } // Now, we have to find all the regions BEFORE we carve the holes, because locate() won't // work when the triangulation is no longer convex. (Incidentally, this is the reason why // regional attributes and area constraints can't be used when refining a preexisting mesh, // which might not be convex; they can only be used with a freshly triangulated PSLG.) if (mesh.regions.Count > 0) { int i = 0; regionTris = new Triangle[mesh.regions.Count]; // Find the starting triangle for each region. foreach (var region in mesh.regions) { regionTris[i] = Mesh.dummytri; // Ignore region points that aren't within the bounds of the mesh. if (mesh.bounds.Contains(region.point)) { // Start searching from some triangle on the outer boundary. searchtri.triangle = Mesh.dummytri; searchtri.orient = 0; searchtri.SymSelf(); // Ensure that the region point is to the left of this boundary // edge; otherwise, locate() will falsely report that the // region point falls within the starting triangle. searchorg = searchtri.Org(); searchdest = searchtri.Dest(); if (Primitives.CounterClockwise(searchorg, searchdest, region.point) > 0.0) { // Find a triangle that contains the region point. intersect = mesh.locator.Locate(region.point, ref searchtri); if ((intersect != LocateResult.Outside) && (!searchtri.IsInfected())) { // Record the triangle for processing after the // holes have been carved. regionTris[i] = searchtri.triangle; regionTris[i].region = region.id; } } } i++; } } if (viri.Count > 0) { // Carve the holes and concavities. Plague(); } if (regionTris != null) { var iterator = new RegionIterator(mesh); for (int i = 0; i < regionTris.Length; i++) { if (regionTris[i] != Mesh.dummytri) { // Make sure the triangle under consideration still exists. // It may have been eaten by the virus. if (!Otri.IsDead(regionTris[i])) { // Apply one region's attribute and/or area constraint. iterator.Process(regionTris[i]); } } } } // Free up memory (virus pool should be empty anyway). viri.Clear(); }
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> /// 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 Locate(Point searchpoint, ref Otri searchtri) { double x; Otri item = new Otri(); Vertex vertex = searchtri.Org(); double num = (searchpoint.X - vertex.x) * (searchpoint.X - vertex.x) + (searchpoint.Y - vertex.y) * (searchpoint.Y - vertex.y); if (this.recenttri.triangle != null && !Otri.IsDead(this.recenttri.triangle)) { vertex = this.recenttri.Org(); if (vertex.x == searchpoint.X && vertex.y == searchpoint.Y) { this.recenttri.Copy(ref searchtri); return(LocateResult.OnVertex); } x = (searchpoint.X - vertex.x) * (searchpoint.X - vertex.x) + (searchpoint.Y - vertex.y) * (searchpoint.Y - vertex.y); if (x < num) { this.recenttri.Copy(ref searchtri); num = x; } } this.sampler.Update(this.mesh); int[] samples = this.sampler.GetSamples(this.mesh); for (int i = 0; i < (int)samples.Length; i++) { int num1 = samples[i]; item.triangle = this.mesh.triangles[num1]; if (!Otri.IsDead(item.triangle)) { vertex = item.Org(); x = (searchpoint.X - vertex.x) * (searchpoint.X - vertex.x) + (searchpoint.Y - vertex.y) * (searchpoint.Y - vertex.y); if (x < num) { item.Copy(ref searchtri); num = x; } } } vertex = searchtri.Org(); Vertex vertex1 = searchtri.Dest(); if (vertex.x == searchpoint.X && vertex.y == searchpoint.Y) { return(LocateResult.OnVertex); } if (vertex1.x == searchpoint.X && vertex1.y == searchpoint.Y) { searchtri.LnextSelf(); return(LocateResult.OnVertex); } double num2 = Primitives.CounterClockwise(vertex, vertex1, searchpoint); if (num2 < 0) { searchtri.SymSelf(); } else if (num2 == 0 && vertex.x < searchpoint.X == searchpoint.X < vertex1.x && vertex.y < searchpoint.Y == searchpoint.Y < vertex1.y) { return(LocateResult.OnEdge); } return(this.PreciseLocate(searchpoint, ref searchtri, false)); }
public void CarveHoles() { Otri otri = new Otri(); Triangle[] triangleArray = null; if (!this.mesh.behavior.Convex) { this.InfectHull(); } if (!this.mesh.behavior.NoHoles) { foreach (Point hole in this.mesh.holes) { if (!this.mesh.bounds.Contains(hole)) { continue; } otri.triangle = Mesh.dummytri; otri.orient = 0; otri.SymSelf(); if (Primitives.CounterClockwise(otri.Org(), otri.Dest(), hole) <= 0 || this.mesh.locator.Locate(hole, ref otri) == LocateResult.Outside || otri.IsInfected()) { continue; } otri.Infect(); this.viri.Add(otri.triangle); } } if (this.mesh.regions.Count > 0) { int num = 0; triangleArray = new Triangle[this.mesh.regions.Count]; foreach (RegionPointer region in this.mesh.regions) { triangleArray[num] = Mesh.dummytri; if (this.mesh.bounds.Contains(region.point)) { otri.triangle = Mesh.dummytri; otri.orient = 0; otri.SymSelf(); if (Primitives.CounterClockwise(otri.Org(), otri.Dest(), region.point) > 0 && this.mesh.locator.Locate(region.point, ref otri) != LocateResult.Outside && !otri.IsInfected()) { triangleArray[num] = otri.triangle; triangleArray[num].region = region.id; } } num++; } } if (this.viri.Count > 0) { this.Plague(); } if (triangleArray != null) { RegionIterator regionIterator = new RegionIterator(this.mesh); for (int i = 0; i < (int)triangleArray.Length; i++) { if (triangleArray[i] != Mesh.dummytri && !Otri.IsDead(triangleArray[i])) { regionIterator.Process(triangleArray[i]); } } } this.viri.Clear(); }