// The following handle manipulation primitives are all described by Guibas // and Stolfi. However, Guibas and Stolfi use an edge-based data structure, // whereas I use a triangle-based data structure. /// <summary> /// Find the abutting triangle; same edge. [sym(abc) -> ba*] /// </summary> /// <remarks> /// Note that the edge direction is necessarily reversed, because the handle specified /// by an oriented triangle is directed counterclockwise around the triangle. /// </remarks> public void Sym(ref Otri o2) { //o2 = tri.triangles[orient]; // decode(ptr, otri2); o2.triangle = triangle.neighbors[orient].triangle; o2.orient = triangle.neighbors[orient].orient; }
/// <summary> /// Find a new location for a Steiner point. /// </summary> /// <param name="torg"></param> /// <param name="tdest"></param> /// <param name="tapex"></param> /// <param name="xi"></param> /// <param name="eta"></param> /// <param name="offcenter"></param> /// <param name="badotri"></param> /// <returns></returns> public Point FindLocation(Vertex torg, Vertex tdest, Vertex tapex, ref double xi, ref double eta, bool offcenter, Otri badotri) { // Based on using -U switch, call the corresponding function if (behavior.MaxAngle == 0.0) { return FindNewLocationWithoutMaxAngle(torg, tdest, tapex, ref xi, ref eta, true, badotri); } // With max angle return FindNewLocation(torg, tdest, tapex, ref xi, ref eta, true, badotri); }
/// <summary> /// Find the next edge (counterclockwise) of the adjacent triangle. [rnext(abc) -> *a*] /// </summary> /// <remarks>rnext() moves one edge counterclockwise about the adjacent /// triangle. (It's best understood by reading Guibas and Stolfi. It /// involves changing triangles twice.) /// </remarks> public void Rnext(ref Otri o2) { //Sym(ref o2); o2.triangle = triangle.neighbors[orient].triangle; o2.orient = triangle.neighbors[orient].orient; //o2.LnextSelf(); o2.orient = plus1Mod3[o2.orient]; //o2.SymSelf(); int tmp = o2.orient; o2.orient = o2.triangle.neighbors[tmp].orient; o2.triangle = o2.triangle.neighbors[tmp].triangle; }
/// <summary> /// Find the previous edge (clockwise) of the adjacent triangle. [rprev(abc) -> b**] /// </summary> /// <remarks>rprev() moves one edge clockwise about the adjacent triangle. /// (It's best understood by reading Guibas and Stolfi. It involves /// changing triangles twice.) /// </remarks> public void Rprev(ref Otri o2) { //Sym(ref o2); o2.triangle = triangle.neighbors[orient].triangle; o2.orient = triangle.neighbors[orient].orient; //o2.LprevSelf(); o2.orient = minus1Mod3[o2.orient]; //o2.SymSelf(); int tmp = o2.orient; o2.orient = o2.triangle.neighbors[tmp].orient; o2.triangle = o2.triangle.neighbors[tmp].triangle; }
public void Oprev(ref Otri o2) { o2.triangle = this.triangle.neighbors[this.orient].triangle; o2.orient = this.triangle.neighbors[this.orient].orient; o2.orient = Otri.plus1Mod3[o2.orient]; }
/// <summary> /// Find the Delaunay triangulation of a polygon that has a certain "nice" shape. /// This includes the polygons that result from deletion of a vertex or insertion /// of a segment. /// </summary> /// <param name="firstedge">The primary edge of the first triangle.</param> /// <param name="lastedge">The primary edge of the last triangle.</param> /// <param name="edgecount">The number of sides of the polygon, including its /// base.</param> /// <param name="doflip">A flag, wether to perform the last flip.</param> /// <param name="triflaws">A flag that determines whether the new triangles should /// be tested for quality, and enqueued if they are bad.</param> /// <remarks> // This is a conceptually difficult routine. The starting assumption is // that we have a polygon with n sides. n - 1 of these sides are currently // represented as edges in the mesh. One side, called the "base", need not // be. // // Inside the polygon is a structure I call a "fan", consisting of n - 1 // triangles that share a common origin. For each of these triangles, the // edge opposite the origin is one of the sides of the polygon. The // primary edge of each triangle is the edge directed from the origin to // the destination; note that this is not the same edge that is a side of // the polygon. 'firstedge' is the primary edge of the first triangle. // From there, the triangles follow in counterclockwise order about the // polygon, until 'lastedge', the primary edge of the last triangle. // 'firstedge' and 'lastedge' are probably connected to other triangles // beyond the extremes of the fan, but their identity is not important, as // long as the fan remains connected to them. // // Imagine the polygon oriented so that its base is at the bottom. This // puts 'firstedge' on the far right, and 'lastedge' on the far left. // The right vertex of the base is the destination of 'firstedge', and the // left vertex of the base is the apex of 'lastedge'. // // The challenge now is to find the right sequence of edge flips to // transform the fan into a Delaunay triangulation of the polygon. Each // edge flip effectively removes one triangle from the fan, committing it // to the polygon. The resulting polygon has one fewer edge. If 'doflip' // is set, the final flip will be performed, resulting in a fan of one // (useless?) triangle. If 'doflip' is not set, the final flip is not // performed, resulting in a fan of two triangles, and an unfinished // triangular polygon that is not yet filled out with a single triangle. // On completion of the routine, 'lastedge' is the last remaining triangle, // or the leftmost of the last two. // // Although the flips are performed in the order described above, the // decisions about what flips to perform are made in precisely the reverse // order. The recursive triangulatepolygon() procedure makes a decision, // uses up to two recursive calls to triangulate the "subproblems" // (polygons with fewer edges), and then performs an edge flip. // // The "decision" it makes is which vertex of the polygon should be // connected to the base. This decision is made by testing every possible // vertex. Once the best vertex is found, the two edges that connect this // vertex to the base become the bases for two smaller polygons. These // are triangulated recursively. Unfortunately, this approach can take // O(n^2) time not only in the worst case, but in many common cases. It's // rarely a big deal for vertex deletion, where n is rarely larger than // ten, but it could be a big deal for segment insertion, especially if // there's a lot of long segments that each cut many triangles. I ought to // code a faster algorithm some day. /// </remarks> private void TriangulatePolygon(Otri firstedge, Otri lastedge, int edgecount, bool doflip, bool triflaws) { Otri testtri = default(Otri); Otri besttri = default(Otri); Otri tempedge = default(Otri); Vertex leftbasevertex, rightbasevertex; Vertex testvertex; Vertex bestvertex; int bestnumber = 1; // Identify the base vertices. leftbasevertex = lastedge.Apex(); rightbasevertex = firstedge.Dest(); // Find the best vertex to connect the base to. firstedge.Onext(ref besttri); bestvertex = besttri.Dest(); besttri.Copy(ref testtri); for (int i = 2; i <= edgecount - 2; i++) { testtri.OnextSelf(); testvertex = testtri.Dest(); // Is this a better vertex? if (Primitives.InCircle(leftbasevertex, rightbasevertex, bestvertex, testvertex) > 0.0) { testtri.Copy(ref besttri); bestvertex = testvertex; bestnumber = i; } } if (bestnumber > 1) { // Recursively triangulate the smaller polygon on the right. besttri.Oprev(ref tempedge); TriangulatePolygon(firstedge, tempedge, bestnumber + 1, true, triflaws); } if (bestnumber < edgecount - 2) { // Recursively triangulate the smaller polygon on the left. besttri.Sym(ref tempedge); TriangulatePolygon(besttri, lastedge, edgecount - bestnumber, true, triflaws); // Find 'besttri' again; it may have been lost to edge flips. tempedge.Sym(ref besttri); } if (doflip) { // Do one final edge flip. Flip(ref besttri); if (triflaws) { // Check the quality of the newly committed triangle. besttri.Sym(ref testtri); quality.TestTriangle(ref testtri); } } // Return the base triangle. besttri.Copy(ref lastedge); }
/// <summary> /// Scout the first triangle on the path from one endpoint to another, and check /// for completion (reaching the second endpoint), a collinear vertex, or the /// intersection of two segments. /// </summary> /// <param name="searchtri"></param> /// <param name="endpoint2"></param> /// <param name="newmark"></param> /// <returns>Returns true if the entire segment is successfully inserted, and false /// if the job must be finished by ConstrainedEdge().</returns> /// <remarks> /// If the first triangle on the path has the second endpoint as its /// destination or apex, a subsegment is inserted and the job is done. /// /// If the first triangle on the path has a destination or apex that lies on /// the segment, a subsegment is inserted connecting the first endpoint to /// the collinear vertex, and the search is continued from the collinear /// vertex. /// /// If the first triangle on the path has a subsegment opposite its origin, /// then there is a segment that intersects the segment being inserted. /// Their intersection vertex is inserted, splitting the subsegment. /// </remarks> private bool ScoutSegment(ref Otri searchtri, Vertex endpoint2, int newmark) { Otri crosstri = default(Otri); Osub crosssubseg = default(Osub); Vertex leftvertex, rightvertex; FindDirectionResult collinear; collinear = FindDirection(ref searchtri, endpoint2); rightvertex = searchtri.Dest(); leftvertex = searchtri.Apex(); if (((leftvertex.x == endpoint2.x) && (leftvertex.y == endpoint2.y)) || ((rightvertex.x == endpoint2.x) && (rightvertex.y == endpoint2.y))) { // The segment is already an edge in the mesh. if ((leftvertex.x == endpoint2.x) && (leftvertex.y == endpoint2.y)) { searchtri.LprevSelf(); } // Insert a subsegment, if there isn't already one there. InsertSubseg(ref searchtri, newmark); return true; } else if (collinear == FindDirectionResult.Leftcollinear) { // We've collided with a vertex between the segment's endpoints. // Make the collinear vertex be the triangle's origin. searchtri.LprevSelf(); InsertSubseg(ref searchtri, newmark); // Insert the remainder of the segment. return ScoutSegment(ref searchtri, endpoint2, newmark); } else if (collinear == FindDirectionResult.Rightcollinear) { // We've collided with a vertex between the segment's endpoints. InsertSubseg(ref searchtri, newmark); // Make the collinear vertex be the triangle's origin. searchtri.LnextSelf(); // Insert the remainder of the segment. return ScoutSegment(ref searchtri, endpoint2, newmark); } else { searchtri.Lnext(ref crosstri); crosstri.SegPivot(ref crosssubseg); // Check for a crossing segment. if (crosssubseg.seg == Mesh.dummysub) { return false; } else { // Insert a vertex at the intersection. SegmentIntersection(ref crosstri, ref crosssubseg, endpoint2); crosstri.Copy(ref searchtri); InsertSubseg(ref searchtri, newmark); // Insert the remainder of the segment. return ScoutSegment(ref searchtri, endpoint2, newmark); } } }
/// <summary> /// Enforce the Delaunay condition at an edge, fanning out recursively from /// an existing vertex. Pay special attention to stacking inverted triangles. /// </summary> /// <param name="fixuptri"></param> /// <param name="leftside">Indicates whether or not fixuptri is to the left of /// the segment being inserted. (Imagine that the segment is pointing up from /// endpoint1 to endpoint2.)</param> /// <remarks> /// This is a support routine for inserting segments into a constrained /// Delaunay triangulation. /// /// The origin of fixuptri is treated as if it has just been inserted, and /// the local Delaunay condition needs to be enforced. It is only enforced /// in one sector, however, that being the angular range defined by /// fixuptri. /// /// This routine also needs to make decisions regarding the "stacking" of /// triangles. (Read the description of ConstrainedEdge() below before /// reading on here, so you understand the algorithm.) If the position of /// the new vertex (the origin of fixuptri) indicates that the vertex before /// it on the polygon is a reflex vertex, then "stack" the triangle by /// doing nothing. (fixuptri is an inverted triangle, which is how stacked /// triangles are identified.) /// /// Otherwise, check whether the vertex before that was a reflex vertex. /// If so, perform an edge flip, thereby eliminating an inverted triangle /// (popping it off the stack). The edge flip may result in the creation /// of a new inverted triangle, depending on whether or not the new vertex /// is visible to the vertex three edges behind on the polygon. /// /// If neither of the two vertices behind the new vertex are reflex /// vertices, fixuptri and fartri, the triangle opposite it, are not /// inverted; hence, ensure that the edge between them is locally Delaunay. /// </remarks> private void DelaunayFixup(ref Otri fixuptri, bool leftside) { Otri neartri = default(Otri); Otri fartri = default(Otri); Osub faredge = default(Osub); Vertex nearvertex, leftvertex, rightvertex, farvertex; fixuptri.Lnext(ref neartri); neartri.Sym(ref fartri); // Check if the edge opposite the origin of fixuptri can be flipped. if (fartri.triangle == Mesh.dummytri) { return; } neartri.SegPivot(ref faredge); if (faredge.seg != Mesh.dummysub) { return; } // Find all the relevant vertices. nearvertex = neartri.Apex(); leftvertex = neartri.Org(); rightvertex = neartri.Dest(); farvertex = fartri.Apex(); // Check whether the previous polygon vertex is a reflex vertex. if (leftside) { if (Primitives.CounterClockwise(nearvertex, leftvertex, farvertex) <= 0.0) { // leftvertex is a reflex vertex too. Nothing can // be done until a convex section is found. return; } } else { if (Primitives.CounterClockwise(farvertex, rightvertex, nearvertex) <= 0.0) { // rightvertex is a reflex vertex too. Nothing can // be done until a convex section is found. return; } } if (Primitives.CounterClockwise(rightvertex, leftvertex, farvertex) > 0.0) { // fartri is not an inverted triangle, and farvertex is not a reflex // vertex. As there are no reflex vertices, fixuptri isn't an // inverted triangle, either. Hence, test the edge between the // triangles to ensure it is locally Delaunay. if (Primitives.InCircle(leftvertex, farvertex, rightvertex, nearvertex) <= 0.0) { return; } // Not locally Delaunay; go on to an edge flip. } // else fartri is inverted; remove it from the stack by flipping. Flip(ref neartri); fixuptri.LprevSelf(); // Restore the origin of fixuptri after the flip. // Recursively process the two triangles that result from the flip. DelaunayFixup(ref fixuptri, leftside); DelaunayFixup(ref fartri, leftside); }
/// <summary> /// Transform two triangles to two different triangles by flipping an edge /// clockwise within a quadrilateral. Reverses the flip() operation so that /// the data structures representing the triangles are back where they were /// before the flip(). /// </summary> /// <param name="flipedge"></param> /// <remarks> /// See above Flip() remarks for more information. /// /// Upon completion of this routine, the 'flipedge' handle holds the edge /// cd of triangle cdb, and is directed up, from vertex c to vertex d. /// (Hence, the two triangles have rotated clockwise.) /// </remarks> internal void Unflip(ref Otri flipedge) { Otri botleft = default(Otri), botright = default(Otri); Otri topleft = default(Otri), topright = default(Otri); Otri top = default(Otri); Otri botlcasing = default(Otri), botrcasing = default(Otri); Otri toplcasing = default(Otri), toprcasing = default(Otri); Osub botlsubseg = default(Osub), botrsubseg = default(Osub); Osub toplsubseg = default(Osub), toprsubseg = default(Osub); Vertex leftvertex, rightvertex, botvertex; Vertex farvertex; // Identify the vertices of the quadrilateral. rightvertex = flipedge.Org(); leftvertex = flipedge.Dest(); botvertex = flipedge.Apex(); flipedge.Sym(ref top); farvertex = top.Apex(); // Identify the casing of the quadrilateral. top.Lprev(ref topleft); topleft.Sym(ref toplcasing); top.Lnext(ref topright); topright.Sym(ref toprcasing); flipedge.Lnext(ref botleft); botleft.Sym(ref botlcasing); flipedge.Lprev(ref botright); botright.Sym(ref botrcasing); // Rotate the quadrilateral one-quarter turn clockwise. topleft.Bond(ref toprcasing); botleft.Bond(ref toplcasing); botright.Bond(ref botlcasing); topright.Bond(ref botrcasing); if (checksegments) { // Check for subsegments and rebond them to the quadrilateral. topleft.SegPivot(ref toplsubseg); botleft.SegPivot(ref botlsubseg); botright.SegPivot(ref botrsubseg); topright.SegPivot(ref toprsubseg); if (toplsubseg.seg == Mesh.dummysub) { botleft.SegDissolve(); } else { botleft.SegBond(ref toplsubseg); } if (botlsubseg.seg == Mesh.dummysub) { botright.SegDissolve(); } else { botright.SegBond(ref botlsubseg); } if (botrsubseg.seg == Mesh.dummysub) { topright.SegDissolve(); } else { topright.SegBond(ref botrsubseg); } if (toprsubseg.seg == Mesh.dummysub) { topleft.SegDissolve(); } else { topleft.SegBond(ref toprsubseg); } } // New vertex assignments for the rotated quadrilateral. flipedge.SetOrg(botvertex); flipedge.SetDest(farvertex); flipedge.SetApex(leftvertex); top.SetOrg(farvertex); top.SetDest(botvertex); top.SetApex(rightvertex); }
public void Lnext(ref Otri o2) { o2.triangle = this.triangle; o2.orient = Otri.plus1Mod3[this.orient]; }
void Check4DeadEvent(ref Otri checktri, SweepEvent[] eventheap, ref int heapsize) { SweepEvent deadevent; SweepEventVertex eventvertex; int eventnum = -1; eventvertex = checktri.Org() as SweepEventVertex; if (eventvertex != null) { deadevent = eventvertex.evt; eventnum = deadevent.heapposition; HeapDelete(eventheap, heapsize, eventnum); heapsize--; checktri.SetOrg(null); } }
/// <summary> /// Copy an oriented triangle. /// </summary> public void Copy(ref Otri o2) { o2.triangle = triangle; o2.orient = orient; }
/// <summary> /// Find the previous edge (clockwise) of a triangle. [lprev(abc) -> cab] /// </summary> public void Lprev(ref Otri o2) { o2.triangle = triangle; o2.orient = minus1Mod3[orient]; }
public void Dnext(ref Otri o2) { o2.triangle = this.triangle.neighbors[this.orient].triangle; o2.orient = this.triangle.neighbors[this.orient].orient; o2.orient = Otri.minus1Mod3[o2.orient]; }
public void Sym(ref Otri o2) { o2.triangle = this.triangle.neighbors[this.orient].triangle; o2.orient = this.triangle.neighbors[this.orient].orient; }
SplayNode Splay(SplayNode splaytree, Point searchpoint, ref Otri searchtri) { SplayNode child, grandchild; SplayNode lefttree, righttree; SplayNode leftright; Vertex checkvertex; bool rightofroot, rightofchild; if (splaytree == null) { return null; } checkvertex = splaytree.keyedge.Dest(); if (checkvertex == splaytree.keydest) { rightofroot = RightOfHyperbola(ref splaytree.keyedge, searchpoint); if (rightofroot) { splaytree.keyedge.Copy(ref searchtri); child = splaytree.rchild; } else { child = splaytree.lchild; } if (child == null) { return splaytree; } checkvertex = child.keyedge.Dest(); if (checkvertex != child.keydest) { child = Splay(child, searchpoint, ref searchtri); if (child == null) { if (rightofroot) { splaytree.rchild = null; } else { splaytree.lchild = null; } return splaytree; } } rightofchild = RightOfHyperbola(ref child.keyedge, searchpoint); if (rightofchild) { child.keyedge.Copy(ref searchtri); grandchild = Splay(child.rchild, searchpoint, ref searchtri); child.rchild = grandchild; } else { grandchild = Splay(child.lchild, searchpoint, ref searchtri); child.lchild = grandchild; } if (grandchild == null) { if (rightofroot) { splaytree.rchild = child.lchild; child.lchild = splaytree; } else { splaytree.lchild = child.rchild; child.rchild = splaytree; } return child; } if (rightofchild) { if (rightofroot) { splaytree.rchild = child.lchild; child.lchild = splaytree; } else { splaytree.lchild = grandchild.rchild; grandchild.rchild = splaytree; } child.rchild = grandchild.lchild; grandchild.lchild = child; } else { if (rightofroot) { splaytree.rchild = grandchild.lchild; grandchild.lchild = splaytree; } else { splaytree.lchild = child.rchild; child.rchild = splaytree; } child.lchild = grandchild.rchild; grandchild.rchild = child; } return grandchild; } else { lefttree = Splay(splaytree.lchild, searchpoint, ref searchtri); righttree = Splay(splaytree.rchild, searchpoint, ref searchtri); splaynodes.Remove(splaytree); if (lefttree == null) { return righttree; } else if (righttree == null) { return lefttree; } else if (lefttree.rchild == null) { lefttree.rchild = righttree.lchild; righttree.lchild = lefttree; return righttree; } else if (righttree.lchild == null) { righttree.lchild = lefttree.rchild; lefttree.rchild = righttree; return lefttree; } else { // printf("Holy Toledo!!!\n"); leftright = lefttree.rchild; while (leftright.rchild != null) { leftright = leftright.rchild; } leftright.rchild = righttree; return lefttree; } } }
/// <summary> /// Test for equality of oriented triangles. /// </summary> public bool Equal(Otri o2) { return((triangle == o2.triangle) && (orient == o2.orient)); }
SplayNode CircleTopInsert(SplayNode splayroot, Otri newkey, Vertex pa, Vertex pb, Vertex pc, double topy) { double ccwabc; double xac, yac, xbc, ybc; double aclen2, bclen2; Point searchpoint = new Point(); // TODO: mesh.nextras Otri dummytri = default(Otri); ccwabc = Primitives.CounterClockwise(pa, pb, pc); xac = pa.x - pc.x; yac = pa.y - pc.y; xbc = pb.x - pc.x; ybc = pb.y - pc.y; aclen2 = xac * xac + yac * yac; bclen2 = xbc * xbc + ybc * ybc; searchpoint.x = pc.x - (yac * bclen2 - ybc * aclen2) / (2.0 * ccwabc); searchpoint.y = topy; return SplayInsert(Splay(splayroot, searchpoint, ref dummytri), newkey, searchpoint); }
// lnext() finds the next edge (counterclockwise) of a triangle. /// <summary> /// Find the next edge (counterclockwise) of a triangle. [lnext(abc) -> bca] /// </summary> public void Lnext(ref Otri o2) { o2.triangle = triangle; o2.orient = plus1Mod3[orient]; }
/// <summary> /// Removes ghost triangles. /// </summary> /// <param name="startghost"></param> /// <returns>Number of vertices on the hull.</returns> int RemoveGhosts(ref Otri startghost) { Otri searchedge = default(Otri); Otri dissolveedge = default(Otri); Otri deadtriangle = default(Otri); Vertex markorg; int hullsize; bool noPoly = !mesh.behavior.Poly; // Find an edge on the convex hull to start point location from. startghost.Lprev(ref searchedge); searchedge.SymSelf(); Mesh.dummytri.neighbors[0] = searchedge; // Remove the bounding box and count the convex hull edges. startghost.Copy(ref dissolveedge); hullsize = 0; do { hullsize++; dissolveedge.Lnext(ref deadtriangle); dissolveedge.LprevSelf(); dissolveedge.SymSelf(); // If no PSLG is involved, set the boundary markers of all the vertices // on the convex hull. If a PSLG is used, this step is done later. if (noPoly) { // Watch out for the case where all the input vertices are collinear. if (dissolveedge.triangle != Mesh.dummytri) { markorg = dissolveedge.Org(); if (markorg.mark == 0) { markorg.mark = 1; } } } // Remove a bounding triangle from a convex hull triangle. dissolveedge.Dissolve(); // Find the next bounding triangle. deadtriangle.Sym(ref dissolveedge); // Delete the bounding triangle. mesh.TriangleDealloc(deadtriangle.triangle); } while (!dissolveedge.Equal(startghost)); return hullsize; }
/// <summary> /// Finds the star of a given point. /// </summary> /// <param name="badotri"></param> /// <param name="p"></param> /// <param name="q"></param> /// <param name="r"></param> /// <param name="whichPoint"></param> /// <param name="points">List of points on the star of the given point.</param> /// <returns>Number of points on the star of the given point.</returns> private int GetStarPoints(Otri badotri, Vertex p, Vertex q, Vertex r, int whichPoint, ref double[] points) { Otri neighotri = default(Otri); // for return value of the function Otri tempotri; // for temporary usage double first_x = 0, first_y = 0; // keeps the first point to be considered double second_x = 0, second_y = 0; // for determining the edge we will begin double third_x = 0, third_y = 0; // termination double[] returnPoint = new double[2]; // for keeping the returned point int numvertices = 0; // for keeping number of surrounding vertices // first determine which point to be used to find its neighbor triangles switch (whichPoint) { case 1: first_x = p.x; // point at the center first_y = p.y; second_x = r.x; // second vertex of first edge to consider second_y = r.y; third_x = q.x; // for terminating the search third_y = q.y; break; case 2: first_x = q.x; // point at the center first_y = q.y; second_x = p.x; // second vertex of first edge to consider second_y = p.y; third_x = r.x; // for terminating the search third_y = r.y; break; case 3: first_x = r.x; // point at the center first_y = r.y; second_x = q.x; // second vertex of first edge to consider second_y = q.y; third_x = p.x; // for terminating the search third_y = p.y; break; } tempotri = badotri; // add first point as the end of first edge points[numvertices] = second_x; numvertices++; points[numvertices] = second_y; numvertices++; // assign as dummy value returnPoint[0] = second_x; returnPoint[1] = second_y; // until we reach the third point of the beginning triangle do { // find the neighbor's third point where it is incident to given edge if (!GetNeighborsVertex(tempotri, first_x, first_y, second_x, second_y, ref returnPoint, ref neighotri)) { // go to next triangle tempotri = neighotri; // now the second point is the neighbor's third vertex second_x = returnPoint[0]; second_y = returnPoint[1]; // add a new point to the list of surrounding points points[numvertices] = returnPoint[0]; numvertices++; points[numvertices] = returnPoint[1]; numvertices++; } else { numvertices = 0; break; } } while (!((Math.Abs(returnPoint[0] - third_x) <= EPS) && (Math.Abs(returnPoint[1] - third_y) <= EPS))); return numvertices / 2; }
/// <summary> /// Create a new triangle with orientation zero. /// </summary> /// <param name="newotri">Reference to the new triangle.</param> internal void MakeTriangle(ref Otri newotri) { Triangle tri = new Triangle(); tri.hash = this.hash_tri++; tri.id = tri.hash; newotri.triangle = tri; newotri.orient = 0; triangles.Add(tri.hash, tri); }
/// <summary> /// Gets a neighbours vertex. /// </summary> /// <param name="badotri"></param> /// <param name="first_x"></param> /// <param name="first_y"></param> /// <param name="second_x"></param> /// <param name="second_y"></param> /// <param name="thirdpoint">Neighbor's third vertex incident to given edge.</param> /// <param name="neighotri">Pointer for the neighbor triangle.</param> /// <returns>Returns true if vertex was found.</returns> private bool GetNeighborsVertex(Otri badotri, double first_x, double first_y, double second_x, double second_y, ref double[] thirdpoint, ref Otri neighotri) { Otri neighbor = default(Otri); // keeps the neighbor triangles bool notFound = false; // boolean variable if we can find that neighbor or not // for keeping the vertices of the neighbor triangle Vertex neighborvertex_1 = null; Vertex neighborvertex_2 = null; Vertex neighborvertex_3 = null; // used for finding neighbor triangle int firstVertexMatched = 0, secondVertexMatched = 0; // to find the correct neighbor //triangle ptr; // Temporary variable used by sym() //int i; // index variable // find neighbors // Check each of the triangle's three neighbors to find the correct one for (badotri.orient = 0; badotri.orient < 3; badotri.orient++) { // Find the neighbor. badotri.Sym(ref neighbor); // check if it is the one we are looking for by checking the corners // first check if the neighbor is nonexistent, since it can be on the border if ((neighbor.triangle != Mesh.dummytri)) { // then check if two wanted corners are also in this triangle // take the vertices of the candidate neighbor neighborvertex_1 = neighbor.Org(); neighborvertex_2 = neighbor.Dest(); neighborvertex_3 = neighbor.Apex(); // check if it is really a triangle if ((neighborvertex_1.x == neighborvertex_2.x && neighborvertex_1.y == neighborvertex_2.y) || (neighborvertex_2.x == neighborvertex_3.x && neighborvertex_2.y == neighborvertex_3.y) || (neighborvertex_1.x == neighborvertex_3.x && neighborvertex_1.y == neighborvertex_3.y)) { //printf("Two vertices are the same!!!!!!!\n"); } else { // begin searching for the correct neighbor triangle firstVertexMatched = 0; if ((Math.Abs(first_x - neighborvertex_1.x) < EPS) && (Math.Abs(first_y - neighborvertex_1.y) < EPS)) { firstVertexMatched = 11; // neighbor's 1st vertex is matched to first vertex } else if ((Math.Abs(first_x - neighborvertex_2.x) < EPS) && (Math.Abs(first_y - neighborvertex_2.y) < EPS)) { firstVertexMatched = 12; // neighbor's 2nd vertex is matched to first vertex } else if ((Math.Abs(first_x - neighborvertex_3.x) < EPS) && (Math.Abs(first_y - neighborvertex_3.y) < EPS)) { firstVertexMatched = 13; // neighbor's 3rd vertex is matched to first vertex }/*else{ // none of them matched } // end of first vertex matching */ secondVertexMatched = 0; if ((Math.Abs(second_x - neighborvertex_1.x) < EPS) && (Math.Abs(second_y - neighborvertex_1.y) < EPS)) { secondVertexMatched = 21; // neighbor's 1st vertex is matched to second vertex } else if ((Math.Abs(second_x - neighborvertex_2.x) < EPS) && (Math.Abs(second_y - neighborvertex_2.y) < EPS)) { secondVertexMatched = 22; // neighbor's 2nd vertex is matched to second vertex } else if ((Math.Abs(second_x - neighborvertex_3.x) < EPS) && (Math.Abs(second_y - neighborvertex_3.y) < EPS)) { secondVertexMatched = 23; // neighbor's 3rd vertex is matched to second vertex }/*else{ // none of them matched } // end of second vertex matching*/ } }// if neighbor exists or not if (((firstVertexMatched == 11) && (secondVertexMatched == 22 || secondVertexMatched == 23)) || ((firstVertexMatched == 12) && (secondVertexMatched == 21 || secondVertexMatched == 23)) || ((firstVertexMatched == 13) && (secondVertexMatched == 21 || secondVertexMatched == 22))) break; }// end of for loop over all orientations switch (firstVertexMatched) { case 0: notFound = true; break; case 11: if (secondVertexMatched == 22) { thirdpoint[0] = neighborvertex_3.x; thirdpoint[1] = neighborvertex_3.y; } else if (secondVertexMatched == 23) { thirdpoint[0] = neighborvertex_2.x; thirdpoint[1] = neighborvertex_2.y; } else { notFound = true; } break; case 12: if (secondVertexMatched == 21) { thirdpoint[0] = neighborvertex_3.x; thirdpoint[1] = neighborvertex_3.y; } else if (secondVertexMatched == 23) { thirdpoint[0] = neighborvertex_1.x; thirdpoint[1] = neighborvertex_1.y; } else { notFound = true; } break; case 13: if (secondVertexMatched == 21) { thirdpoint[0] = neighborvertex_2.x; thirdpoint[1] = neighborvertex_2.y; } else if (secondVertexMatched == 22) { thirdpoint[0] = neighborvertex_1.x; thirdpoint[1] = neighborvertex_1.y; } else { notFound = true; } break; default: if (secondVertexMatched == 0) { notFound = true; } break; } // pointer of the neighbor triangle neighotri = neighbor; return notFound; }
/// <summary> /// Force a segment into a constrained Delaunay triangulation by deleting the /// triangles it intersects, and triangulating the polygons that form on each /// side of it. /// </summary> /// <param name="starttri"></param> /// <param name="endpoint2"></param> /// <param name="newmark"></param> /// <remarks> /// Generates a single subsegment connecting 'endpoint1' to 'endpoint2'. /// The triangle 'starttri' has 'endpoint1' as its origin. 'newmark' is the /// boundary marker of the segment. /// /// To insert a segment, every triangle whose interior intersects the /// segment is deleted. The union of these deleted triangles is a polygon /// (which is not necessarily monotone, but is close enough), which is /// divided into two polygons by the new segment. This routine's task is /// to generate the Delaunay triangulation of these two polygons. /// /// You might think of this routine's behavior as a two-step process. The /// first step is to walk from endpoint1 to endpoint2, flipping each edge /// encountered. This step creates a fan of edges connected to endpoint1, /// including the desired edge to endpoint2. The second step enforces the /// Delaunay condition on each side of the segment in an incremental manner: /// proceeding along the polygon from endpoint1 to endpoint2 (this is done /// independently on each side of the segment), each vertex is "enforced" /// as if it had just been inserted, but affecting only the previous /// vertices. The result is the same as if the vertices had been inserted /// in the order they appear on the polygon, so the result is Delaunay. /// /// In truth, ConstrainedEdge() interleaves these two steps. The procedure /// walks from endpoint1 to endpoint2, and each time an edge is encountered /// and flipped, the newly exposed vertex (at the far end of the flipped /// edge) is "enforced" upon the previously flipped edges, usually affecting /// only one side of the polygon (depending upon which side of the segment /// the vertex falls on). /// /// The algorithm is complicated by the need to handle polygons that are not /// convex. Although the polygon is not necessarily monotone, it can be /// triangulated in a manner similar to the stack-based algorithms for /// monotone polygons. For each reflex vertex (local concavity) of the /// polygon, there will be an inverted triangle formed by one of the edge /// flips. (An inverted triangle is one with negative area - that is, its /// vertices are arranged in clockwise order - and is best thought of as a /// wrinkle in the fabric of the mesh.) Each inverted triangle can be /// thought of as a reflex vertex pushed on the stack, waiting to be fixed /// later. /// /// A reflex vertex is popped from the stack when a vertex is inserted that /// is visible to the reflex vertex. (However, if the vertex behind the /// reflex vertex is not visible to the reflex vertex, a new inverted /// triangle will take its place on the stack.) These details are handled /// by the DelaunayFixup() routine above. /// </remarks> private void ConstrainedEdge(ref Otri starttri, Vertex endpoint2, int newmark) { Otri fixuptri = default(Otri), fixuptri2 = default(Otri); Osub crosssubseg = default(Osub); Vertex endpoint1; Vertex farvertex; double area; bool collision; bool done; endpoint1 = starttri.Org(); starttri.Lnext(ref fixuptri); Flip(ref fixuptri); // 'collision' indicates whether we have found a vertex directly // between endpoint1 and endpoint2. collision = false; done = false; do { farvertex = fixuptri.Org(); // 'farvertex' is the extreme point of the polygon we are "digging" // to get from endpoint1 to endpoint2. if ((farvertex.x == endpoint2.x) && (farvertex.y == endpoint2.y)) { fixuptri.Oprev(ref fixuptri2); // Enforce the Delaunay condition around endpoint2. DelaunayFixup(ref fixuptri, false); DelaunayFixup(ref fixuptri2, true); done = true; } else { // Check whether farvertex is to the left or right of the segment being // inserted, to decide which edge of fixuptri to dig through next. area = Primitives.CounterClockwise(endpoint1, endpoint2, farvertex); if (area == 0.0) { // We've collided with a vertex between endpoint1 and endpoint2. collision = true; fixuptri.Oprev(ref fixuptri2); // Enforce the Delaunay condition around farvertex. DelaunayFixup(ref fixuptri, false); DelaunayFixup(ref fixuptri2, true); done = true; } else { if (area > 0.0) { // farvertex is to the left of the segment. fixuptri.Oprev(ref fixuptri2); // Enforce the Delaunay condition around farvertex, on the // left side of the segment only. DelaunayFixup(ref fixuptri2, true); // Flip the edge that crosses the segment. After the edge is // flipped, one of its endpoints is the fan vertex, and the // destination of fixuptri is the fan vertex. fixuptri.LprevSelf(); } else { // farvertex is to the right of the segment. DelaunayFixup(ref fixuptri, false); // Flip the edge that crosses the segment. After the edge is // flipped, one of its endpoints is the fan vertex, and the // destination of fixuptri is the fan vertex. fixuptri.OprevSelf(); } // Check for two intersecting segments. fixuptri.SegPivot(ref crosssubseg); if (crosssubseg.seg == Mesh.dummysub) { Flip(ref fixuptri); // May create inverted triangle at left. } else { // We've collided with a segment between endpoint1 and endpoint2. collision = true; // Insert a vertex at the intersection. SegmentIntersection(ref fixuptri, ref crosssubseg, endpoint2); done = true; } } } } while (!done); // Insert a subsegment to make the segment permanent. InsertSubseg(ref fixuptri, newmark); // If there was a collision with an interceding vertex, install another // segment connecting that vertex with endpoint2. if (collision) { // Insert the remainder of the segment. if (!ScoutSegment(ref fixuptri, endpoint2, newmark)) { ConstrainedEdge(ref fixuptri, endpoint2, newmark); } } }
/// <summary> /// Given the triangulation, and a vertex returns the minimum distance to the /// vertices of the triangle where the given vertex located. /// </summary> /// <param name="newlocX"></param> /// <param name="newlocY"></param> /// <param name="searchtri"></param> /// <returns></returns> private double MinDistanceToNeighbor(double newlocX, double newlocY, ref Otri searchtri) { Otri horiz = default(Otri); // for search operation LocateResult intersect = LocateResult.Outside; Vertex v1, v2, v3, torg, tdest; double d1, d2, d3, ahead; //triangle ptr; // Temporary variable used by sym(). Point newvertex = new Point(newlocX, newlocY); // printf("newvertex %f,%f\n", newvertex[0], newvertex[1]); // Find the location of the vertex to be inserted. Check if a good // starting triangle has already been provided by the caller. // Find a boundary triangle. //horiz.tri = m.dummytri; //horiz.orient = 0; //horiz.symself(); // Search for a triangle containing 'newvertex'. // Start searching from the triangle provided by the caller. // Where are we? torg = searchtri.Org(); tdest = searchtri.Dest(); // Check the starting triangle's vertices. if ((torg.x == newvertex.x) && (torg.y == newvertex.y)) { intersect = LocateResult.OnVertex; searchtri.Copy(ref horiz); } else if ((tdest.x == newvertex.x) && (tdest.y == newvertex.y)) { searchtri.LnextSelf(); intersect = LocateResult.OnVertex; searchtri.Copy(ref horiz); } else { // Orient 'searchtri' to fit the preconditions of calling preciselocate(). ahead = Primitives.CounterClockwise(torg, tdest, newvertex); if (ahead < 0.0) { // Turn around so that 'searchpoint' is to the left of the // edge specified by 'searchtri'. searchtri.SymSelf(); searchtri.Copy(ref horiz); intersect = mesh.locator.PreciseLocate(newvertex, ref horiz, false); } else if (ahead == 0.0) { // Check if 'searchpoint' is between 'torg' and 'tdest'. if (((torg.x < newvertex.x) == (newvertex.x < tdest.x)) && ((torg.y < newvertex.y) == (newvertex.y < tdest.y))) { intersect = LocateResult.OnEdge; searchtri.Copy(ref horiz); } } else { searchtri.Copy(ref horiz); intersect = mesh.locator.PreciseLocate(newvertex, ref horiz, false); } } if (intersect == LocateResult.OnVertex || intersect == LocateResult.Outside) { // set distance to 0 //m.VertexDealloc(newvertex); return 0.0; } else { // intersect == ONEDGE || intersect == INTRIANGLE // find the triangle vertices v1 = horiz.Org(); v2 = horiz.Dest(); v3 = horiz.Apex(); d1 = (v1.x - newvertex.x) * (v1.x - newvertex.x) + (v1.y - newvertex.y) * (v1.y - newvertex.y); d2 = (v2.x - newvertex.x) * (v2.x - newvertex.x) + (v2.y - newvertex.y) * (v2.y - newvertex.y); d3 = (v3.x - newvertex.x) * (v3.x - newvertex.x) + (v3.y - newvertex.y) * (v3.y - newvertex.y); //m.VertexDealloc(newvertex); // find minimum of the distance if (d1 <= d2 && d1 <= d3) { return d1; } else if (d2 <= d3) { return d2; } else { return d3; } } }
/// <summary> /// Find the first triangle on the path from one point to another. /// </summary> /// <param name="searchtri"></param> /// <param name="searchpoint"></param> /// <returns> /// The return value notes whether the destination or apex of the found /// triangle is collinear with the two points in question.</returns> /// <remarks> /// Finds the triangle that intersects a line segment drawn from the /// origin of 'searchtri' to the point 'searchpoint', and returns the result /// in 'searchtri'. The origin of 'searchtri' does not change, even though /// the triangle returned may differ from the one passed in. This routine /// is used to find the direction to move in to get from one point to /// another. /// </remarks> private FindDirectionResult FindDirection(ref Otri searchtri, Vertex searchpoint) { Otri checktri = default(Otri); Vertex startvertex; Vertex leftvertex, rightvertex; double leftccw, rightccw; bool leftflag, rightflag; startvertex = searchtri.Org(); rightvertex = searchtri.Dest(); leftvertex = searchtri.Apex(); // Is 'searchpoint' to the left? leftccw = Primitives.CounterClockwise(searchpoint, startvertex, leftvertex); leftflag = leftccw > 0.0; // Is 'searchpoint' to the right? rightccw = Primitives.CounterClockwise(startvertex, searchpoint, rightvertex); rightflag = rightccw > 0.0; if (leftflag && rightflag) { // 'searchtri' faces directly away from 'searchpoint'. We could go left // or right. Ask whether it's a triangle or a boundary on the left. searchtri.Onext(ref checktri); if (checktri.triangle == Mesh.dummytri) { leftflag = false; } else { rightflag = false; } } while (leftflag) { // Turn left until satisfied. searchtri.OnextSelf(); if (searchtri.triangle == Mesh.dummytri) { logger.Error("Unable to find a triangle on path.", "Mesh.FindDirection().1"); throw new Exception("Unable to find a triangle on path."); } leftvertex = searchtri.Apex(); rightccw = leftccw; leftccw = Primitives.CounterClockwise(searchpoint, startvertex, leftvertex); leftflag = leftccw > 0.0; } while (rightflag) { // Turn right until satisfied. searchtri.OprevSelf(); if (searchtri.triangle == Mesh.dummytri) { logger.Error("Unable to find a triangle on path.", "Mesh.FindDirection().2"); throw new Exception("Unable to find a triangle on path."); } rightvertex = searchtri.Dest(); leftccw = rightccw; rightccw = Primitives.CounterClockwise(startvertex, searchpoint, rightvertex); rightflag = rightccw > 0.0; } if (leftccw == 0.0) { return FindDirectionResult.Leftcollinear; } else if (rightccw == 0.0) { return FindDirectionResult.Rightcollinear; } else { return FindDirectionResult.Within; } }
/// <summary> /// Transform two triangles to two different triangles by flipping an edge /// counterclockwise within a quadrilateral. /// </summary> /// <param name="flipedge">Handle to the edge that will be flipped.</param> /// <remarks>Imagine the original triangles, abc and bad, oriented so that the /// shared edge ab lies in a horizontal plane, with the vertex b on the left /// and the vertex a on the right. The vertex c lies below the edge, and /// the vertex d lies above the edge. The 'flipedge' handle holds the edge /// ab of triangle abc, and is directed left, from vertex a to vertex b. /// /// The triangles abc and bad are deleted and replaced by the triangles cdb /// and dca. The triangles that represent abc and bad are NOT deallocated; /// they are reused for dca and cdb, respectively. Hence, any handles that /// may have held the original triangles are still valid, although not /// directed as they were before. /// /// Upon completion of this routine, the 'flipedge' handle holds the edge /// dc of triangle dca, and is directed down, from vertex d to vertex c. /// (Hence, the two triangles have rotated counterclockwise.) /// /// WARNING: This transformation is geometrically valid only if the /// quadrilateral adbc is convex. Furthermore, this transformation is /// valid only if there is not a subsegment between the triangles abc and /// bad. This routine does not check either of these preconditions, and /// it is the responsibility of the calling routine to ensure that they are /// met. If they are not, the streets shall be filled with wailing and /// gnashing of teeth. /// /// Terminology /// /// A "local transformation" replaces a small set of triangles with another /// set of triangles. This may or may not involve inserting or deleting a /// vertex. /// /// The term "casing" is used to describe the set of triangles that are /// attached to the triangles being transformed, but are not transformed /// themselves. Think of the casing as a fixed hollow structure inside /// which all the action happens. A "casing" is only defined relative to /// a single transformation; each occurrence of a transformation will /// involve a different casing. /// </remarks> internal void Flip(ref Otri flipedge) { Otri botleft = default(Otri), botright = default(Otri); Otri topleft = default(Otri), topright = default(Otri); Otri top = default(Otri); Otri botlcasing = default(Otri), botrcasing = default(Otri); Otri toplcasing = default(Otri), toprcasing = default(Otri); Osub botlsubseg = default(Osub), botrsubseg = default(Osub); Osub toplsubseg = default(Osub), toprsubseg = default(Osub); Vertex leftvertex, rightvertex, botvertex; Vertex farvertex; // Identify the vertices of the quadrilateral. rightvertex = flipedge.Org(); leftvertex = flipedge.Dest(); botvertex = flipedge.Apex(); flipedge.Sym(ref top); // SELF CHECK //if (top.triangle == dummytri) //{ // logger.Error("Attempt to flip on boundary.", "Mesh.Flip()"); // flipedge.LnextSelf(); // return; //} //if (checksegments) //{ // flipedge.SegPivot(ref toplsubseg); // if (toplsubseg.ss != dummysub) // { // logger.Error("Attempt to flip a segment.", "Mesh.Flip()"); // flipedge.LnextSelf(); // return; // } //} farvertex = top.Apex(); // Identify the casing of the quadrilateral. top.Lprev(ref topleft); topleft.Sym(ref toplcasing); top.Lnext(ref topright); topright.Sym(ref toprcasing); flipedge.Lnext(ref botleft); botleft.Sym(ref botlcasing); flipedge.Lprev(ref botright); botright.Sym(ref botrcasing); // Rotate the quadrilateral one-quarter turn counterclockwise. topleft.Bond(ref botlcasing); botleft.Bond(ref botrcasing); botright.Bond(ref toprcasing); topright.Bond(ref toplcasing); if (checksegments) { // Check for subsegments and rebond them to the quadrilateral. topleft.SegPivot(ref toplsubseg); botleft.SegPivot(ref botlsubseg); botright.SegPivot(ref botrsubseg); topright.SegPivot(ref toprsubseg); if (toplsubseg.seg == Mesh.dummysub) { topright.SegDissolve(); } else { topright.SegBond(ref toplsubseg); } if (botlsubseg.seg == Mesh.dummysub) { topleft.SegDissolve(); } else { topleft.SegBond(ref botlsubseg); } if (botrsubseg.seg == Mesh.dummysub) { botleft.SegDissolve(); } else { botleft.SegBond(ref botrsubseg); } if (toprsubseg.seg == Mesh.dummysub) { botright.SegDissolve(); } else { botright.SegBond(ref toprsubseg); } } // New vertex assignments for the rotated quadrilateral. flipedge.SetOrg(farvertex); flipedge.SetDest(botvertex); flipedge.SetApex(rightvertex); top.SetOrg(botvertex); top.SetDest(farvertex); top.SetApex(leftvertex); }
/// <summary> /// Find the intersection of an existing segment and a segment that is being /// inserted. Insert a vertex at the intersection, splitting an existing subsegment. /// </summary> /// <param name="splittri"></param> /// <param name="splitsubseg"></param> /// <param name="endpoint2"></param> /// <remarks> /// The segment being inserted connects the apex of splittri to endpoint2. /// splitsubseg is the subsegment being split, and MUST adjoin splittri. /// Hence, endpoints of the subsegment being split are the origin and /// destination of splittri. /// /// On completion, splittri is a handle having the newly inserted /// intersection point as its origin, and endpoint1 as its destination. /// </remarks> private void SegmentIntersection(ref Otri splittri, ref Osub splitsubseg, Vertex endpoint2) { Osub opposubseg = default(Osub); Vertex endpoint1; Vertex torg, tdest; Vertex leftvertex, rightvertex; Vertex newvertex; InsertVertexResult success; double ex, ey; double tx, ty; double etx, ety; double split, denom; // Find the other three segment endpoints. endpoint1 = splittri.Apex(); torg = splittri.Org(); tdest = splittri.Dest(); // Segment intersection formulae; see the Antonio reference. tx = tdest.x - torg.x; ty = tdest.y - torg.y; ex = endpoint2.x - endpoint1.x; ey = endpoint2.y - endpoint1.y; etx = torg.x - endpoint2.x; ety = torg.y - endpoint2.y; denom = ty * ex - tx * ey; if (denom == 0.0) { logger.Error("Attempt to find intersection of parallel segments.", "Mesh.SegmentIntersection()"); throw new Exception("Attempt to find intersection of parallel segments."); } split = (ey * etx - ex * ety) / denom; // Create the new vertex. newvertex = new Vertex( torg.x + split * (tdest.x - torg.x), torg.y + split * (tdest.y - torg.y), splitsubseg.seg.boundary, this.nextras); newvertex.hash = this.hash_vtx++; newvertex.id = newvertex.hash; // Interpolate its attributes. for (int i = 0; i < nextras; i++) { newvertex.attributes[i] = torg.attributes[i] + split * (tdest.attributes[i] - torg.attributes[i]); } vertices.Add(newvertex.hash, newvertex); // Insert the intersection vertex. This should always succeed. success = InsertVertex(newvertex, ref splittri, ref splitsubseg, false, false); if (success != InsertVertexResult.Successful) { logger.Error("Failure to split a segment.", "Mesh.SegmentIntersection()"); throw new Exception("Failure to split a segment."); } // Record a triangle whose origin is the new vertex. newvertex.tri = splittri; if (steinerleft > 0) { steinerleft--; } // Divide the segment into two, and correct the segment endpoints. splitsubseg.SymSelf(); splitsubseg.Pivot(ref opposubseg); splitsubseg.Dissolve(); opposubseg.Dissolve(); do { splitsubseg.SetSegOrg(newvertex); splitsubseg.NextSelf(); } while (splitsubseg.seg != Mesh.dummysub); do { opposubseg.SetSegOrg(newvertex); opposubseg.NextSelf(); } while (opposubseg.seg != Mesh.dummysub); // Inserting the vertex may have caused edge flips. We wish to rediscover // the edge connecting endpoint1 to the new intersection vertex. FindDirection(ref splittri, endpoint1); rightvertex = splittri.Dest(); leftvertex = splittri.Apex(); if ((leftvertex.x == endpoint1.x) && (leftvertex.y == endpoint1.y)) { splittri.OnextSelf(); } else if ((rightvertex.x != endpoint1.x) || (rightvertex.y != endpoint1.y)) { logger.Error("Topological inconsistency after splitting a segment.", "Mesh.SegmentIntersection()"); throw new Exception("Topological inconsistency after splitting a segment."); } // 'splittri' should have destination endpoint1. }
/// <summary> /// Insert a vertex into a Delaunay triangulation, performing flips as necessary /// to maintain the Delaunay property. /// </summary> /// <param name="newvertex">The point to be inserted.</param> /// <param name="searchtri">The triangle to start the search.</param> /// <param name="splitseg">Segment to split.</param> /// <param name="segmentflaws">Check for creation of encroached subsegments.</param> /// <param name="triflaws">Check for creation of bad quality triangles.</param> /// <returns>If a duplicate vertex or violated segment does not prevent the /// vertex from being inserted, the return value will be ENCROACHINGVERTEX if /// the vertex encroaches upon a subsegment (and checking is enabled), or /// SUCCESSFULVERTEX otherwise. In either case, 'searchtri' is set to a handle /// whose origin is the newly inserted vertex.</returns> /// <remarks> /// The point 'newvertex' is located. If 'searchtri.triangle' is not NULL, /// the search for the containing triangle begins from 'searchtri'. If /// 'searchtri.triangle' is NULL, a full point location procedure is called. /// If 'insertvertex' is found inside a triangle, the triangle is split into /// three; if 'insertvertex' lies on an edge, the edge is split in two, /// thereby splitting the two adjacent triangles into four. Edge flips are /// used to restore the Delaunay property. If 'insertvertex' lies on an /// existing vertex, no action is taken, and the value DUPLICATEVERTEX is /// returned. On return, 'searchtri' is set to a handle whose origin is the /// existing vertex. /// /// InsertVertex() does not use flip() for reasons of speed; some /// information can be reused from edge flip to edge flip, like the /// locations of subsegments. /// /// Param 'splitseg': Normally, the parameter 'splitseg' is set to NULL, /// implying that no subsegment should be split. In this case, if 'insertvertex' /// is found to lie on a segment, no action is taken, and the value VIOLATINGVERTEX /// is returned. On return, 'searchtri' is set to a handle whose primary edge is the /// violated subsegment. /// If the calling routine wishes to split a subsegment by inserting a vertex in it, /// the parameter 'splitseg' should be that subsegment. In this case, 'searchtri' /// MUST be the triangle handle reached by pivoting from that subsegment; no point /// location is done. /// /// Param 'segmentflaws': Flags that indicate whether or not there should /// be checks for the creation of encroached subsegments. If a newly inserted /// vertex encroaches upon subsegments, these subsegments are added to the list /// of subsegments to be split if 'segmentflaws' is set. /// /// Param 'triflaws': Flags that indicate whether or not there should be /// checks for the creation of bad quality triangles. If bad triangles are /// created, these are added to the queue if 'triflaws' is set. /// </remarks> internal InsertVertexResult InsertVertex(Vertex newvertex, ref Otri searchtri, ref Osub splitseg, bool segmentflaws, bool triflaws) { Otri horiz = default(Otri); Otri top = default(Otri); Otri botleft = default(Otri), botright = default(Otri); Otri topleft = default(Otri), topright = default(Otri); Otri newbotleft = default(Otri), newbotright = default(Otri); Otri newtopright = default(Otri); Otri botlcasing = default(Otri), botrcasing = default(Otri); Otri toplcasing = default(Otri), toprcasing = default(Otri); Otri testtri = default(Otri); Osub botlsubseg = default(Osub), botrsubseg = default(Osub); Osub toplsubseg = default(Osub), toprsubseg = default(Osub); Osub brokensubseg = default(Osub); Osub checksubseg = default(Osub); Osub rightsubseg = default(Osub); Osub newsubseg = default(Osub); BadSubseg encroached; //FlipStacker newflip; Vertex first; Vertex leftvertex, rightvertex, botvertex, topvertex, farvertex; Vertex segmentorg, segmentdest; int region; double area; InsertVertexResult success; LocateResult intersect; bool doflip; bool mirrorflag; bool enq; if (splitseg.seg == null) { // Find the location of the vertex to be inserted. Check if a good // starting triangle has already been provided by the caller. if (searchtri.triangle == dummytri) { // Find a boundary triangle. horiz.triangle = dummytri; horiz.orient = 0; horiz.SymSelf(); // Search for a triangle containing 'newvertex'. intersect = locator.Locate(newvertex, ref horiz); } else { // Start searching from the triangle provided by the caller. searchtri.Copy(ref horiz); intersect = locator.PreciseLocate(newvertex, ref horiz, true); } } else { // The calling routine provides the subsegment in which // the vertex is inserted. searchtri.Copy(ref horiz); intersect = LocateResult.OnEdge; } if (intersect == LocateResult.OnVertex) { // There's already a vertex there. Return in 'searchtri' a triangle // whose origin is the existing vertex. horiz.Copy(ref searchtri); locator.Update(ref horiz); return InsertVertexResult.Duplicate; } if ((intersect == LocateResult.OnEdge) || (intersect == LocateResult.Outside)) { // The vertex falls on an edge or boundary. if (checksegments && (splitseg.seg == null)) { // Check whether the vertex falls on a subsegment. horiz.SegPivot(ref brokensubseg); if (brokensubseg.seg != dummysub) { // The vertex falls on a subsegment, and hence will not be inserted. if (segmentflaws) { enq = behavior.NoBisect != 2; if (enq && (behavior.NoBisect == 1)) { // This subsegment may be split only if it is an // internal boundary. horiz.Sym(ref testtri); enq = testtri.triangle != dummytri; } if (enq) { // Add the subsegment to the list of encroached subsegments. encroached = new BadSubseg(); encroached.encsubseg = brokensubseg; encroached.subsegorg = brokensubseg.Org(); encroached.subsegdest = brokensubseg.Dest(); quality.AddBadSubseg(encroached); } } // Return a handle whose primary edge contains the vertex, // which has not been inserted. horiz.Copy(ref searchtri); locator.Update(ref horiz); return InsertVertexResult.Violating; } } // Insert the vertex on an edge, dividing one triangle into two (if // the edge lies on a boundary) or two triangles into four. horiz.Lprev(ref botright); botright.Sym(ref botrcasing); horiz.Sym(ref topright); // Is there a second triangle? (Or does this edge lie on a boundary?) mirrorflag = topright.triangle != dummytri; if (mirrorflag) { topright.LnextSelf(); topright.Sym(ref toprcasing); MakeTriangle(ref newtopright); } else { // Splitting a boundary edge increases the number of boundary edges. hullsize++; } MakeTriangle(ref newbotright); // Set the vertices of changed and new triangles. rightvertex = horiz.Org(); leftvertex = horiz.Dest(); botvertex = horiz.Apex(); newbotright.SetOrg(botvertex); newbotright.SetDest(rightvertex); newbotright.SetApex(newvertex); horiz.SetOrg(newvertex); // Set the region of a new triangle. newbotright.triangle.region = botright.triangle.region; if (behavior.VarArea) { // Set the area constraint of a new triangle. newbotright.triangle.area = botright.triangle.area; } if (mirrorflag) { topvertex = topright.Dest(); newtopright.SetOrg(rightvertex); newtopright.SetDest(topvertex); newtopright.SetApex(newvertex); topright.SetOrg(newvertex); // Set the region of another new triangle. newtopright.triangle.region = topright.triangle.region; if (behavior.VarArea) { // Set the area constraint of another new triangle. newtopright.triangle.area = topright.triangle.area; } } // There may be subsegments that need to be bonded // to the new triangle(s). if (checksegments) { botright.SegPivot(ref botrsubseg); if (botrsubseg.seg != dummysub) { botright.SegDissolve(); newbotright.SegBond(ref botrsubseg); } if (mirrorflag) { topright.SegPivot(ref toprsubseg); if (toprsubseg.seg != dummysub) { topright.SegDissolve(); newtopright.SegBond(ref toprsubseg); } } } // Bond the new triangle(s) to the surrounding triangles. newbotright.Bond(ref botrcasing); newbotright.LprevSelf(); newbotright.Bond(ref botright); newbotright.LprevSelf(); if (mirrorflag) { newtopright.Bond(ref toprcasing); newtopright.LnextSelf(); newtopright.Bond(ref topright); newtopright.LnextSelf(); newtopright.Bond(ref newbotright); } if (splitseg.seg != null) { // Split the subsegment into two. splitseg.SetDest(newvertex); segmentorg = splitseg.SegOrg(); segmentdest = splitseg.SegDest(); splitseg.SymSelf(); splitseg.Pivot(ref rightsubseg); InsertSubseg(ref newbotright, splitseg.seg.boundary); newbotright.SegPivot(ref newsubseg); newsubseg.SetSegOrg(segmentorg); newsubseg.SetSegDest(segmentdest); splitseg.Bond(ref newsubseg); newsubseg.SymSelf(); newsubseg.Bond(ref rightsubseg); splitseg.SymSelf(); // Transfer the subsegment's boundary marker to the vertex if required. if (newvertex.mark == 0) { newvertex.mark = splitseg.seg.boundary; } } if (checkquality) { flipstack.Clear(); flipstack.Push(default(Otri)); // Dummy flip (see UndoVertex) flipstack.Push(horiz); } // Position 'horiz' on the first edge to check for // the Delaunay property. horiz.LnextSelf(); } else { // Insert the vertex in a triangle, splitting it into three. horiz.Lnext(ref botleft); horiz.Lprev(ref botright); botleft.Sym(ref botlcasing); botright.Sym(ref botrcasing); MakeTriangle(ref newbotleft); MakeTriangle(ref newbotright); // Set the vertices of changed and new triangles. rightvertex = horiz.Org(); leftvertex = horiz.Dest(); botvertex = horiz.Apex(); newbotleft.SetOrg(leftvertex); newbotleft.SetDest(botvertex); newbotleft.SetApex(newvertex); newbotright.SetOrg(botvertex); newbotright.SetDest(rightvertex); newbotright.SetApex(newvertex); horiz.SetApex(newvertex); // Set the region of the new triangles. newbotleft.triangle.region = horiz.triangle.region; newbotright.triangle.region = horiz.triangle.region; if (behavior.VarArea) { // Set the area constraint of the new triangles. area = horiz.triangle.area; newbotleft.triangle.area = area; newbotright.triangle.area = area; } // There may be subsegments that need to be bonded // to the new triangles. if (checksegments) { botleft.SegPivot(ref botlsubseg); if (botlsubseg.seg != dummysub) { botleft.SegDissolve(); newbotleft.SegBond(ref botlsubseg); } botright.SegPivot(ref botrsubseg); if (botrsubseg.seg != dummysub) { botright.SegDissolve(); newbotright.SegBond(ref botrsubseg); } } // Bond the new triangles to the surrounding triangles. newbotleft.Bond(ref botlcasing); newbotright.Bond(ref botrcasing); newbotleft.LnextSelf(); newbotright.LprevSelf(); newbotleft.Bond(ref newbotright); newbotleft.LnextSelf(); botleft.Bond(ref newbotleft); newbotright.LprevSelf(); botright.Bond(ref newbotright); if (checkquality) { flipstack.Clear(); flipstack.Push(horiz); } } // The insertion is successful by default, unless an encroached // subsegment is found. success = InsertVertexResult.Successful; // Circle around the newly inserted vertex, checking each edge opposite it // for the Delaunay property. Non-Delaunay edges are flipped. 'horiz' is // always the edge being checked. 'first' marks where to stop circling. first = horiz.Org(); rightvertex = first; leftvertex = horiz.Dest(); // Circle until finished. while (true) { // By default, the edge will be flipped. doflip = true; if (checksegments) { // Check for a subsegment, which cannot be flipped. horiz.SegPivot(ref checksubseg); if (checksubseg.seg != dummysub) { // The edge is a subsegment and cannot be flipped. doflip = false; if (segmentflaws) { // Does the new vertex encroach upon this subsegment? if (quality.CheckSeg4Encroach(ref checksubseg) > 0) { success = InsertVertexResult.Encroaching; } } } } if (doflip) { // Check if the edge is a boundary edge. horiz.Sym(ref top); if (top.triangle == dummytri) { // The edge is a boundary edge and cannot be flipped. doflip = false; } else { // Find the vertex on the other side of the edge. farvertex = top.Apex(); // In the incremental Delaunay triangulation algorithm, any of // 'leftvertex', 'rightvertex', and 'farvertex' could be vertices // of the triangular bounding box. These vertices must be // treated as if they are infinitely distant, even though their // "coordinates" are not. if ((leftvertex == infvertex1) || (leftvertex == infvertex2) || (leftvertex == infvertex3)) { // 'leftvertex' is infinitely distant. Check the convexity of // the boundary of the triangulation. 'farvertex' might be // infinite as well, but trust me, this same condition should // be applied. doflip = Primitives.CounterClockwise(newvertex, rightvertex, farvertex) > 0.0; } else if ((rightvertex == infvertex1) || (rightvertex == infvertex2) || (rightvertex == infvertex3)) { // 'rightvertex' is infinitely distant. Check the convexity of // the boundary of the triangulation. 'farvertex' might be // infinite as well, but trust me, this same condition should // be applied. doflip = Primitives.CounterClockwise(farvertex, leftvertex, newvertex) > 0.0; } else if ((farvertex == infvertex1) || (farvertex == infvertex2) || (farvertex == infvertex3)) { // 'farvertex' is infinitely distant and cannot be inside // the circumcircle of the triangle 'horiz'. doflip = false; } else { // Test whether the edge is locally Delaunay. doflip = Primitives.InCircle(leftvertex, newvertex, rightvertex, farvertex) > 0.0; } if (doflip) { // We made it! Flip the edge 'horiz' by rotating its containing // quadrilateral (the two triangles adjacent to 'horiz'). // Identify the casing of the quadrilateral. top.Lprev(ref topleft); topleft.Sym(ref toplcasing); top.Lnext(ref topright); topright.Sym(ref toprcasing); horiz.Lnext(ref botleft); botleft.Sym(ref botlcasing); horiz.Lprev(ref botright); botright.Sym(ref botrcasing); // Rotate the quadrilateral one-quarter turn counterclockwise. topleft.Bond(ref botlcasing); botleft.Bond(ref botrcasing); botright.Bond(ref toprcasing); topright.Bond(ref toplcasing); if (checksegments) { // Check for subsegments and rebond them to the quadrilateral. topleft.SegPivot(ref toplsubseg); botleft.SegPivot(ref botlsubseg); botright.SegPivot(ref botrsubseg); topright.SegPivot(ref toprsubseg); if (toplsubseg.seg == dummysub) { topright.SegDissolve(); } else { topright.SegBond(ref toplsubseg); } if (botlsubseg.seg == dummysub) { topleft.SegDissolve(); } else { topleft.SegBond(ref botlsubseg); } if (botrsubseg.seg == dummysub) { botleft.SegDissolve(); } else { botleft.SegBond(ref botrsubseg); } if (toprsubseg.seg == dummysub) { botright.SegDissolve(); } else { botright.SegBond(ref toprsubseg); } } // New vertex assignments for the rotated quadrilateral. horiz.SetOrg(farvertex); horiz.SetDest(newvertex); horiz.SetApex(rightvertex); top.SetOrg(newvertex); top.SetDest(farvertex); top.SetApex(leftvertex); // Assign region. // TODO: check region ok (no Math.Min necessary) region = Math.Min(top.triangle.region, horiz.triangle.region); top.triangle.region = region; horiz.triangle.region = region; if (behavior.VarArea) { if ((top.triangle.area <= 0.0) || (horiz.triangle.area <= 0.0)) { area = -1.0; } else { // Take the average of the two triangles' area constraints. // This prevents small area constraints from migrating a // long, long way from their original location due to flips. area = 0.5 * (top.triangle.area + horiz.triangle.area); } top.triangle.area = area; horiz.triangle.area = area; } if (checkquality) { flipstack.Push(horiz); } // On the next iterations, consider the two edges that were exposed (this // is, are now visible to the newly inserted vertex) by the edge flip. horiz.LprevSelf(); leftvertex = farvertex; } } } if (!doflip) { // The handle 'horiz' is accepted as locally Delaunay. if (triflaws) { // Check the triangle 'horiz' for quality. quality.TestTriangle(ref horiz); } // Look for the next edge around the newly inserted vertex. horiz.LnextSelf(); horiz.Sym(ref testtri); // Check for finishing a complete revolution about the new vertex, or // falling outside of the triangulation. The latter will happen when // a vertex is inserted at a boundary. if ((leftvertex == first) || (testtri.triangle == dummytri)) { // We're done. Return a triangle whose origin is the new vertex. horiz.Lnext(ref searchtri); Otri recenttri = default(Otri); horiz.Lnext(ref recenttri); locator.Update(ref recenttri); return success; } // Finish finding the next edge around the newly inserted vertex. testtri.Lnext(ref horiz); rightvertex = leftvertex; leftvertex = horiz.Dest(); } } }
/// <summary> /// Delete a vertex from a Delaunay triangulation, ensuring that the /// triangulation remains Delaunay. /// </summary> /// <param name="deltri"></param> /// <remarks>The origin of 'deltri' is deleted. The union of the triangles /// adjacent to this vertex is a polygon, for which the Delaunay triangulation /// is found. Two triangles are removed from the mesh. /// /// Only interior vertices that do not lie on segments or boundaries /// may be deleted. /// </remarks> internal void DeleteVertex(ref Otri deltri) { Otri countingtri = default(Otri); Otri firstedge = default(Otri), lastedge = default(Otri); Otri deltriright = default(Otri); Otri lefttri = default(Otri), righttri = default(Otri); Otri leftcasing = default(Otri), rightcasing = default(Otri); Osub leftsubseg = default(Osub), rightsubseg = default(Osub); Vertex delvertex; Vertex neworg; int edgecount; delvertex = deltri.Org(); VertexDealloc(delvertex); // Count the degree of the vertex being deleted. deltri.Onext(ref countingtri); edgecount = 1; while (!deltri.Equal(countingtri)) { edgecount++; countingtri.OnextSelf(); } if (edgecount > 3) { // Triangulate the polygon defined by the union of all triangles // adjacent to the vertex being deleted. Check the quality of // the resulting triangles. deltri.Onext(ref firstedge); deltri.Oprev(ref lastedge); TriangulatePolygon(firstedge, lastedge, edgecount, false, behavior.NoBisect == 0); } // Splice out two triangles. deltri.Lprev(ref deltriright); deltri.Dnext(ref lefttri); lefttri.Sym(ref leftcasing); deltriright.Oprev(ref righttri); righttri.Sym(ref rightcasing); deltri.Bond(ref leftcasing); deltriright.Bond(ref rightcasing); lefttri.SegPivot(ref leftsubseg); if (leftsubseg.seg != Mesh.dummysub) { deltri.SegBond(ref leftsubseg); } righttri.SegPivot(ref rightsubseg); if (rightsubseg.seg != Mesh.dummysub) { deltriright.SegBond(ref rightsubseg); } // Set the new origin of 'deltri' and check its quality. neworg = lefttri.Org(); deltri.SetOrg(neworg); if (behavior.NoBisect == 0) { quality.TestTriangle(ref deltri); } // Delete the two spliced-out triangles. TriangleDealloc(lefttri.triangle); TriangleDealloc(righttri.triangle); }
public void Lprev(ref Otri o2) { o2.triangle = this.triangle; o2.orient = Otri.minus1Mod3[this.orient]; }
/// <summary> /// Create a new subsegment and inserts it between two triangles. Its /// vertices are properly initialized. /// </summary> /// <param name="tri">The new subsegment is inserted at the edge /// described by this handle.</param> /// <param name="subsegmark">The marker 'subsegmark' is applied to the /// subsegment and, if appropriate, its vertices.</param> internal void InsertSubseg(ref Otri tri, int subsegmark) { Otri oppotri = default(Otri); Osub newsubseg = default(Osub); Vertex triorg, tridest; triorg = tri.Org(); tridest = tri.Dest(); // Mark vertices if possible. if (triorg.mark == 0) { triorg.mark = subsegmark; } if (tridest.mark == 0) { tridest.mark = subsegmark; } // Check if there's already a subsegment here. tri.SegPivot(ref newsubseg); if (newsubseg.seg == dummysub) { // Make new subsegment and initialize its vertices. MakeSegment(ref newsubseg); newsubseg.SetOrg(tridest); newsubseg.SetDest(triorg); newsubseg.SetSegOrg(tridest); newsubseg.SetSegDest(triorg); // Bond new subsegment to the two triangles it is sandwiched between. // Note that the facing triangle 'oppotri' might be equal to 'dummytri' // (outer space), but the new subsegment is bonded to it all the same. tri.SegBond(ref newsubseg); tri.Sym(ref oppotri); newsubseg.SymSelf(); oppotri.SegBond(ref newsubseg); newsubseg.seg.boundary = subsegmark; } else { if (newsubseg.seg.boundary == 0) { newsubseg.seg.boundary = subsegmark; } } }
/// <summary> /// Find a new location for a Steiner point. /// </summary> /// <param name="torg"></param> /// <param name="tdest"></param> /// <param name="tapex"></param> /// <param name="circumcenter"></param> /// <param name="xi"></param> /// <param name="eta"></param> /// <param name="offcenter"></param> /// <param name="badotri"></param> private Point FindNewLocation(Vertex torg, Vertex tdest, Vertex tapex, ref double xi, ref double eta, bool offcenter, Otri badotri) { double offconstant = behavior.offconstant; // for calculating the distances of the edges double xdo, ydo, xao, yao, xda, yda; double dodist, aodist, dadist; // for exact calculation double denominator; double dx, dy, dxoff, dyoff; ////////////////////////////// HALE'S VARIABLES ////////////////////////////// // keeps the difference of coordinates edge double xShortestEdge = 0, yShortestEdge = 0 /*, xMiddleEdge, yMiddleEdge, xLongestEdge, yLongestEdge*/; // keeps the square of edge lengths double shortestEdgeDist = 0, middleEdgeDist = 0, longestEdgeDist = 0; // keeps the vertices according to the angle incident to that vertex in a triangle Point smallestAngleCorner, middleAngleCorner, largestAngleCorner; // keeps the type of orientation if the triangle int orientation = 0; // keeps the coordinates of circumcenter of itself and neighbor triangle circumcenter Point myCircumcenter, neighborCircumcenter; // keeps if bad triangle is almost good or not int almostGood = 0; // keeps the cosine of the largest angle double cosMaxAngle; bool isObtuse; // 1: obtuse 0: nonobtuse // keeps the radius of petal double petalRadius; // for calculating petal center double xPetalCtr_1, yPetalCtr_1, xPetalCtr_2, yPetalCtr_2, xPetalCtr, yPetalCtr, xMidOfShortestEdge, yMidOfShortestEdge; double dxcenter1, dycenter1, dxcenter2, dycenter2; // for finding neighbor Otri neighborotri = default(Otri); double[] thirdPoint = new double[2]; //int neighborNotFound = -1; // for keeping the vertices of the neighbor triangle Vertex neighborvertex_1; Vertex neighborvertex_2; Vertex neighborvertex_3; // dummy variables double xi_tmp = 0, eta_tmp = 0; //vertex thirdVertex; // for petal intersection double vector_x, vector_y, xMidOfLongestEdge, yMidOfLongestEdge, inter_x, inter_y; double[] p = new double[5], voronoiOrInter = new double[4]; bool isCorrect; // for vector calculations in perturbation double ax, ay, d; double pertConst = 0.06; // perturbation constant double lengthConst = 1; // used at comparing circumcenter's distance to proposed point's distance double justAcute = 1; // used for making the program working for one direction only // for smoothing int relocated = 0;// used to differentiate between calling the deletevertex and just proposing a steiner point double[] newloc = new double[2]; // new location suggested by smoothing double origin_x = 0, origin_y = 0; // for keeping torg safe Otri delotri; // keeping the original orientation for relocation process // keeps the first and second direction suggested points double dxFirstSuggestion, dyFirstSuggestion, dxSecondSuggestion, dySecondSuggestion; // second direction variables double xMidOfMiddleEdge, yMidOfMiddleEdge; double minangle; // in order to make sure that the circumcircle of the bad triangle is greater than petal // for calculating the slab double linepnt1_x, linepnt1_y, linepnt2_x, linepnt2_y; // two points of the line double line_inter_x = 0, line_inter_y = 0; double line_vector_x, line_vector_y; double[] line_p = new double[3]; // used for getting the return values of functions related to line intersection double[] line_result = new double[4]; // intersection of slab and the petal double petal_slab_inter_x_first, petal_slab_inter_y_first, petal_slab_inter_x_second, petal_slab_inter_y_second, x_1, y_1, x_2, y_2; double petal_bisector_x, petal_bisector_y, dist; double alpha; bool neighborNotFound_first; bool neighborNotFound_second; ////////////////////////////// END OF HALE'S VARIABLES ////////////////////////////// Statistic.CircumcenterCount++; // Compute the circumcenter of the triangle. xdo = tdest.x - torg.x; ydo = tdest.y - torg.y; xao = tapex.x - torg.x; yao = tapex.y - torg.y; xda = tapex.x - tdest.x; yda = tapex.y - tdest.y; // keeps the square of the distances dodist = xdo * xdo + ydo * ydo; aodist = xao * xao + yao * yao; dadist = (tdest.x - tapex.x) * (tdest.x - tapex.x) + (tdest.y - tapex.y) * (tdest.y - tapex.y); // checking if the user wanted exact arithmetic or not if (Behavior.NoExact) { denominator = 0.5 / (xdo * yao - xao * ydo); } else { // Use the counterclockwise() routine to ensure a positive (and // reasonably accurate) result, avoiding any possibility of // division by zero. denominator = 0.5 / Primitives.CounterClockwise(tdest, tapex, torg); // Don't count the above as an orientation test. Statistic.CounterClockwiseCount--; } // calculate the circumcenter in terms of distance to origin point dx = (yao * dodist - ydo * aodist) * denominator; dy = (xdo * aodist - xao * dodist) * denominator; // for debugging and for keeping circumcenter to use later // coordinate value of the circumcenter myCircumcenter = new Point(torg.x + dx, torg.y + dy); delotri = badotri; // save for later ///////////////// FINDING THE ORIENTATION OF TRIANGLE ////////////////// // Find the (squared) length of the triangle's shortest edge. This // serves as a conservative estimate of the insertion radius of the // circumcenter's parent. The estimate is used to ensure that // the algorithm terminates even if very small angles appear in // the input PSLG. // find the orientation of the triangle, basically shortest and longest edges orientation = LongestShortestEdge(aodist, dadist, dodist); //printf("org: (%f,%f), dest: (%f,%f), apex: (%f,%f)\n",torg[0],torg[1],tdest[0],tdest[1],tapex[0],tapex[1]); ///////////////////////////////////////////////////////////////////////////////////////////// // 123: shortest: aodist // 213: shortest: dadist // 312: shortest: dodist // // middle: dadist // middle: aodist // middle: aodist // // longest: dodist // longest: dodist // longest: dadist // // 132: shortest: aodist // 231: shortest: dadist // 321: shortest: dodist // // middle: dodist // middle: dodist // middle: dadist // // longest: dadist // longest: aodist // longest: aodist // ///////////////////////////////////////////////////////////////////////////////////////////// switch (orientation) { case 123: // assign necessary information /// smallest angle corner: dest /// largest angle corner: apex xShortestEdge = xao; yShortestEdge = yao; //xMiddleEdge = xda; yMiddleEdge = yda; //xLongestEdge = xdo; yLongestEdge = ydo; shortestEdgeDist = aodist; middleEdgeDist = dadist; longestEdgeDist = dodist; smallestAngleCorner = tdest; middleAngleCorner = torg; largestAngleCorner = tapex; break; case 132: // assign necessary information /// smallest angle corner: dest /// largest angle corner: org xShortestEdge = xao; yShortestEdge = yao; //xMiddleEdge = xdo; yMiddleEdge = ydo; //xLongestEdge = xda; yLongestEdge = yda; shortestEdgeDist = aodist; middleEdgeDist = dodist; longestEdgeDist = dadist; smallestAngleCorner = tdest; middleAngleCorner = tapex; largestAngleCorner = torg; break; case 213: // assign necessary information /// smallest angle corner: org /// largest angle corner: apex xShortestEdge = xda; yShortestEdge = yda; //xMiddleEdge = xao; yMiddleEdge = yao; //xLongestEdge = xdo; yLongestEdge = ydo; shortestEdgeDist = dadist; middleEdgeDist = aodist; longestEdgeDist = dodist; smallestAngleCorner = torg; middleAngleCorner = tdest; largestAngleCorner = tapex; break; case 231: // assign necessary information /// smallest angle corner: org /// largest angle corner: dest xShortestEdge = xda; yShortestEdge = yda; //xMiddleEdge = xdo; yMiddleEdge = ydo; //xLongestEdge = xao; yLongestEdge = yao; shortestEdgeDist = dadist; middleEdgeDist = dodist; longestEdgeDist = aodist; smallestAngleCorner = torg; middleAngleCorner = tapex; largestAngleCorner = tdest; break; case 312: // assign necessary information /// smallest angle corner: apex /// largest angle corner: org xShortestEdge = xdo; yShortestEdge = ydo; //xMiddleEdge = xao; yMiddleEdge = yao; //xLongestEdge = xda; yLongestEdge = yda; shortestEdgeDist = dodist; middleEdgeDist = aodist; longestEdgeDist = dadist; smallestAngleCorner = tapex; middleAngleCorner = tdest; largestAngleCorner = torg; break; case 321: // assign necessary information default: // TODO: is this safe? /// smallest angle corner: apex /// largest angle corner: dest xShortestEdge = xdo; yShortestEdge = ydo; //xMiddleEdge = xda; yMiddleEdge = yda; //xLongestEdge = xao; yLongestEdge = yao; shortestEdgeDist = dodist; middleEdgeDist = dadist; longestEdgeDist = aodist; smallestAngleCorner = tapex; middleAngleCorner = torg; largestAngleCorner = tdest; break; }// end of switch // check for offcenter condition if (offcenter && (offconstant > 0.0)) { // origin has the smallest angle if (orientation == 213 || orientation == 231) { // Find the position of the off-center, as described by Alper Ungor. dxoff = 0.5 * xShortestEdge - offconstant * yShortestEdge; dyoff = 0.5 * yShortestEdge + offconstant * xShortestEdge; // If the off-center is closer to destination than the // circumcenter, use the off-center instead. /// doubleLY BAD CASE /// if (dxoff * dxoff + dyoff * dyoff < (dx - xdo) * (dx - xdo) + (dy - ydo) * (dy - ydo)) { dx = xdo + dxoff; dy = ydo + dyoff; } /// ALMOST GOOD CASE /// else { almostGood = 1; } // destination has the smallest angle } else if (orientation == 123 || orientation == 132) { // Find the position of the off-center, as described by Alper Ungor. dxoff = 0.5 * xShortestEdge + offconstant * yShortestEdge; dyoff = 0.5 * yShortestEdge - offconstant * xShortestEdge; // If the off-center is closer to the origin than the // circumcenter, use the off-center instead. /// doubleLY BAD CASE /// if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) { dx = dxoff; dy = dyoff; } /// ALMOST GOOD CASE /// else { almostGood = 1; } // apex has the smallest angle } else {//orientation == 312 || orientation == 321 // Find the position of the off-center, as described by Alper Ungor. dxoff = 0.5 * xShortestEdge - offconstant * yShortestEdge; dyoff = 0.5 * yShortestEdge + offconstant * xShortestEdge; // If the off-center is closer to the origin than the // circumcenter, use the off-center instead. /// doubleLY BAD CASE /// if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) { dx = dxoff; dy = dyoff; } /// ALMOST GOOD CASE /// else { almostGood = 1; } } } // if the bad triangle is almost good, apply our approach if (almostGood == 1) { /// calculate cosine of largest angle /// cosMaxAngle = (middleEdgeDist + shortestEdgeDist - longestEdgeDist) / (2 * Math.Sqrt(middleEdgeDist) * Math.Sqrt(shortestEdgeDist)); if (cosMaxAngle < 0.0) { // obtuse isObtuse = true; } else if (Math.Abs(cosMaxAngle - 0.0) <= EPS) { // right triangle (largest angle is 90 degrees) isObtuse = true; } else { // nonobtuse isObtuse = false; } /// RELOCATION (LOCAL SMOOTHING) /// /// check for possible relocation of one of triangle's points /// relocated = DoSmoothing(delotri, torg, tdest, tapex, ref newloc); /// if relocation is possible, delete that vertex and insert a vertex at the new location /// if (relocated > 0) { Statistic.RelocationCount++; dx = newloc[0] - torg.x; dy = newloc[1] - torg.y; origin_x = torg.x; // keep for later use origin_y = torg.y; switch (relocated) { case 1: //printf("Relocate: (%f,%f)\n", torg[0],torg[1]); mesh.DeleteVertex(ref delotri); break; case 2: //printf("Relocate: (%f,%f)\n", tdest[0],tdest[1]); delotri.LnextSelf(); mesh.DeleteVertex(ref delotri); break; case 3: //printf("Relocate: (%f,%f)\n", tapex[0],tapex[1]); delotri.LprevSelf(); mesh.DeleteVertex(ref delotri); break; } } else { // calculate radius of the petal according to angle constraint // first find the visible region, PETAL // find the center of the circle and radius // choose minimum angle as the maximum of quality angle and the minimum angle of the bad triangle minangle = Math.Acos((middleEdgeDist + longestEdgeDist - shortestEdgeDist) / (2 * Math.Sqrt(middleEdgeDist) * Math.Sqrt(longestEdgeDist))) * 180.0 / Math.PI; if (behavior.MinAngle > minangle) { minangle = behavior.MinAngle; } else { minangle = minangle + 0.5; } petalRadius = Math.Sqrt(shortestEdgeDist) / (2 * Math.Sin(minangle * Math.PI / 180.0)); /// compute two possible centers of the petal /// // finding the center // first find the middle point of smallest edge xMidOfShortestEdge = (middleAngleCorner.x + largestAngleCorner.x) / 2.0; yMidOfShortestEdge = (middleAngleCorner.y + largestAngleCorner.y) / 2.0; // two possible centers xPetalCtr_1 = xMidOfShortestEdge + Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.y - largestAngleCorner.y) / Math.Sqrt(shortestEdgeDist); yPetalCtr_1 = yMidOfShortestEdge + Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.x - middleAngleCorner.x) / Math.Sqrt(shortestEdgeDist); xPetalCtr_2 = xMidOfShortestEdge - Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.y - largestAngleCorner.y) / Math.Sqrt(shortestEdgeDist); yPetalCtr_2 = yMidOfShortestEdge - Math.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.x - middleAngleCorner.x) / Math.Sqrt(shortestEdgeDist); // find the correct circle since there will be two possible circles // calculate the distance to smallest angle corner dxcenter1 = (xPetalCtr_1 - smallestAngleCorner.x) * (xPetalCtr_1 - smallestAngleCorner.x); dycenter1 = (yPetalCtr_1 - smallestAngleCorner.y) * (yPetalCtr_1 - smallestAngleCorner.y); dxcenter2 = (xPetalCtr_2 - smallestAngleCorner.x) * (xPetalCtr_2 - smallestAngleCorner.x); dycenter2 = (yPetalCtr_2 - smallestAngleCorner.y) * (yPetalCtr_2 - smallestAngleCorner.y); // whichever is closer to smallest angle corner, it must be the center if (dxcenter1 + dycenter1 <= dxcenter2 + dycenter2) { xPetalCtr = xPetalCtr_1; yPetalCtr = yPetalCtr_1; } else { xPetalCtr = xPetalCtr_2; yPetalCtr = yPetalCtr_2; } /// find the third point of the neighbor triangle /// neighborNotFound_first = GetNeighborsVertex(badotri, middleAngleCorner.x, middleAngleCorner.y, smallestAngleCorner.x, smallestAngleCorner.y, ref thirdPoint, ref neighborotri); /// find the circumcenter of the neighbor triangle /// dxFirstSuggestion = dx; // if we cannot find any appropriate suggestion, we use circumcenter dyFirstSuggestion = dy; /// before checking the neighbor, find the petal and slab intersections /// // calculate the intersection point of the petal and the slab lines // first find the vector // distance between xmid and petal center dist = Math.Sqrt((xPetalCtr - xMidOfShortestEdge) * (xPetalCtr - xMidOfShortestEdge) + (yPetalCtr - yMidOfShortestEdge) * (yPetalCtr - yMidOfShortestEdge)); // find the unit vector goes from mid point to petal center line_vector_x = (xPetalCtr - xMidOfShortestEdge) / dist; line_vector_y = (yPetalCtr - yMidOfShortestEdge) / dist; // find the third point other than p and q petal_bisector_x = xPetalCtr + line_vector_x * petalRadius; petal_bisector_y = yPetalCtr + line_vector_y * petalRadius; alpha = (2.0 * behavior.MaxAngle + minangle - 180.0) * Math.PI / 180.0; // rotate the vector cw around the petal center x_1 = petal_bisector_x * Math.Cos(alpha) + petal_bisector_y * Math.Sin(alpha) + xPetalCtr - xPetalCtr * Math.Cos(alpha) - yPetalCtr * Math.Sin(alpha); y_1 = -petal_bisector_x * Math.Sin(alpha) + petal_bisector_y * Math.Cos(alpha) + yPetalCtr + xPetalCtr * Math.Sin(alpha) - yPetalCtr * Math.Cos(alpha); // rotate the vector ccw around the petal center x_2 = petal_bisector_x * Math.Cos(alpha) - petal_bisector_y * Math.Sin(alpha) + xPetalCtr - xPetalCtr * Math.Cos(alpha) + yPetalCtr * Math.Sin(alpha); y_2 = petal_bisector_x * Math.Sin(alpha) + petal_bisector_y * Math.Cos(alpha) + yPetalCtr - xPetalCtr * Math.Sin(alpha) - yPetalCtr * Math.Cos(alpha); // we need to find correct intersection point, since there are two possibilities // weather it is obtuse/acute the one closer to the minimum angle corner is the first direction isCorrect = ChooseCorrectPoint(x_2, y_2, middleAngleCorner.x, middleAngleCorner.y, x_1, y_1, true); // make sure which point is the correct one to be considered if (isCorrect) { petal_slab_inter_x_first = x_1; petal_slab_inter_y_first = y_1; petal_slab_inter_x_second = x_2; petal_slab_inter_y_second = y_2; } else { petal_slab_inter_x_first = x_2; petal_slab_inter_y_first = y_2; petal_slab_inter_x_second = x_1; petal_slab_inter_y_second = y_1; } /// choose the correct intersection point /// // calculate middle point of the longest edge(bisector) xMidOfLongestEdge = (middleAngleCorner.x + smallestAngleCorner.x) / 2.0; yMidOfLongestEdge = (middleAngleCorner.y + smallestAngleCorner.y) / 2.0; // if there is a neighbor triangle if (!neighborNotFound_first) { neighborvertex_1 = neighborotri.Org(); neighborvertex_2 = neighborotri.Dest(); neighborvertex_3 = neighborotri.Apex(); // now calculate neighbor's circumcenter which is the voronoi site neighborCircumcenter = Primitives.FindCircumcenter(neighborvertex_1, neighborvertex_2, neighborvertex_3, ref xi_tmp, ref eta_tmp); /// compute petal and Voronoi edge intersection /// // in order to avoid degenerate cases, we need to do a vector based calculation for line vector_x = (middleAngleCorner.y - smallestAngleCorner.y);//(-y, x) vector_y = smallestAngleCorner.x - middleAngleCorner.x; vector_x = myCircumcenter.x + vector_x; vector_y = myCircumcenter.y + vector_y; // by intersecting bisectors you will end up with the one you want to walk on // then this line and circle should be intersected CircleLineIntersection(myCircumcenter.x, myCircumcenter.y, vector_x, vector_y, xPetalCtr, yPetalCtr, petalRadius, ref p); // we need to find correct intersection point, since line intersects circle twice isCorrect = ChooseCorrectPoint(xMidOfLongestEdge, yMidOfLongestEdge, p[3], p[4], myCircumcenter.x, myCircumcenter.y, isObtuse); // make sure which point is the correct one to be considered if (isCorrect) { inter_x = p[3]; inter_y = p[4]; } else { inter_x = p[1]; inter_y = p[2]; } //----------------------hale new first direction: for slab calculation---------------// // calculate the intersection of angle lines and Voronoi linepnt1_x = middleAngleCorner.x; linepnt1_y = middleAngleCorner.y; // vector from middleAngleCorner to largestAngleCorner line_vector_x = largestAngleCorner.x - middleAngleCorner.x; line_vector_y = largestAngleCorner.y - middleAngleCorner.y; // rotate the vector around middleAngleCorner in cw by maxangle degrees linepnt2_x = petal_slab_inter_x_first; linepnt2_y = petal_slab_inter_y_first; // now calculate the intersection of two lines LineLineIntersection(myCircumcenter.x, myCircumcenter.y, vector_x, vector_y, linepnt1_x, linepnt1_y, linepnt2_x, linepnt2_y, ref line_p); // check if there is a suitable intersection if (line_p[0] > 0.0) { line_inter_x = line_p[1]; line_inter_y = line_p[2]; } else { // for debugging (to make sure) //printf("1) No intersection between two lines!!!\n"); //printf("(%.14f,%.14f) (%.14f,%.14f) (%.14f,%.14f) (%.14f,%.14f)\n",myCircumcenter.x,myCircumcenter.y,vector_x,vector_y,linepnt1_x,linepnt1_y,linepnt2_x,linepnt2_y); } //---------------------------------------------------------------------// /// check if there is a Voronoi vertex between before intersection /// // check if the voronoi vertex is between the intersection and circumcenter PointBetweenPoints(inter_x, inter_y, myCircumcenter.x, myCircumcenter.y, neighborCircumcenter.x, neighborCircumcenter.y, ref voronoiOrInter); /// determine the point to be suggested /// if (p[0] > 0.0) { // there is at least one intersection point // if it is between circumcenter and intersection // if it returns 1.0 this means we have a voronoi vertex within feasible region if (Math.Abs(voronoiOrInter[0] - 1.0) <= EPS) { //-----------------hale new continues 1------------------// // now check if the line intersection is between cc and voronoi PointBetweenPoints(voronoiOrInter[2], voronoiOrInter[3], myCircumcenter.x, myCircumcenter.y, line_inter_x, line_inter_y, ref line_result); if (Math.Abs(line_result[0] - 1.0) <= EPS && line_p[0] > 0.0) { // check if we can go further by picking the slab line and petal intersection // calculate the distance to the smallest angle corner // check if we create a bad triangle or not if (((smallestAngleCorner.x - petal_slab_inter_x_first) * (smallestAngleCorner.x - petal_slab_inter_x_first) + (smallestAngleCorner.y - petal_slab_inter_y_first) * (smallestAngleCorner.y - petal_slab_inter_y_first) > lengthConst * ((smallestAngleCorner.x - line_inter_x) * (smallestAngleCorner.x - line_inter_x) + (smallestAngleCorner.y - line_inter_y) * (smallestAngleCorner.y - line_inter_y))) && (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, petal_slab_inter_x_first, petal_slab_inter_y_first)) && MinDistanceToNeighbor(petal_slab_inter_x_first, petal_slab_inter_y_first, ref neighborotri) > MinDistanceToNeighbor(line_inter_x, line_inter_y, ref neighborotri)) { // check the neighbor's vertices also, which one if better //slab and petal intersection is advised dxFirstSuggestion = petal_slab_inter_x_first - torg.x; dyFirstSuggestion = petal_slab_inter_y_first - torg.y; } else { // slab intersection point is further away if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, line_inter_x, line_inter_y)) { // apply perturbation // find the distance between circumcenter and intersection point d = Math.Sqrt((line_inter_x - myCircumcenter.x) * (line_inter_x - myCircumcenter.x) + (line_inter_y - myCircumcenter.y) * (line_inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter ax = myCircumcenter.x - line_inter_x; ay = myCircumcenter.y - line_inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter line_inter_x = line_inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); line_inter_y = line_inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, line_inter_x, line_inter_y)) { // go back to circumcenter dxFirstSuggestion = dx; dyFirstSuggestion = dy; } else { // intersection point is suggested dxFirstSuggestion = line_inter_x - torg.x; dyFirstSuggestion = line_inter_y - torg.y; } } else {// we are not creating a bad triangle // slab intersection is advised dxFirstSuggestion = line_result[2] - torg.x; dyFirstSuggestion = line_result[3] - torg.y; } } //------------------------------------------------------// } else { /// NOW APPLY A BREADTH-FIRST SEARCH ON THE VORONOI if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, neighborCircumcenter.x, neighborCircumcenter.y)) { // go back to circumcenter dxFirstSuggestion = dx; dyFirstSuggestion = dy; } else { // we are not creating a bad triangle // neighbor's circumcenter is suggested dxFirstSuggestion = voronoiOrInter[2] - torg.x; dyFirstSuggestion = voronoiOrInter[3] - torg.y; } } } else { // there is no voronoi vertex between intersection point and circumcenter //-----------------hale new continues 2-----------------// // now check if the line intersection is between cc and intersection point PointBetweenPoints(inter_x, inter_y, myCircumcenter.x, myCircumcenter.y, line_inter_x, line_inter_y, ref line_result); if (Math.Abs(line_result[0] - 1.0) <= EPS && line_p[0] > 0.0) { // check if we can go further by picking the slab line and petal intersection // calculate the distance to the smallest angle corner if (((smallestAngleCorner.x - petal_slab_inter_x_first) * (smallestAngleCorner.x - petal_slab_inter_x_first) + (smallestAngleCorner.y - petal_slab_inter_y_first) * (smallestAngleCorner.y - petal_slab_inter_y_first) > lengthConst * ((smallestAngleCorner.x - line_inter_x) * (smallestAngleCorner.x - line_inter_x) + (smallestAngleCorner.y - line_inter_y) * (smallestAngleCorner.y - line_inter_y))) && (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, petal_slab_inter_x_first, petal_slab_inter_y_first)) && MinDistanceToNeighbor(petal_slab_inter_x_first, petal_slab_inter_y_first, ref neighborotri) > MinDistanceToNeighbor(line_inter_x, line_inter_y, ref neighborotri)) { //slab and petal intersection is advised dxFirstSuggestion = petal_slab_inter_x_first - torg.x; dyFirstSuggestion = petal_slab_inter_y_first - torg.y; } else { // slab intersection point is further away if (IsBadTriangleAngle(largestAngleCorner.x, largestAngleCorner.y, middleAngleCorner.x, middleAngleCorner.y, line_inter_x, line_inter_y)) { // apply perturbation // find the distance between circumcenter and intersection point d = Math.Sqrt((line_inter_x - myCircumcenter.x) * (line_inter_x - myCircumcenter.x) + (line_inter_y - myCircumcenter.y) * (line_inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter ax = myCircumcenter.x - line_inter_x; ay = myCircumcenter.y - line_inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter line_inter_x = line_inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); line_inter_y = line_inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, line_inter_x, line_inter_y)) { // go back to circumcenter dxFirstSuggestion = dx; dyFirstSuggestion = dy; } else { // intersection point is suggested dxFirstSuggestion = line_inter_x - torg.x; dyFirstSuggestion = line_inter_y - torg.y; } } else {// we are not creating a bad triangle // slab intersection is advised dxFirstSuggestion = line_result[2] - torg.x; dyFirstSuggestion = line_result[3] - torg.y; } } //------------------------------------------------------// } else { if (IsBadTriangleAngle(largestAngleCorner.x, largestAngleCorner.y, middleAngleCorner.x, middleAngleCorner.y, inter_x, inter_y)) { //printf("testtriangle returned false! bad triangle\n"); // if it is inside feasible region, then insert v2 // apply perturbation // find the distance between circumcenter and intersection point d = Math.Sqrt((inter_x - myCircumcenter.x) * (inter_x - myCircumcenter.x) + (inter_y - myCircumcenter.y) * (inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter ax = myCircumcenter.x - inter_x; ay = myCircumcenter.y - inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter inter_x = inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); inter_y = inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, inter_x, inter_y)) { // go back to circumcenter dxFirstSuggestion = dx; dyFirstSuggestion = dy; } else { // intersection point is suggested dxFirstSuggestion = inter_x - torg.x; dyFirstSuggestion = inter_y - torg.y; } } else { // intersection point is suggested dxFirstSuggestion = inter_x - torg.x; dyFirstSuggestion = inter_y - torg.y; } } } /// if it is an acute triangle, check if it is a good enough location /// // for acute triangle case, we need to check if it is ok to use either of them if ((smallestAngleCorner.x - myCircumcenter.x) * (smallestAngleCorner.x - myCircumcenter.x) + (smallestAngleCorner.y - myCircumcenter.y) * (smallestAngleCorner.y - myCircumcenter.y) > lengthConst * ((smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) * (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) * (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)))) { // use circumcenter dxFirstSuggestion = dx; dyFirstSuggestion = dy; }// else we stick to what we have found }// intersection point }// if it is on the boundary, meaning no neighbor triangle in this direction, try other direction /// DO THE SAME THING FOR THE OTHER DIRECTION /// /// find the third point of the neighbor triangle /// neighborNotFound_second = GetNeighborsVertex(badotri, largestAngleCorner.x, largestAngleCorner.y, smallestAngleCorner.x, smallestAngleCorner.y, ref thirdPoint, ref neighborotri); /// find the circumcenter of the neighbor triangle /// dxSecondSuggestion = dx; // if we cannot find any appropriate suggestion, we use circumcenter dySecondSuggestion = dy; /// choose the correct intersection point /// // calculate middle point of the longest edge(bisector) xMidOfMiddleEdge = (largestAngleCorner.x + smallestAngleCorner.x) / 2.0; yMidOfMiddleEdge = (largestAngleCorner.y + smallestAngleCorner.y) / 2.0; // if there is a neighbor triangle if (!neighborNotFound_second) { neighborvertex_1 = neighborotri.Org(); neighborvertex_2 = neighborotri.Dest(); neighborvertex_3 = neighborotri.Apex(); // now calculate neighbor's circumcenter which is the voronoi site neighborCircumcenter = Primitives.FindCircumcenter(neighborvertex_1, neighborvertex_2, neighborvertex_3, ref xi_tmp, ref eta_tmp); /// compute petal and Voronoi edge intersection /// // in order to avoid degenerate cases, we need to do a vector based calculation for line vector_x = (largestAngleCorner.y - smallestAngleCorner.y);//(-y, x) vector_y = smallestAngleCorner.x - largestAngleCorner.x; vector_x = myCircumcenter.x + vector_x; vector_y = myCircumcenter.y + vector_y; // by intersecting bisectors you will end up with the one you want to walk on // then this line and circle should be intersected CircleLineIntersection(myCircumcenter.x, myCircumcenter.y, vector_x, vector_y, xPetalCtr, yPetalCtr, petalRadius, ref p); // we need to find correct intersection point, since line intersects circle twice // this direction is always ACUTE isCorrect = ChooseCorrectPoint(xMidOfMiddleEdge, yMidOfMiddleEdge, p[3], p[4], myCircumcenter.x, myCircumcenter.y, false/*(isObtuse+1)%2*/); // make sure which point is the correct one to be considered if (isCorrect) { inter_x = p[3]; inter_y = p[4]; } else { inter_x = p[1]; inter_y = p[2]; } //----------------------hale new second direction:for slab calculation---------------// // calculate the intersection of angle lines and Voronoi linepnt1_x = largestAngleCorner.x; linepnt1_y = largestAngleCorner.y; // vector from largestAngleCorner to middleAngleCorner line_vector_x = middleAngleCorner.x - largestAngleCorner.x; line_vector_y = middleAngleCorner.y - largestAngleCorner.y; // rotate the vector around largestAngleCorner in ccw by maxangle degrees linepnt2_x = petal_slab_inter_x_second; linepnt2_y = petal_slab_inter_y_second; // now calculate the intersection of two lines LineLineIntersection(myCircumcenter.x, myCircumcenter.y, vector_x, vector_y, linepnt1_x, linepnt1_y, linepnt2_x, linepnt2_y, ref line_p); // check if there is a suitable intersection if (line_p[0] > 0.0) { line_inter_x = line_p[1]; line_inter_y = line_p[2]; } else { // for debugging (to make sure) //printf("1) No intersection between two lines!!!\n"); //printf("(%.14f,%.14f) (%.14f,%.14f) (%.14f,%.14f) (%.14f,%.14f)\n",myCircumcenter.x,myCircumcenter.y,vector_x,vector_y,linepnt1_x,linepnt1_y,linepnt2_x,linepnt2_y); } //---------------------------------------------------------------------// /// check if there is a Voronoi vertex between before intersection /// // check if the voronoi vertex is between the intersection and circumcenter PointBetweenPoints(inter_x, inter_y, myCircumcenter.x, myCircumcenter.y, neighborCircumcenter.x, neighborCircumcenter.y, ref voronoiOrInter); /// determine the point to be suggested /// if (p[0] > 0.0) { // there is at least one intersection point // if it is between circumcenter and intersection // if it returns 1.0 this means we have a voronoi vertex within feasible region if (Math.Abs(voronoiOrInter[0] - 1.0) <= EPS) { //-----------------hale new continues 1------------------// // now check if the line intersection is between cc and voronoi PointBetweenPoints(voronoiOrInter[2], voronoiOrInter[3], myCircumcenter.x, myCircumcenter.y, line_inter_x, line_inter_y, ref line_result); if (Math.Abs(line_result[0] - 1.0) <= EPS && line_p[0] > 0.0) { // check if we can go further by picking the slab line and petal intersection // calculate the distance to the smallest angle corner // if (((smallestAngleCorner.x - petal_slab_inter_x_second) * (smallestAngleCorner.x - petal_slab_inter_x_second) + (smallestAngleCorner.y - petal_slab_inter_y_second) * (smallestAngleCorner.y - petal_slab_inter_y_second) > lengthConst * ((smallestAngleCorner.x - line_inter_x) * (smallestAngleCorner.x - line_inter_x) + (smallestAngleCorner.y - line_inter_y) * (smallestAngleCorner.y - line_inter_y))) && (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, petal_slab_inter_x_second, petal_slab_inter_y_second)) && MinDistanceToNeighbor(petal_slab_inter_x_second, petal_slab_inter_y_second, ref neighborotri) > MinDistanceToNeighbor(line_inter_x, line_inter_y, ref neighborotri)) { // slab and petal intersection is advised dxSecondSuggestion = petal_slab_inter_x_second - torg.x; dySecondSuggestion = petal_slab_inter_y_second - torg.y; } else { // slab intersection point is further away if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, line_inter_x, line_inter_y)) { // apply perturbation // find the distance between circumcenter and intersection point d = Math.Sqrt((line_inter_x - myCircumcenter.x) * (line_inter_x - myCircumcenter.x) + (line_inter_y - myCircumcenter.y) * (line_inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter ax = myCircumcenter.x - line_inter_x; ay = myCircumcenter.y - line_inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter line_inter_x = line_inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); line_inter_y = line_inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, line_inter_x, line_inter_y)) { // go back to circumcenter dxSecondSuggestion = dx; dySecondSuggestion = dy; } else { // intersection point is suggested dxSecondSuggestion = line_inter_x - torg.x; dySecondSuggestion = line_inter_y - torg.y; } } else {// we are not creating a bad triangle // slab intersection is advised dxSecondSuggestion = line_result[2] - torg.x; dySecondSuggestion = line_result[3] - torg.y; } } //------------------------------------------------------// } else { if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, neighborCircumcenter.x, neighborCircumcenter.y)) { // go back to circumcenter dxSecondSuggestion = dx; dySecondSuggestion = dy; } else { // we are not creating a bad triangle // neighbor's circumcenter is suggested dxSecondSuggestion = voronoiOrInter[2] - torg.x; dySecondSuggestion = voronoiOrInter[3] - torg.y; } } } else { // there is no voronoi vertex between intersection point and circumcenter //-----------------hale new continues 2-----------------// // now check if the line intersection is between cc and intersection point PointBetweenPoints(inter_x, inter_y, myCircumcenter.x, myCircumcenter.y, line_inter_x, line_inter_y, ref line_result); if (Math.Abs(line_result[0] - 1.0) <= EPS && line_p[0] > 0.0) { // check if we can go further by picking the slab line and petal intersection // calculate the distance to the smallest angle corner if (((smallestAngleCorner.x - petal_slab_inter_x_second) * (smallestAngleCorner.x - petal_slab_inter_x_second) + (smallestAngleCorner.y - petal_slab_inter_y_second) * (smallestAngleCorner.y - petal_slab_inter_y_second) > lengthConst * ((smallestAngleCorner.x - line_inter_x) * (smallestAngleCorner.x - line_inter_x) + (smallestAngleCorner.y - line_inter_y) * (smallestAngleCorner.y - line_inter_y))) && (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, petal_slab_inter_x_second, petal_slab_inter_y_second)) && MinDistanceToNeighbor(petal_slab_inter_x_second, petal_slab_inter_y_second, ref neighborotri) > MinDistanceToNeighbor(line_inter_x, line_inter_y, ref neighborotri)) { // slab and petal intersection is advised dxSecondSuggestion = petal_slab_inter_x_second - torg.x; dySecondSuggestion = petal_slab_inter_y_second - torg.y; } else { // slab intersection point is further away ; if (IsBadTriangleAngle(largestAngleCorner.x, largestAngleCorner.y, middleAngleCorner.x, middleAngleCorner.y, line_inter_x, line_inter_y)) { // apply perturbation // find the distance between circumcenter and intersection point d = Math.Sqrt((line_inter_x - myCircumcenter.x) * (line_inter_x - myCircumcenter.x) + (line_inter_y - myCircumcenter.y) * (line_inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter ax = myCircumcenter.x - line_inter_x; ay = myCircumcenter.y - line_inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter line_inter_x = line_inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); line_inter_y = line_inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, line_inter_x, line_inter_y)) { // go back to circumcenter dxSecondSuggestion = dx; dySecondSuggestion = dy; } else { // intersection point is suggested dxSecondSuggestion = line_inter_x - torg.x; dySecondSuggestion = line_inter_y - torg.y; } } else { // we are not creating a bad triangle // slab intersection is advised dxSecondSuggestion = line_result[2] - torg.x; dySecondSuggestion = line_result[3] - torg.y; } } //------------------------------------------------------// } else { if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, inter_x, inter_y)) { // if it is inside feasible region, then insert v2 // apply perturbation // find the distance between circumcenter and intersection point d = Math.Sqrt((inter_x - myCircumcenter.x) * (inter_x - myCircumcenter.x) + (inter_y - myCircumcenter.y) * (inter_y - myCircumcenter.y)); // then find the vector going from intersection point to circumcenter ax = myCircumcenter.x - inter_x; ay = myCircumcenter.y - inter_y; ax = ax / d; ay = ay / d; // now calculate the new intersection point which is perturbated towards the circumcenter inter_x = inter_x + ax * pertConst * Math.Sqrt(shortestEdgeDist); inter_y = inter_y + ay * pertConst * Math.Sqrt(shortestEdgeDist); if (IsBadTriangleAngle(middleAngleCorner.x, middleAngleCorner.y, largestAngleCorner.x, largestAngleCorner.y, inter_x, inter_y)) { // go back to circumcenter dxSecondSuggestion = dx; dySecondSuggestion = dy; } else { // intersection point is suggested dxSecondSuggestion = inter_x - torg.x; dySecondSuggestion = inter_y - torg.y; } } else { // intersection point is suggested dxSecondSuggestion = inter_x - torg.x; dySecondSuggestion = inter_y - torg.y; } } } /// if it is an acute triangle, check if it is a good enough location /// // for acute triangle case, we need to check if it is ok to use either of them if ((smallestAngleCorner.x - myCircumcenter.x) * (smallestAngleCorner.x - myCircumcenter.x) + (smallestAngleCorner.y - myCircumcenter.y) * (smallestAngleCorner.y - myCircumcenter.y) > lengthConst * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) * (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) * (smallestAngleCorner.y - (dySecondSuggestion + torg.y)))) { // use circumcenter dxSecondSuggestion = dx; dySecondSuggestion = dy; }// else we stick on what we have found } }// if it is on the boundary, meaning no neighbor triangle in this direction, the other direction might be ok if (isObtuse) { if (neighborNotFound_first && neighborNotFound_second) { //obtuse: check if the other direction works if (justAcute * ((smallestAngleCorner.x - (xMidOfMiddleEdge)) * (smallestAngleCorner.x - (xMidOfMiddleEdge)) + (smallestAngleCorner.y - (yMidOfMiddleEdge)) * (smallestAngleCorner.y - (yMidOfMiddleEdge))) > (smallestAngleCorner.x - (xMidOfLongestEdge)) * (smallestAngleCorner.x - (xMidOfLongestEdge)) + (smallestAngleCorner.y - (yMidOfLongestEdge)) * (smallestAngleCorner.y - (yMidOfLongestEdge))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; } else { dx = dxFirstSuggestion; dy = dyFirstSuggestion; } } else if (neighborNotFound_first) { //obtuse: check if the other direction works if (justAcute * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) * (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) * (smallestAngleCorner.y - (dySecondSuggestion + torg.y))) > (smallestAngleCorner.x - (xMidOfLongestEdge)) * (smallestAngleCorner.x - (xMidOfLongestEdge)) + (smallestAngleCorner.y - (yMidOfLongestEdge)) * (smallestAngleCorner.y - (yMidOfLongestEdge))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; } else { dx = dxFirstSuggestion; dy = dyFirstSuggestion; } } else if (neighborNotFound_second) { //obtuse: check if the other direction works if (justAcute * ((smallestAngleCorner.x - (xMidOfMiddleEdge)) * (smallestAngleCorner.x - (xMidOfMiddleEdge)) + (smallestAngleCorner.y - (yMidOfMiddleEdge)) * (smallestAngleCorner.y - (yMidOfMiddleEdge))) > (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) * (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) * (smallestAngleCorner.y - (dyFirstSuggestion + torg.y))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; } else { dx = dxFirstSuggestion; dy = dyFirstSuggestion; } } else { //obtuse: check if the other direction works if (justAcute * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) * (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) * (smallestAngleCorner.y - (dySecondSuggestion + torg.y))) > (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) * (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) * (smallestAngleCorner.y - (dyFirstSuggestion + torg.y))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; } else { dx = dxFirstSuggestion; dy = dyFirstSuggestion; } } } else { // acute : consider other direction if (neighborNotFound_first && neighborNotFound_second) { //obtuse: check if the other direction works if (justAcute * ((smallestAngleCorner.x - (xMidOfMiddleEdge)) * (smallestAngleCorner.x - (xMidOfMiddleEdge)) + (smallestAngleCorner.y - (yMidOfMiddleEdge)) * (smallestAngleCorner.y - (yMidOfMiddleEdge))) > (smallestAngleCorner.x - (xMidOfLongestEdge)) * (smallestAngleCorner.x - (xMidOfLongestEdge)) + (smallestAngleCorner.y - (yMidOfLongestEdge)) * (smallestAngleCorner.y - (yMidOfLongestEdge))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; } else { dx = dxFirstSuggestion; dy = dyFirstSuggestion; } } else if (neighborNotFound_first) { //obtuse: check if the other direction works if (justAcute * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) * (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) * (smallestAngleCorner.y - (dySecondSuggestion + torg.y))) > (smallestAngleCorner.x - (xMidOfLongestEdge)) * (smallestAngleCorner.x - (xMidOfLongestEdge)) + (smallestAngleCorner.y - (yMidOfLongestEdge)) * (smallestAngleCorner.y - (yMidOfLongestEdge))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; } else { dx = dxFirstSuggestion; dy = dyFirstSuggestion; } } else if (neighborNotFound_second) { //obtuse: check if the other direction works if (justAcute * ((smallestAngleCorner.x - (xMidOfMiddleEdge)) * (smallestAngleCorner.x - (xMidOfMiddleEdge)) + (smallestAngleCorner.y - (yMidOfMiddleEdge)) * (smallestAngleCorner.y - (yMidOfMiddleEdge))) > (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) * (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) * (smallestAngleCorner.y - (dyFirstSuggestion + torg.y))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; } else { dx = dxFirstSuggestion; dy = dyFirstSuggestion; } } else { //obtuse: check if the other direction works if (justAcute * ((smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) * (smallestAngleCorner.x - (dxSecondSuggestion + torg.x)) + (smallestAngleCorner.y - (dySecondSuggestion + torg.y)) * (smallestAngleCorner.y - (dySecondSuggestion + torg.y))) > (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) * (smallestAngleCorner.x - (dxFirstSuggestion + torg.x)) + (smallestAngleCorner.y - (dyFirstSuggestion + torg.y)) * (smallestAngleCorner.y - (dyFirstSuggestion + torg.y))) { dx = dxSecondSuggestion; dy = dySecondSuggestion; } else { dx = dxFirstSuggestion; dy = dyFirstSuggestion; } } }// end if obtuse }// end of relocation }// end of almostGood Point circumcenter = new Point(); if (relocated <= 0) { circumcenter.x = torg.x + dx; circumcenter.y = torg.y + dy; } else { circumcenter.x = origin_x + dx; circumcenter.y = origin_y + dy; } xi = (yao * dx - xao * dy) * (2.0 * denominator); eta = (xdo * dy - ydo * dx) * (2.0 * denominator); return circumcenter; }
/// <summary> /// Finds a triangle abutting a subsegment. /// </summary> public void TriPivot(ref Otri ot) { ot = seg.triangles[orient]; //decode(ptr, otri) }
public void TriPivot(ref Otri ot) { ot = this.seg.triangles[this.orient]; }
SplayNode SplayInsert(SplayNode splayroot, Otri newkey, Point searchpoint) { SplayNode newsplaynode; newsplaynode = new SplayNode(); //poolalloc(m.splaynodes); splaynodes.Add(newsplaynode); newkey.Copy(ref newsplaynode.keyedge); newsplaynode.keydest = newkey.Dest(); if (splayroot == null) { newsplaynode.lchild = null; newsplaynode.rchild = null; } else if (RightOfHyperbola(ref splayroot.keyedge, searchpoint)) { newsplaynode.lchild = splayroot; newsplaynode.rchild = splayroot.rchild; splayroot.rchild = null; } else { newsplaynode.lchild = splayroot.lchild; newsplaynode.rchild = splayroot; splayroot.lchild = null; } return newsplaynode; }
/// <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; float 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(); } }
bool RightOfHyperbola(ref Otri fronttri, Point newsite) { Vertex leftvertex, rightvertex; double dxa, dya, dxb, dyb; Statistic.HyperbolaCount++; leftvertex = fronttri.Dest(); rightvertex = fronttri.Apex(); if ((leftvertex.y < rightvertex.y) || ((leftvertex.y == rightvertex.y) && (leftvertex.x < rightvertex.x))) { if (newsite.x >= rightvertex.x) { return true; } } else { if (newsite.x <= leftvertex.x) { return false; } } dxa = leftvertex.x - newsite.x; dya = leftvertex.y - newsite.y; dxb = rightvertex.x - newsite.x; dyb = rightvertex.y - newsite.y; return dya * (dxb * dxb + dyb * dyb) > dyb * (dxa * dxa + dya * dya); }
/// <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; float searchdist, dist; float 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); }
SplayNode FrontLocate(SplayNode splayroot, Otri bottommost, Vertex searchvertex, ref Otri searchtri, ref bool farright) { bool farrightflag; bottommost.Copy(ref searchtri); splayroot = Splay(splayroot, searchvertex, ref searchtri); farrightflag = false; while (!farrightflag && RightOfHyperbola(ref searchtri, searchvertex)) { searchtri.OnextSelf(); farrightflag = searchtri.Equal(bottommost); } farright = farrightflag; return splayroot; }
public void Update(ref Otri otri) { otri.Copy(ref recenttri); }
/// <summary> /// Add a bad triangle to the end of a queue. /// </summary> /// <param name="enqtri"></param> /// <param name="minedge"></param> /// <param name="enqapex"></param> /// <param name="enqorg"></param> /// <param name="enqdest"></param> public void Enqueue(ref Otri enqtri, double minedge, Vertex enqapex, Vertex enqorg, Vertex enqdest) { // Allocate space for the bad triangle. BadTriangle newbad = new BadTriangle(); newbad.poortri = enqtri; newbad.key = minedge; newbad.triangapex = enqapex; newbad.triangorg = enqorg; newbad.triangdest = enqdest; Enqueue(newbad); }