/// <summary> /// Add a bad triangle to the end of a queue. /// </summary> /// <param name="enqtri"></param> /// <param name="minedge"></param> /// <param name="enqapex"></param> /// <param name="enqorg"></param> /// <param name="enqdest"></param> public void Enqueue(ref Otri enqtri, double minedge, Vertex enqapex, Vertex enqorg, Vertex enqdest) { // Allocate space for the bad triangle. BadTriangle newbad = new BadTriangle(); newbad.poortri = enqtri; newbad.key = minedge; newbad.triangapex = enqapex; newbad.triangorg = enqorg; newbad.triangdest = enqdest; Enqueue(newbad); }
/// <summary> /// Add a bad triangle data structure to the end of a queue. /// </summary> /// <param name="badtri">The bad triangle to enqueue.</param> public void Enqueue(BadTriangle badtri) { double length, multiplier; int exponent, expincrement; int queuenumber; int posexponent; int i; this.count++; // Determine the appropriate queue to put the bad triangle into. // Recall that the key is the square of its shortest edge length. if (badtri.key >= 1.0) { length = badtri.key; posexponent = 1; } else { // 'badtri.key' is 2.0 to a negative exponent, so we'll record that // fact and use the reciprocal of 'badtri.key', which is > 1.0. length = 1.0 / badtri.key; posexponent = 0; } // 'length' is approximately 2.0 to what exponent? The following code // determines the answer in time logarithmic in the exponent. exponent = 0; while (length > 2.0) { // Find an approximation by repeated squaring of two. expincrement = 1; multiplier = 0.5; while (length * multiplier * multiplier > 1.0) { expincrement *= 2; multiplier *= multiplier; } // Reduce the value of 'length', then iterate if necessary. exponent += expincrement; length *= multiplier; } // 'length' is approximately squareroot(2.0) to what exponent? exponent = 2 * exponent + (length > SQRT2 ? 1 : 0); // 'exponent' is now in the range 0...2047 for IEEE double precision. // Choose a queue in the range 0...4095. The shortest edges have the // highest priority (queue 4095). if (posexponent > 0) { queuenumber = 2047 - exponent; } else { queuenumber = 2048 + exponent; } // Are we inserting into an empty queue? if (queuefront[queuenumber] == null) { // Yes, we are inserting into an empty queue. // Will this become the highest-priority queue? if (queuenumber > firstnonemptyq) { // Yes, this is the highest-priority queue. nextnonemptyq[queuenumber] = firstnonemptyq; firstnonemptyq = queuenumber; } else { // No, this is not the highest-priority queue. // Find the queue with next higher priority. i = queuenumber + 1; while (queuefront[i] == null) { i++; } // Mark the newly nonempty queue as following a higher-priority queue. nextnonemptyq[queuenumber] = nextnonemptyq[i]; nextnonemptyq[i] = queuenumber; } // Put the bad triangle at the beginning of the (empty) queue. queuefront[queuenumber] = badtri; } else { // Add the bad triangle to the end of an already nonempty queue. queuetail[queuenumber].nexttriang = badtri; } // Maintain a pointer to the last triangle of the queue. queuetail[queuenumber] = badtri; // Newly enqueued bad triangle has no successor in the queue. badtri.nexttriang = null; }
/// <summary> /// Inserts a vertex at the circumcenter of a triangle. Deletes /// the newly inserted vertex if it encroaches upon a segment. /// </summary> /// <param name="badtri"></param> private void SplitTriangle(BadTriangle badtri) { Otri badotri = default(Otri); Vertex borg, bdest, bapex; Point newloc; // Location of the new vertex double xi = 0, eta = 0; InsertVertexResult success; bool errorflag; badotri = badtri.poortri; borg = badotri.Org(); bdest = badotri.Dest(); bapex = badotri.Apex(); // Make sure that this triangle is still the same triangle it was // when it was tested and determined to be of bad quality. // Subsequent transformations may have made it a different triangle. if (!Otri.IsDead(badotri.triangle) && (borg == badtri.triangorg) && (bdest == badtri.triangdest) && (bapex == badtri.triangapex)) { errorflag = false; // Create a new vertex at the triangle's circumcenter. // Using the original (simpler) Steiner point location method // for mesh refinement. // TODO: NewLocation doesn't work for refinement. Why? Maybe // reset VertexType? if (behavior.fixedArea || behavior.VarArea) { newloc = Primitives.FindCircumcenter(borg, bdest, bapex, ref xi, ref eta, behavior.offconstant); } else { newloc = newLocation.FindLocation(borg, bdest, bapex, ref xi, ref eta, true, badotri); } // Check whether the new vertex lies on a triangle vertex. if (((newloc.x == borg.x) && (newloc.y == borg.y)) || ((newloc.x == bdest.x) && (newloc.y == bdest.y)) || ((newloc.x == bapex.x) && (newloc.y == bapex.y))) { if (Behavior.Verbose) { logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()"); errorflag = true; } } else { // The new vertex must be in the interior, and therefore is a // free vertex with a marker of zero. Vertex newvertex = new Vertex(newloc.x, newloc.y, 0, mesh.nextras); newvertex.type = VertexType.FreeVertex; for (int i = 0; i < mesh.nextras; i++) { // Interpolate the vertex attributes at the circumcenter. newvertex.attributes[i] = borg.attributes[i] + xi * (bdest.attributes[i] - borg.attributes[i]) + eta * (bapex.attributes[i] - borg.attributes[i]); } // Ensure that the handle 'badotri' does not represent the longest // edge of the triangle. This ensures that the circumcenter must // fall to the left of this edge, so point location will work. // (If the angle org-apex-dest exceeds 90 degrees, then the // circumcenter lies outside the org-dest edge, and eta is // negative. Roundoff error might prevent eta from being // negative when it should be, so I test eta against xi.) if (eta < xi) { badotri.LprevSelf(); } // Insert the circumcenter, searching from the edge of the triangle, // and maintain the Delaunay property of the triangulation. Osub tmp = default(Osub); success = mesh.InsertVertex(newvertex, ref badotri, ref tmp, true, true); if (success == InsertVertexResult.Successful) { newvertex.hash = mesh.hash_vtx++; newvertex.id = newvertex.hash; mesh.vertices.Add(newvertex.hash, newvertex); if (mesh.steinerleft > 0) { mesh.steinerleft--; } } else if (success == InsertVertexResult.Encroaching) { // If the newly inserted vertex encroaches upon a subsegment, // delete the new vertex. mesh.UndoVertex(); } else if (success == InsertVertexResult.Violating) { // Failed to insert the new vertex, but some subsegment was // marked as being encroached. } else { // success == DUPLICATEVERTEX // Couldn't insert the new vertex because a vertex is already there. if (Behavior.Verbose) { logger.Warning("New vertex falls on existing vertex.", "Quality.SplitTriangle()"); errorflag = true; } } } if (errorflag) { logger.Error("The new vertex is at the circumcenter of triangle: This probably " + "means that I am trying to refine triangles to a smaller size than can be " + "accommodated by the finite precision of floating point arithmetic.", "Quality.SplitTriangle()"); throw new Exception("The new vertex is at the circumcenter of triangle."); } } }