/// <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> /// 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(); } }
/// <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); }
/// <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> /// 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> /// Recursively form a Delaunay triangulation by the divide-and-conquer method. /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <param name="axis"></param> /// <param name="farleft"></param> /// <param name="farright"></param> /// <remarks> /// Recursively breaks down the problem into smaller pieces, which are /// knitted together by mergehulls(). The base cases (problems of two or /// three vertices) are handled specially here. /// /// On completion, 'farleft' and 'farright' are bounding triangles such that /// the origin of 'farleft' is the leftmost vertex (breaking ties by /// choosing the highest leftmost vertex), and the destination of /// 'farright' is the rightmost vertex (breaking ties by choosing the /// lowest rightmost vertex). /// </remarks> void DivconqRecurse(int left, int right, int axis, ref Otri farleft, ref Otri farright) { Otri midtri = default(Otri); Otri tri1 = default(Otri); Otri tri2 = default(Otri); Otri tri3 = default(Otri); Otri innerleft = default(Otri), innerright = default(Otri); double area; int vertices = right - left + 1; int divider; if (vertices == 2) { // The triangulation of two vertices is an edge. An edge is // represented by two bounding triangles. mesh.MakeTriangle(ref farleft); farleft.SetOrg(sortarray[left]); farleft.SetDest(sortarray[left + 1]); // The apex is intentionally left NULL. mesh.MakeTriangle(ref farright); farright.SetOrg(sortarray[left + 1]); farright.SetDest(sortarray[left]); // The apex is intentionally left NULL. farleft.Bond(ref farright); farleft.LprevSelf(); farright.LnextSelf(); farleft.Bond(ref farright); farleft.LprevSelf(); farright.LnextSelf(); farleft.Bond(ref farright); // Ensure that the origin of 'farleft' is sortarray[0]. farright.Lprev(ref farleft); return; } else if (vertices == 3) { // The triangulation of three vertices is either a triangle (with // three bounding triangles) or two edges (with four bounding // triangles). In either case, four triangles are created. mesh.MakeTriangle(ref midtri); mesh.MakeTriangle(ref tri1); mesh.MakeTriangle(ref tri2); mesh.MakeTriangle(ref tri3); area = Primitives.CounterClockwise(sortarray[left], sortarray[left + 1], sortarray[left + 2]); if (area == 0.0) { // Three collinear vertices; the triangulation is two edges. midtri.SetOrg(sortarray[left]); midtri.SetDest(sortarray[left + 1]); tri1.SetOrg(sortarray[left + 1]); tri1.SetDest(sortarray[left]); tri2.SetOrg(sortarray[left + 2]); tri2.SetDest(sortarray[left + 1]); tri3.SetOrg(sortarray[left + 1]); tri3.SetDest(sortarray[left + 2]); // All apices are intentionally left NULL. midtri.Bond(ref tri1); tri2.Bond(ref tri3); midtri.LnextSelf(); tri1.LprevSelf(); tri2.LnextSelf(); tri3.LprevSelf(); midtri.Bond(ref tri3); tri1.Bond(ref tri2); midtri.LnextSelf(); tri1.LprevSelf(); tri2.LnextSelf(); tri3.LprevSelf(); midtri.Bond(ref tri1); tri2.Bond(ref tri3); // Ensure that the origin of 'farleft' is sortarray[0]. tri1.Copy(ref farleft); // Ensure that the destination of 'farright' is sortarray[2]. tri2.Copy(ref farright); } else { // The three vertices are not collinear; the triangulation is one // triangle, namely 'midtri'. midtri.SetOrg(sortarray[left]); tri1.SetDest(sortarray[left]); tri3.SetOrg(sortarray[left]); // Apices of tri1, tri2, and tri3 are left NULL. if (area > 0.0) { // The vertices are in counterclockwise order. midtri.SetDest(sortarray[left + 1]); tri1.SetOrg(sortarray[left + 1]); tri2.SetDest(sortarray[left + 1]); midtri.SetApex(sortarray[left + 2]); tri2.SetOrg(sortarray[left + 2]); tri3.SetDest(sortarray[left + 2]); } else { // The vertices are in clockwise order. midtri.SetDest(sortarray[left + 2]); tri1.SetOrg(sortarray[left + 2]); tri2.SetDest(sortarray[left + 2]); midtri.SetApex(sortarray[left + 1]); tri2.SetOrg(sortarray[left + 1]); tri3.SetDest(sortarray[left + 1]); } // The topology does not depend on how the vertices are ordered. midtri.Bond(ref tri1); midtri.LnextSelf(); midtri.Bond(ref tri2); midtri.LnextSelf(); midtri.Bond(ref tri3); tri1.LprevSelf(); tri2.LnextSelf(); tri1.Bond(ref tri2); tri1.LprevSelf(); tri3.LprevSelf(); tri1.Bond(ref tri3); tri2.LnextSelf(); tri3.LprevSelf(); tri2.Bond(ref tri3); // Ensure that the origin of 'farleft' is sortarray[0]. tri1.Copy(ref farleft); // Ensure that the destination of 'farright' is sortarray[2]. if (area > 0.0) { tri2.Copy(ref farright); } else { farleft.Lnext(ref farright); } } return; } else { // Split the vertices in half. divider = vertices >> 1; // Recursively triangulate each half. DivconqRecurse(left, left + divider - 1, 1 - axis, ref farleft, ref innerleft); //DebugWriter.Session.Write(mesh, true); DivconqRecurse(left + divider, right, 1 - axis, ref innerright, ref farright); //DebugWriter.Session.Write(mesh, true); // Merge the two triangulations into one. MergeHulls(ref farleft, ref innerleft, ref innerright, ref farright, axis); //DebugWriter.Session.Write(mesh, true); } }
/// <summary> /// Merge two adjacent Delaunay triangulations into a single Delaunay triangulation. /// </summary> /// <param name="farleft">Bounding triangles of the left triangulation.</param> /// <param name="innerleft">Bounding triangles of the left triangulation.</param> /// <param name="innerright">Bounding triangles of the right triangulation.</param> /// <param name="farright">Bounding triangles of the right triangulation.</param> /// <param name="axis"></param> /// <remarks> /// This is similar to the algorithm given by Guibas and Stolfi, but uses /// a triangle-based, rather than edge-based, data structure. /// /// The algorithm walks up the gap between the two triangulations, knitting /// them together. As they are merged, some of their bounding triangles /// are converted into real triangles of the triangulation. The procedure /// pulls each hull's bounding triangles apart, then knits them together /// like the teeth of two gears. The Delaunay property determines, at each /// step, whether the next "tooth" is a bounding triangle of the left hull /// or the right. When a bounding triangle becomes real, its apex is /// changed from NULL to a real vertex. /// /// Only two new triangles need to be allocated. These become new bounding /// triangles at the top and bottom of the seam. They are used to connect /// the remaining bounding triangles (those that have not been converted /// into real triangles) into a single fan. /// /// On entry, 'farleft' and 'innerleft' are bounding triangles of the left /// triangulation. The origin of 'farleft' is the leftmost vertex, and /// the destination of 'innerleft' is the rightmost vertex of the /// triangulation. Similarly, 'innerright' and 'farright' are bounding /// triangles of the right triangulation. The origin of 'innerright' and /// destination of 'farright' are the leftmost and rightmost vertices. /// /// On completion, the origin of 'farleft' is the leftmost vertex of the /// merged triangulation, and the destination of 'farright' is the rightmost /// vertex. /// </remarks> void MergeHulls(ref Otri farleft, ref Otri innerleft, ref Otri innerright, ref Otri farright, int axis) { Otri leftcand = default(Otri), rightcand = default(Otri); Otri nextedge = default(Otri); Otri sidecasing = default(Otri), topcasing = default(Otri), outercasing = default(Otri); Otri checkedge = default(Otri); Otri baseedge = default(Otri); Vertex innerleftdest; Vertex innerrightorg; Vertex innerleftapex, innerrightapex; Vertex farleftpt, farrightpt; Vertex farleftapex, farrightapex; Vertex lowerleft, lowerright; Vertex upperleft, upperright; Vertex nextapex; Vertex checkvertex; bool changemade; bool badedge; bool leftfinished, rightfinished; innerleftdest = innerleft.Dest(); innerleftapex = innerleft.Apex(); innerrightorg = innerright.Org(); innerrightapex = innerright.Apex(); // Special treatment for horizontal cuts. if (useDwyer && (axis == 1)) { farleftpt = farleft.Org(); farleftapex = farleft.Apex(); farrightpt = farright.Dest(); farrightapex = farright.Apex(); // The pointers to the extremal vertices are shifted to point to the // topmost and bottommost vertex of each hull, rather than the // leftmost and rightmost vertices. while (farleftapex.y < farleftpt.y) { farleft.LnextSelf(); farleft.SymSelf(); farleftpt = farleftapex; farleftapex = farleft.Apex(); } innerleft.Sym(ref checkedge); checkvertex = checkedge.Apex(); while (checkvertex.y > innerleftdest.y) { checkedge.Lnext(ref innerleft); innerleftapex = innerleftdest; innerleftdest = checkvertex; innerleft.Sym(ref checkedge); checkvertex = checkedge.Apex(); } while (innerrightapex.y < innerrightorg.y) { innerright.LnextSelf(); innerright.SymSelf(); innerrightorg = innerrightapex; innerrightapex = innerright.Apex(); } farright.Sym(ref checkedge); checkvertex = checkedge.Apex(); while (checkvertex.y > farrightpt.y) { checkedge.Lnext(ref farright); farrightapex = farrightpt; farrightpt = checkvertex; farright.Sym(ref checkedge); checkvertex = checkedge.Apex(); } } // Find a line tangent to and below both hulls. do { changemade = false; // Make innerleftdest the "bottommost" vertex of the left hull. if (Primitives.CounterClockwise(innerleftdest, innerleftapex, innerrightorg) > 0.0) { innerleft.LprevSelf(); innerleft.SymSelf(); innerleftdest = innerleftapex; innerleftapex = innerleft.Apex(); changemade = true; } // Make innerrightorg the "bottommost" vertex of the right hull. if (Primitives.CounterClockwise(innerrightapex, innerrightorg, innerleftdest) > 0.0) { innerright.LnextSelf(); innerright.SymSelf(); innerrightorg = innerrightapex; innerrightapex = innerright.Apex(); changemade = true; } } while (changemade); // Find the two candidates to be the next "gear tooth." innerleft.Sym(ref leftcand); innerright.Sym(ref rightcand); // Create the bottom new bounding triangle. mesh.MakeTriangle(ref baseedge); // Connect it to the bounding boxes of the left and right triangulations. baseedge.Bond(ref innerleft); baseedge.LnextSelf(); baseedge.Bond(ref innerright); baseedge.LnextSelf(); baseedge.SetOrg(innerrightorg); baseedge.SetDest(innerleftdest); // Apex is intentionally left NULL. // Fix the extreme triangles if necessary. farleftpt = farleft.Org(); if (innerleftdest == farleftpt) { baseedge.Lnext(ref farleft); } farrightpt = farright.Dest(); if (innerrightorg == farrightpt) { baseedge.Lprev(ref farright); } // The vertices of the current knitting edge. lowerleft = innerleftdest; lowerright = innerrightorg; // The candidate vertices for knitting. upperleft = leftcand.Apex(); upperright = rightcand.Apex(); // Walk up the gap between the two triangulations, knitting them together. while (true) { // Have we reached the top? (This isn't quite the right question, // because even though the left triangulation might seem finished now, // moving up on the right triangulation might reveal a new vertex of // the left triangulation. And vice-versa.) leftfinished = Primitives.CounterClockwise(upperleft, lowerleft, lowerright) <= 0.0; rightfinished = Primitives.CounterClockwise(upperright, lowerleft, lowerright) <= 0.0; if (leftfinished && rightfinished) { // Create the top new bounding triangle. mesh.MakeTriangle(ref nextedge); nextedge.SetOrg(lowerleft); nextedge.SetDest(lowerright); // Apex is intentionally left NULL. // Connect it to the bounding boxes of the two triangulations. nextedge.Bond(ref baseedge); nextedge.LnextSelf(); nextedge.Bond(ref rightcand); nextedge.LnextSelf(); nextedge.Bond(ref leftcand); // Special treatment for horizontal cuts. if (useDwyer && (axis == 1)) { farleftpt = farleft.Org(); farleftapex = farleft.Apex(); farrightpt = farright.Dest(); farrightapex = farright.Apex(); farleft.Sym(ref checkedge); checkvertex = checkedge.Apex(); // The pointers to the extremal vertices are restored to the // leftmost and rightmost vertices (rather than topmost and // bottommost). while (checkvertex.x < farleftpt.x) { checkedge.Lprev(ref farleft); farleftapex = farleftpt; farleftpt = checkvertex; farleft.Sym(ref checkedge); checkvertex = checkedge.Apex(); } while (farrightapex.x > farrightpt.x) { farright.LprevSelf(); farright.SymSelf(); farrightpt = farrightapex; farrightapex = farright.Apex(); } } return; } // Consider eliminating edges from the left triangulation. if (!leftfinished) { // What vertex would be exposed if an edge were deleted? leftcand.Lprev(ref nextedge); nextedge.SymSelf(); nextapex = nextedge.Apex(); // If nextapex is NULL, then no vertex would be exposed; the // triangulation would have been eaten right through. if (nextapex != null) { // Check whether the edge is Delaunay. badedge = Primitives.InCircle(lowerleft, lowerright, upperleft, nextapex) > 0.0; while (badedge) { // Eliminate the edge with an edge flip. As a result, the // left triangulation will have one more boundary triangle. nextedge.LnextSelf(); nextedge.Sym(ref topcasing); nextedge.LnextSelf(); nextedge.Sym(ref sidecasing); nextedge.Bond(ref topcasing); leftcand.Bond(ref sidecasing); leftcand.LnextSelf(); leftcand.Sym(ref outercasing); nextedge.LprevSelf(); nextedge.Bond(ref outercasing); // Correct the vertices to reflect the edge flip. leftcand.SetOrg(lowerleft); leftcand.SetDest(null); leftcand.SetApex(nextapex); nextedge.SetOrg(null); nextedge.SetDest(upperleft); nextedge.SetApex(nextapex); // Consider the newly exposed vertex. upperleft = nextapex; // What vertex would be exposed if another edge were deleted? sidecasing.Copy(ref nextedge); nextapex = nextedge.Apex(); if (nextapex != null) { // Check whether the edge is Delaunay. badedge = Primitives.InCircle(lowerleft, lowerright, upperleft, nextapex) > 0.0; } else { // Avoid eating right through the triangulation. badedge = false; } } } } // Consider eliminating edges from the right triangulation. if (!rightfinished) { // What vertex would be exposed if an edge were deleted? rightcand.Lnext(ref nextedge); nextedge.SymSelf(); nextapex = nextedge.Apex(); // If nextapex is NULL, then no vertex would be exposed; the // triangulation would have been eaten right through. if (nextapex != null) { // Check whether the edge is Delaunay. badedge = Primitives.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0; while (badedge) { // Eliminate the edge with an edge flip. As a result, the // right triangulation will have one more boundary triangle. nextedge.LprevSelf(); nextedge.Sym(ref topcasing); nextedge.LprevSelf(); nextedge.Sym(ref sidecasing); nextedge.Bond(ref topcasing); rightcand.Bond(ref sidecasing); rightcand.LprevSelf(); rightcand.Sym(ref outercasing); nextedge.LnextSelf(); nextedge.Bond(ref outercasing); // Correct the vertices to reflect the edge flip. rightcand.SetOrg(null); rightcand.SetDest(lowerright); rightcand.SetApex(nextapex); nextedge.SetOrg(upperright); nextedge.SetDest(null); nextedge.SetApex(nextapex); // Consider the newly exposed vertex. upperright = nextapex; // What vertex would be exposed if another edge were deleted? sidecasing.Copy(ref nextedge); nextapex = nextedge.Apex(); if (nextapex != null) { // Check whether the edge is Delaunay. badedge = Primitives.InCircle(lowerleft, lowerright, upperright, nextapex) > 0.0; } else { // Avoid eating right through the triangulation. badedge = false; } } } } if (leftfinished || (!rightfinished && (Primitives.InCircle(upperleft, lowerleft, lowerright, upperright) > 0.0))) { // Knit the triangulations, adding an edge from 'lowerleft' // to 'upperright'. baseedge.Bond(ref rightcand); rightcand.Lprev(ref baseedge); baseedge.SetDest(lowerleft); lowerright = upperright; baseedge.Sym(ref rightcand); upperright = rightcand.Apex(); } else { // Knit the triangulations, adding an edge from 'upperleft' // to 'lowerright'. baseedge.Bond(ref leftcand); leftcand.Lnext(ref baseedge); baseedge.SetOrg(lowerright); lowerleft = upperleft; baseedge.Sym(ref leftcand); upperleft = leftcand.Apex(); } } }
/// <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 FindNewLocationWithoutMaxAngle(Vertex torg, Vertex tdest, Vertex tapex, ref float xi, ref float eta, bool offcenter, Otri badotri) { float offconstant = behavior.offconstant; // for calculating the distances of the edges float xdo, ydo, xao, yao, xda, yda; float dodist, aodist, dadist; // for exact calculation float denominator; float dx, dy, dxoff, dyoff; ////////////////////////////// HALE'S VARIABLES ////////////////////////////// // keeps the difference of coordinates edge float xShortestEdge = 0, yShortestEdge = 0, xMiddleEdge, yMiddleEdge, xLongestEdge, yLongestEdge; // keeps the square of edge lengths float 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 float cosMaxAngle; bool isObtuse; // 1: obtuse 0: nonobtuse // keeps the radius of petal float petalRadius; // for calculating petal center float xPetalCtr_1, yPetalCtr_1, xPetalCtr_2, yPetalCtr_2, xPetalCtr, yPetalCtr, xMidOfShortestEdge, yMidOfShortestEdge; float dxcenter1, dycenter1, dxcenter2, dycenter2; // for finding neighbor Otri neighborotri = default(Otri); float[] thirdPoint = new float[2]; //int neighborNotFound = -1; bool neighborNotFound; // for keeping the vertices of the neighbor triangle Vertex neighborvertex_1; Vertex neighborvertex_2; Vertex neighborvertex_3; // dummy variables float xi_tmp = 0, eta_tmp = 0; //vertex thirdVertex; // for petal intersection float vector_x, vector_y, xMidOfLongestEdge, yMidOfLongestEdge, inter_x, inter_y; float[] p = new float[5], voronoiOrInter = new float[4]; bool isCorrect; // for vector calculations in perturbation float ax, ay, d; float pertConst = 0.06f; // perturbation constant float lengthConst = 1; // used at comparing circumcenter's distance to proposed point's distance float 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 float[] newloc = new float[2]; // new location suggested by smoothing float 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 float dxFirstSuggestion, dyFirstSuggestion, dxSecondSuggestion, dySecondSuggestion; // second direction variables float xMidOfMiddleEdge, yMidOfMiddleEdge; ////////////////////////////// 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.5f / (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.5f / 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.0f)) { // origin has the smallest angle if (orientation == 213 || orientation == 231) { // Find the position of the off-center, as described by Alper Ungor. dxoff = 0.5f * xShortestEdge - offconstant * yShortestEdge; dyoff = 0.5f * 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.5f * xShortestEdge + offconstant * yShortestEdge; dyoff = 0.5f * 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.5f * xShortestEdge - offconstant * yShortestEdge; dyoff = 0.5f * 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 * UnityEngine.Mathf.Sqrt(middleEdgeDist) * UnityEngine.Mathf.Sqrt(shortestEdgeDist)); if (cosMaxAngle < 0.0f) { // obtuse isObtuse = true; } else if (UnityEngine.Mathf.Abs(cosMaxAngle - 0.0f) <= UnityEngine.Mathf.Epsilon) { // 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 petalRadius = UnityEngine.Mathf.Sqrt(shortestEdgeDist) / (2f * UnityEngine.Mathf.Sin(behavior.MinAngle * UnityEngine.Mathf.PI / 180.0f)); /// compute two possible centers of the petal /// // finding the center // first find the middle point of smallest edge xMidOfShortestEdge = (middleAngleCorner.x + largestAngleCorner.x) / 2.0f; yMidOfShortestEdge = (middleAngleCorner.y + largestAngleCorner.y) / 2.0f; // two possible centers xPetalCtr_1 = xMidOfShortestEdge + UnityEngine.Mathf.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.y - largestAngleCorner.y) / UnityEngine.Mathf.Sqrt(shortestEdgeDist); yPetalCtr_1 = yMidOfShortestEdge + UnityEngine.Mathf.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.x - middleAngleCorner.x) / UnityEngine.Mathf.Sqrt(shortestEdgeDist); xPetalCtr_2 = xMidOfShortestEdge - UnityEngine.Mathf.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (middleAngleCorner.y - largestAngleCorner.y) / UnityEngine.Mathf.Sqrt(shortestEdgeDist); yPetalCtr_2 = yMidOfShortestEdge - UnityEngine.Mathf.Sqrt(petalRadius * petalRadius - (shortestEdgeDist / 4)) * (largestAngleCorner.x - middleAngleCorner.x) / UnityEngine.Mathf.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 = 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; // if there is a neighbor triangle if (!neighborNotFound) { 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); /// choose the correct intersection point /// // calculate middle point of the longest edge(bisector) xMidOfLongestEdge = (middleAngleCorner.x + smallestAngleCorner.x) / 2.0f; yMidOfLongestEdge = (middleAngleCorner.y + smallestAngleCorner.y) / 2.0f; // 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]; } /// 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 (UnityEngine.Mathf.Abs(voronoiOrInter[0] - 1.0f) <= UnityEngine.Mathf.Epsilon) { 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 if (IsBadTriangleAngle(largestAngleCorner.x, largestAngleCorner.y, middleAngleCorner.x, middleAngleCorner.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 = UnityEngine.Mathf.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 * UnityEngine.Mathf.Sqrt(shortestEdgeDist); inter_y = inter_y + ay * pertConst * UnityEngine.Mathf.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 = 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; // if there is a neighbor triangle if (!neighborNotFound) { 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); /// choose the correct intersection point /// // calcuwedgeslate middle point of the longest edge(bisector) xMidOfMiddleEdge = (largestAngleCorner.x + smallestAngleCorner.x) / 2.0f; yMidOfMiddleEdge = (largestAngleCorner.y + smallestAngleCorner.y) / 2.0f; // 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]; } /// 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.0f) { // 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 (UnityEngine.Mathf.Abs(voronoiOrInter[0] - 1.0f) <= UnityEngine.Mathf.Epsilon) { 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 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 = UnityEngine.Mathf.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 * UnityEngine.Mathf.Sqrt(shortestEdgeDist); inter_y = inter_y + ay * pertConst * UnityEngine.Mathf.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) { //obtuse: do nothing dx = dxFirstSuggestion; dy = dyFirstSuggestion; } else { // acute : consider other direction 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.0f * denominator); eta = (xdo * dy - ydo * dx) * (2.0f * denominator); return circumcenter; }