public static CounterClockwise ( System.Point pa, System.Point pb, System.Point pc ) : double | ||
pa | System.Point | Point a. |
pb | System.Point | Point b. |
pc | System.Point | Point c. |
return | double |
public static Point FindCircumcenter(Point torg, Point tdest, Point tapex, ref double xi, ref double eta) { double num; Statistic.CircumcenterCount = Statistic.CircumcenterCount + (long)1; double num1 = tdest.x - torg.x; double num2 = tdest.y - torg.y; double num3 = tapex.x - torg.x; double num4 = tapex.y - torg.y; double num5 = num1 * num1 + num2 * num2; double num6 = num3 * num3 + num4 * num4; if (!Behavior.NoExact) { num = 0.5 / Primitives.CounterClockwise(tdest, tapex, torg); Statistic.CounterClockwiseCount = Statistic.CounterClockwiseCount - (long)1; } else { num = 0.5 / (num1 * num4 - num3 * num2); } double num7 = (num4 * num5 - num2 * num6) * num; double num8 = (num1 * num6 - num3 * num5) * num; xi = (num4 * num7 - num3 * num8) * (2 * num); eta = (num1 * num8 - num2 * num7) * (2 * num); return(new Point(torg.x + num7, torg.y + num8)); }
/// <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> /// Test the mesh for topological consistency. /// </summary> public bool CheckMesh() { Otri tri = default(Otri); Otri oppotri = default(Otri), oppooppotri = default(Otri); Vertex triorg, tridest, triapex; Vertex oppoorg, oppodest; 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 t in mesh.triangles.Values) { tri.triangle = t; // Check all three edges of the triangle. for (tri.orient = 0; tri.orient < 3; tri.orient++) { triorg = tri.Org(); tridest = tri.Dest(); if (tri.orient == 0) { // Only test for inversion once. // Test if the triangle is flat or inverted. triapex = tri.Apex(); if (Primitives.CounterClockwise(triorg, tridest, triapex) <= 0.0) { logger.Warning("Triangle is flat or inverted.", "Quality.CheckMesh()"); horrors++; } } // Find the neighboring triangle on this edge. tri.Sym(ref oppotri); if (oppotri.triangle != Mesh.dummytri) { // Check that the triangle's neighbor knows it's a neighbor. oppotri.Sym(ref oppooppotri); if ((tri.triangle != oppooppotri.triangle) || (tri.orient != oppooppotri.orient)) { if (tri.triangle == oppooppotri.triangle) { logger.Warning("Asymmetric triangle-triangle bond: (Right triangle, wrong orientation)", "Quality.CheckMesh()"); } horrors++; } // Check that both triangles agree on the identities // of their shared vertices. oppoorg = oppotri.Org(); oppodest = oppotri.Dest(); if ((triorg != oppodest) || (tridest != oppoorg)) { logger.Warning("Mismatched edge coordinates between two triangles.", "Quality.CheckMesh()"); horrors++; } } } } // Check for unconnected vertices mesh.MakeVertexMap(); foreach (var v in mesh.vertices.Values) { if (v.tri.triangle == null) { logger.Warning("Vertex (ID " + v.id + ") not connected to mesh (duplicate input vertex?)", "Quality.CheckMesh()"); } } if (horrors == 0) // && Behavior.Verbose { logger.Info("Mesh topology appears to be consistent."); } // 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> /// 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.LprevSelf(); 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 = Primitives.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 = Primitives.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.LprevSelf(); return(LocateResult.OnEdge); } if (orgorient == 0.0) { searchtri.LnextSelf(); 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.SegPivot(ref checkedge); if (checkedge.seg != Mesh.dummysub) { // Go back to the last triangle. backtracktri.Copy(ref searchtri); return(LocateResult.Outside); } } // Check for walking right out of the triangulation. if (searchtri.triangle == Mesh.dummytri) { // Go back to the last triangle. backtracktri.Copy(ref searchtri); return(LocateResult.Outside); } fapex = searchtri.Apex(); } }
/// <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(); }
public static Point FindCircumcenter(Point torg, Point tdest, Point tapex, ref double xi, ref double eta, double offconstant) { double num; double num1; double num2; Statistic.CircumcenterCount = Statistic.CircumcenterCount + (long)1; double num3 = tdest.x - torg.x; double num4 = tdest.y - torg.y; double num5 = tapex.x - torg.x; double num6 = tapex.y - torg.y; double num7 = num3 * num3 + num4 * num4; double num8 = num5 * num5 + num6 * num6; double num9 = (tdest.x - tapex.x) * (tdest.x - tapex.x) + (tdest.y - tapex.y) * (tdest.y - tapex.y); if (!Behavior.NoExact) { num = 0.5 / Primitives.CounterClockwise(tdest, tapex, torg); Statistic.CounterClockwiseCount = Statistic.CounterClockwiseCount - (long)1; } else { num = 0.5 / (num3 * num6 - num5 * num4); } double num10 = (num6 * num7 - num4 * num8) * num; double num11 = (num3 * num8 - num5 * num7) * num; if (num7 < num8 && num7 < num9) { if (offconstant > 0) { num1 = 0.5 * num3 - offconstant * num4; num2 = 0.5 * num4 + offconstant * num3; if (num1 * num1 + num2 * num2 < num10 * num10 + num11 * num11) { num10 = num1; num11 = num2; } } } else if (num8 < num9) { if (offconstant > 0) { num1 = 0.5 * num5 + offconstant * num6; num2 = 0.5 * num6 - offconstant * num5; if (num1 * num1 + num2 * num2 < num10 * num10 + num11 * num11) { num10 = num1; num11 = num2; } } } else if (offconstant > 0) { num1 = 0.5 * (tapex.x - tdest.x) - offconstant * (tapex.y - tdest.y); num2 = 0.5 * (tapex.y - tdest.y) + offconstant * (tapex.x - tdest.x); if (num1 * num1 + num2 * num2 < (num10 - num3) * (num10 - num3) + (num11 - num4) * (num11 - num4)) { num10 = num3 + num1; num11 = num4 + num2; } } xi = (num6 * num10 - num5 * num11) * (2 * num); eta = (num3 * num11 - num4 * num10) * (2 * num); return(new Point(torg.x + num10, torg.y + num11)); }
public bool CheckMesh() { Otri otri = new Otri(); Otri otri1 = new Otri(); Otri otri2 = new Otri(); 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(); if (otri.orient == 0 && Primitives.CounterClockwise(vertex, vertex1, otri.Apex()) <= 0) { this.logger.Warning("Triangle is flat or inverted.", "Quality.CheckMesh()"); num++; } otri.Sym(ref otri1); if (otri1.triangle != Mesh.dummytri) { otri1.Sym(ref otri2); if (otri.triangle != otri2.triangle || otri.orient != otri2.orient) { if (otri.triangle == otri2.triangle) { this.logger.Warning("Asymmetric triangle-triangle bond: (Right triangle, wrong orientation)", "Quality.CheckMesh()"); } num++; } Vertex vertex2 = otri1.Org(); if (vertex != otri1.Dest() || vertex1 != vertex2) { this.logger.Warning("Mismatched edge coordinates between two triangles.", "Quality.CheckMesh()"); num++; } } otri.orient = otri.orient + 1; } } this.mesh.MakeVertexMap(); foreach (Vertex value1 in this.mesh.vertices.Values) { if (value1.tri.triangle != null) { continue; } this.logger.Warning(string.Concat("Vertex (ID ", value1.id, ") not connected to mesh (duplicate input vertex?)"), "Quality.CheckMesh()"); } if (num == 0) { this.logger.Info("Mesh topology appears to be consistent."); } Behavior.NoExact = noExact; return(num == 0); }
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 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); }
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(); }