/// <summary> /// Find next subsegment in sequence. [next(ab) -> b*] /// </summary> /// <remarks>snext() finds the next subsegment (from the same segment) in /// sequence; one whose origin is the input subsegment's destination. /// </remarks> public void Next(ref Osub o2) { o2 = seg.subsegs[1 - orient]; //sdecode(sptr, o2); }
/// <summary> /// Reverse the orientation of a subsegment. [sym(ab) -> ba] /// </summary> /// <remarks>ssym() toggles the orientation of a subsegment. /// </remarks> public void Sym(ref Osub o2) { o2.seg = seg; o2.orient = 1 - orient; }
/// <summary> /// Find adjoining subsegment with the same origin. [pivot(ab) -> a*] /// </summary> /// <remarks>spivot() finds the other subsegment (from the same segment) /// that shares the same origin. /// </remarks> public void Pivot(ref Osub o2) { o2 = seg.subsegs[orient]; //sdecode(sptr, o2); }
/// <summary> /// Copy a subsegment. /// </summary> public void Copy(ref Osub o2) { o2.seg = seg; o2.orient = orient; }
/// <summary> /// Test for equality of subsegments. /// </summary> public bool Equal(Osub o2) { return ((seg == o2.seg) && (orient == o2.orient)); }
/// <summary> /// Insert a vertex into a Delaunay triangulation, performing flips as necessary /// to maintain the Delaunay property. /// </summary> /// <param name="newvertex">The point to be inserted.</param> /// <param name="searchtri">The triangle to start the search.</param> /// <param name="splitseg">Segment to split.</param> /// <param name="segmentflaws">Check for creation of encroached subsegments.</param> /// <param name="triflaws">Check for creation of bad quality triangles.</param> /// <returns>If a duplicate vertex or violated segment does not prevent the /// vertex from being inserted, the return value will be ENCROACHINGVERTEX if /// the vertex encroaches upon a subsegment (and checking is enabled), or /// SUCCESSFULVERTEX otherwise. In either case, 'searchtri' is set to a handle /// whose origin is the newly inserted vertex.</returns> /// <remarks> /// The point 'newvertex' is located. If 'searchtri.triangle' is not NULL, /// the search for the containing triangle begins from 'searchtri'. If /// 'searchtri.triangle' is NULL, a full point location procedure is called. /// If 'insertvertex' is found inside a triangle, the triangle is split into /// three; if 'insertvertex' lies on an edge, the edge is split in two, /// thereby splitting the two adjacent triangles into four. Edge flips are /// used to restore the Delaunay property. If 'insertvertex' lies on an /// existing vertex, no action is taken, and the value DUPLICATEVERTEX is /// returned. On return, 'searchtri' is set to a handle whose origin is the /// existing vertex. /// /// InsertVertex() does not use flip() for reasons of speed; some /// information can be reused from edge flip to edge flip, like the /// locations of subsegments. /// /// Param 'splitseg': Normally, the parameter 'splitseg' is set to NULL, /// implying that no subsegment should be split. In this case, if 'insertvertex' /// is found to lie on a segment, no action is taken, and the value VIOLATINGVERTEX /// is returned. On return, 'searchtri' is set to a handle whose primary edge is the /// violated subsegment. /// If the calling routine wishes to split a subsegment by inserting a vertex in it, /// the parameter 'splitseg' should be that subsegment. In this case, 'searchtri' /// MUST be the triangle handle reached by pivoting from that subsegment; no point /// location is done. /// /// Param 'segmentflaws': Flags that indicate whether or not there should /// be checks for the creation of encroached subsegments. If a newly inserted /// vertex encroaches upon subsegments, these subsegments are added to the list /// of subsegments to be split if 'segmentflaws' is set. /// /// Param 'triflaws': Flags that indicate whether or not there should be /// checks for the creation of bad quality triangles. If bad triangles are /// created, these are added to the queue if 'triflaws' is set. /// </remarks> internal InsertVertexResult InsertVertex(Vertex newvertex, ref Otri searchtri, ref Osub splitseg, bool segmentflaws, bool triflaws) { Otri horiz = default(Otri); Otri top = default(Otri); Otri botleft = default(Otri), botright = default(Otri); Otri topleft = default(Otri), topright = default(Otri); Otri newbotleft = default(Otri), newbotright = default(Otri); Otri newtopright = default(Otri); Otri botlcasing = default(Otri), botrcasing = default(Otri); Otri toplcasing = default(Otri), toprcasing = default(Otri); Otri testtri = default(Otri); Osub botlsubseg = default(Osub), botrsubseg = default(Osub); Osub toplsubseg = default(Osub), toprsubseg = default(Osub); Osub brokensubseg = default(Osub); Osub checksubseg = default(Osub); Osub rightsubseg = default(Osub); Osub newsubseg = default(Osub); BadSubseg encroached; //FlipStacker newflip; Vertex first; Vertex leftvertex, rightvertex, botvertex, topvertex, farvertex; Vertex segmentorg, segmentdest; int region; double area; InsertVertexResult success; LocateResult intersect; bool doflip; bool mirrorflag; bool enq; if (splitseg.seg == null) { // Find the location of the vertex to be inserted. Check if a good // starting triangle has already been provided by the caller. if (searchtri.triangle == dummytri) { // Find a boundary triangle. horiz.triangle = dummytri; horiz.orient = 0; horiz.SymSelf(); // Search for a triangle containing 'newvertex'. intersect = locator.Locate(newvertex, ref horiz); } else { // Start searching from the triangle provided by the caller. searchtri.Copy(ref horiz); intersect = locator.PreciseLocate(newvertex, ref horiz, true); } } else { // The calling routine provides the subsegment in which // the vertex is inserted. searchtri.Copy(ref horiz); intersect = LocateResult.OnEdge; } if (intersect == LocateResult.OnVertex) { // There's already a vertex there. Return in 'searchtri' a triangle // whose origin is the existing vertex. horiz.Copy(ref searchtri); locator.Update(ref horiz); return InsertVertexResult.Duplicate; } if ((intersect == LocateResult.OnEdge) || (intersect == LocateResult.Outside)) { // The vertex falls on an edge or boundary. if (checksegments && (splitseg.seg == null)) { // Check whether the vertex falls on a subsegment. horiz.SegPivot(ref brokensubseg); if (brokensubseg.seg != dummysub) { // The vertex falls on a subsegment, and hence will not be inserted. if (segmentflaws) { enq = behavior.NoBisect != 2; if (enq && (behavior.NoBisect == 1)) { // This subsegment may be split only if it is an // internal boundary. horiz.Sym(ref testtri); enq = testtri.triangle != dummytri; } if (enq) { // Add the subsegment to the list of encroached subsegments. encroached = new BadSubseg(); encroached.encsubseg = brokensubseg; encroached.subsegorg = brokensubseg.Org(); encroached.subsegdest = brokensubseg.Dest(); quality.AddBadSubseg(encroached); } } // Return a handle whose primary edge contains the vertex, // which has not been inserted. horiz.Copy(ref searchtri); locator.Update(ref horiz); return InsertVertexResult.Violating; } } // Insert the vertex on an edge, dividing one triangle into two (if // the edge lies on a boundary) or two triangles into four. horiz.Lprev(ref botright); botright.Sym(ref botrcasing); horiz.Sym(ref topright); // Is there a second triangle? (Or does this edge lie on a boundary?) mirrorflag = topright.triangle != dummytri; if (mirrorflag) { topright.LnextSelf(); topright.Sym(ref toprcasing); MakeTriangle(ref newtopright); } else { // Splitting a boundary edge increases the number of boundary edges. hullsize++; } MakeTriangle(ref newbotright); // Set the vertices of changed and new triangles. rightvertex = horiz.Org(); leftvertex = horiz.Dest(); botvertex = horiz.Apex(); newbotright.SetOrg(botvertex); newbotright.SetDest(rightvertex); newbotright.SetApex(newvertex); horiz.SetOrg(newvertex); // Set the region of a new triangle. newbotright.triangle.region = botright.triangle.region; if (behavior.VarArea) { // Set the area constraint of a new triangle. newbotright.triangle.area = botright.triangle.area; } if (mirrorflag) { topvertex = topright.Dest(); newtopright.SetOrg(rightvertex); newtopright.SetDest(topvertex); newtopright.SetApex(newvertex); topright.SetOrg(newvertex); // Set the region of another new triangle. newtopright.triangle.region = topright.triangle.region; if (behavior.VarArea) { // Set the area constraint of another new triangle. newtopright.triangle.area = topright.triangle.area; } } // There may be subsegments that need to be bonded // to the new triangle(s). if (checksegments) { botright.SegPivot(ref botrsubseg); if (botrsubseg.seg != dummysub) { botright.SegDissolve(); newbotright.SegBond(ref botrsubseg); } if (mirrorflag) { topright.SegPivot(ref toprsubseg); if (toprsubseg.seg != dummysub) { topright.SegDissolve(); newtopright.SegBond(ref toprsubseg); } } } // Bond the new triangle(s) to the surrounding triangles. newbotright.Bond(ref botrcasing); newbotright.LprevSelf(); newbotright.Bond(ref botright); newbotright.LprevSelf(); if (mirrorflag) { newtopright.Bond(ref toprcasing); newtopright.LnextSelf(); newtopright.Bond(ref topright); newtopright.LnextSelf(); newtopright.Bond(ref newbotright); } if (splitseg.seg != null) { // Split the subsegment into two. splitseg.SetDest(newvertex); segmentorg = splitseg.SegOrg(); segmentdest = splitseg.SegDest(); splitseg.SymSelf(); splitseg.Pivot(ref rightsubseg); InsertSubseg(ref newbotright, splitseg.seg.boundary); newbotright.SegPivot(ref newsubseg); newsubseg.SetSegOrg(segmentorg); newsubseg.SetSegDest(segmentdest); splitseg.Bond(ref newsubseg); newsubseg.SymSelf(); newsubseg.Bond(ref rightsubseg); splitseg.SymSelf(); // Transfer the subsegment's boundary marker to the vertex if required. if (newvertex.mark == 0) { newvertex.mark = splitseg.seg.boundary; } } if (checkquality) { flipstack.Clear(); flipstack.Push(default(Otri)); // Dummy flip (see UndoVertex) flipstack.Push(horiz); } // Position 'horiz' on the first edge to check for // the Delaunay property. horiz.LnextSelf(); } else { // Insert the vertex in a triangle, splitting it into three. horiz.Lnext(ref botleft); horiz.Lprev(ref botright); botleft.Sym(ref botlcasing); botright.Sym(ref botrcasing); MakeTriangle(ref newbotleft); MakeTriangle(ref newbotright); // Set the vertices of changed and new triangles. rightvertex = horiz.Org(); leftvertex = horiz.Dest(); botvertex = horiz.Apex(); newbotleft.SetOrg(leftvertex); newbotleft.SetDest(botvertex); newbotleft.SetApex(newvertex); newbotright.SetOrg(botvertex); newbotright.SetDest(rightvertex); newbotright.SetApex(newvertex); horiz.SetApex(newvertex); // Set the region of the new triangles. newbotleft.triangle.region = horiz.triangle.region; newbotright.triangle.region = horiz.triangle.region; if (behavior.VarArea) { // Set the area constraint of the new triangles. area = horiz.triangle.area; newbotleft.triangle.area = area; newbotright.triangle.area = area; } // There may be subsegments that need to be bonded // to the new triangles. if (checksegments) { botleft.SegPivot(ref botlsubseg); if (botlsubseg.seg != dummysub) { botleft.SegDissolve(); newbotleft.SegBond(ref botlsubseg); } botright.SegPivot(ref botrsubseg); if (botrsubseg.seg != dummysub) { botright.SegDissolve(); newbotright.SegBond(ref botrsubseg); } } // Bond the new triangles to the surrounding triangles. newbotleft.Bond(ref botlcasing); newbotright.Bond(ref botrcasing); newbotleft.LnextSelf(); newbotright.LprevSelf(); newbotleft.Bond(ref newbotright); newbotleft.LnextSelf(); botleft.Bond(ref newbotleft); newbotright.LprevSelf(); botright.Bond(ref newbotright); if (checkquality) { flipstack.Clear(); flipstack.Push(horiz); } } // The insertion is successful by default, unless an encroached // subsegment is found. success = InsertVertexResult.Successful; // Circle around the newly inserted vertex, checking each edge opposite it // for the Delaunay property. Non-Delaunay edges are flipped. 'horiz' is // always the edge being checked. 'first' marks where to stop circling. first = horiz.Org(); rightvertex = first; leftvertex = horiz.Dest(); // Circle until finished. while (true) { // By default, the edge will be flipped. doflip = true; if (checksegments) { // Check for a subsegment, which cannot be flipped. horiz.SegPivot(ref checksubseg); if (checksubseg.seg != dummysub) { // The edge is a subsegment and cannot be flipped. doflip = false; if (segmentflaws) { // Does the new vertex encroach upon this subsegment? if (quality.CheckSeg4Encroach(ref checksubseg) > 0) { success = InsertVertexResult.Encroaching; } } } } if (doflip) { // Check if the edge is a boundary edge. horiz.Sym(ref top); if (top.triangle == dummytri) { // The edge is a boundary edge and cannot be flipped. doflip = false; } else { // Find the vertex on the other side of the edge. farvertex = top.Apex(); // In the incremental Delaunay triangulation algorithm, any of // 'leftvertex', 'rightvertex', and 'farvertex' could be vertices // of the triangular bounding box. These vertices must be // treated as if they are infinitely distant, even though their // "coordinates" are not. if ((leftvertex == infvertex1) || (leftvertex == infvertex2) || (leftvertex == infvertex3)) { // 'leftvertex' is infinitely distant. Check the convexity of // the boundary of the triangulation. 'farvertex' might be // infinite as well, but trust me, this same condition should // be applied. doflip = Primitives.CounterClockwise(newvertex, rightvertex, farvertex) > 0.0; } else if ((rightvertex == infvertex1) || (rightvertex == infvertex2) || (rightvertex == infvertex3)) { // 'rightvertex' is infinitely distant. Check the convexity of // the boundary of the triangulation. 'farvertex' might be // infinite as well, but trust me, this same condition should // be applied. doflip = Primitives.CounterClockwise(farvertex, leftvertex, newvertex) > 0.0; } else if ((farvertex == infvertex1) || (farvertex == infvertex2) || (farvertex == infvertex3)) { // 'farvertex' is infinitely distant and cannot be inside // the circumcircle of the triangle 'horiz'. doflip = false; } else { // Test whether the edge is locally Delaunay. doflip = Primitives.InCircle(leftvertex, newvertex, rightvertex, farvertex) > 0.0; } if (doflip) { // We made it! Flip the edge 'horiz' by rotating its containing // quadrilateral (the two triangles adjacent to 'horiz'). // Identify the casing of the quadrilateral. top.Lprev(ref topleft); topleft.Sym(ref toplcasing); top.Lnext(ref topright); topright.Sym(ref toprcasing); horiz.Lnext(ref botleft); botleft.Sym(ref botlcasing); horiz.Lprev(ref botright); botright.Sym(ref botrcasing); // Rotate the quadrilateral one-quarter turn counterclockwise. topleft.Bond(ref botlcasing); botleft.Bond(ref botrcasing); botright.Bond(ref toprcasing); topright.Bond(ref toplcasing); if (checksegments) { // Check for subsegments and rebond them to the quadrilateral. topleft.SegPivot(ref toplsubseg); botleft.SegPivot(ref botlsubseg); botright.SegPivot(ref botrsubseg); topright.SegPivot(ref toprsubseg); if (toplsubseg.seg == dummysub) { topright.SegDissolve(); } else { topright.SegBond(ref toplsubseg); } if (botlsubseg.seg == dummysub) { topleft.SegDissolve(); } else { topleft.SegBond(ref botlsubseg); } if (botrsubseg.seg == dummysub) { botleft.SegDissolve(); } else { botleft.SegBond(ref botrsubseg); } if (toprsubseg.seg == dummysub) { botright.SegDissolve(); } else { botright.SegBond(ref toprsubseg); } } // New vertex assignments for the rotated quadrilateral. horiz.SetOrg(farvertex); horiz.SetDest(newvertex); horiz.SetApex(rightvertex); top.SetOrg(newvertex); top.SetDest(farvertex); top.SetApex(leftvertex); // Assign region. // TODO: check region ok (no Math.Min necessary) region = Math.Min(top.triangle.region, horiz.triangle.region); top.triangle.region = region; horiz.triangle.region = region; if (behavior.VarArea) { if ((top.triangle.area <= 0.0) || (horiz.triangle.area <= 0.0)) { area = -1.0; } else { // Take the average of the two triangles' area constraints. // This prevents small area constraints from migrating a // long, long way from their original location due to flips. area = 0.5 * (top.triangle.area + horiz.triangle.area); } top.triangle.area = area; horiz.triangle.area = area; } if (checkquality) { flipstack.Push(horiz); } // On the next iterations, consider the two edges that were exposed (this // is, are now visible to the newly inserted vertex) by the edge flip. horiz.LprevSelf(); leftvertex = farvertex; } } } if (!doflip) { // The handle 'horiz' is accepted as locally Delaunay. if (triflaws) { // Check the triangle 'horiz' for quality. quality.TestTriangle(ref horiz); } // Look for the next edge around the newly inserted vertex. horiz.LnextSelf(); horiz.Sym(ref testtri); // Check for finishing a complete revolution about the new vertex, or // falling outside of the triangulation. The latter will happen when // a vertex is inserted at a boundary. if ((leftvertex == first) || (testtri.triangle == dummytri)) { // We're done. Return a triangle whose origin is the new vertex. horiz.Lnext(ref searchtri); Otri recenttri = default(Otri); horiz.Lnext(ref recenttri); locator.Update(ref recenttri); return success; } // Finish finding the next edge around the newly inserted vertex. testtri.Lnext(ref horiz); rightvertex = leftvertex; leftvertex = horiz.Dest(); } } }
/// <summary> /// Bond two subsegments together. [bond(abc, ba)] /// </summary> public void Bond(ref Osub o2) { seg.subsegs[orient] = o2; o2.seg.subsegs[o2.orient] = this; }
/// <summary> /// Create a new subsegment with orientation zero. /// </summary> /// <param name="newsubseg">Reference to the new subseg.</param> internal void MakeSegment(ref Osub newsubseg) { Segment seg = new Segment(); seg.hash = this.hash_seg++; newsubseg.seg = seg; newsubseg.orient = 0; subsegs.Add(seg.hash, seg); }
/// <summary> /// Find the intersection of an existing segment and a segment that is being /// inserted. Insert a vertex at the intersection, splitting an existing subsegment. /// </summary> /// <param name="splittri"></param> /// <param name="splitsubseg"></param> /// <param name="endpoint2"></param> /// <remarks> /// The segment being inserted connects the apex of splittri to endpoint2. /// splitsubseg is the subsegment being split, and MUST adjoin splittri. /// Hence, endpoints of the subsegment being split are the origin and /// destination of splittri. /// /// On completion, splittri is a handle having the newly inserted /// intersection point as its origin, and endpoint1 as its destination. /// </remarks> private void SegmentIntersection(ref Otri splittri, ref Osub splitsubseg, Vertex endpoint2) { Osub opposubseg = default(Osub); Vertex endpoint1; Vertex torg, tdest; Vertex leftvertex, rightvertex; Vertex newvertex; InsertVertexResult success; double ex, ey; double tx, ty; double etx, ety; double split, denom; // Find the other three segment endpoints. endpoint1 = splittri.Apex(); torg = splittri.Org(); tdest = splittri.Dest(); // Segment intersection formulae; see the Antonio reference. tx = tdest.x - torg.x; ty = tdest.y - torg.y; ex = endpoint2.x - endpoint1.x; ey = endpoint2.y - endpoint1.y; etx = torg.x - endpoint2.x; ety = torg.y - endpoint2.y; denom = ty * ex - tx * ey; if (denom == 0.0) { logger.Error("Attempt to find intersection of parallel segments.", "Mesh.SegmentIntersection()"); throw new Exception("Attempt to find intersection of parallel segments."); } split = (ey * etx - ex * ety) / denom; // Create the new vertex. newvertex = new Vertex( torg.x + split * (tdest.x - torg.x), torg.y + split * (tdest.y - torg.y), splitsubseg.seg.boundary, this.nextras); newvertex.hash = this.hash_vtx++; newvertex.id = newvertex.hash; // Interpolate its attributes. for (int i = 0; i < nextras; i++) { newvertex.attributes[i] = torg.attributes[i] + split * (tdest.attributes[i] - torg.attributes[i]); } vertices.Add(newvertex.hash, newvertex); // Insert the intersection vertex. This should always succeed. success = InsertVertex(newvertex, ref splittri, ref splitsubseg, false, false); if (success != InsertVertexResult.Successful) { logger.Error("Failure to split a segment.", "Mesh.SegmentIntersection()"); throw new Exception("Failure to split a segment."); } // Record a triangle whose origin is the new vertex. newvertex.tri = splittri; if (steinerleft > 0) { steinerleft--; } // Divide the segment into two, and correct the segment endpoints. splitsubseg.SymSelf(); splitsubseg.Pivot(ref opposubseg); splitsubseg.Dissolve(); opposubseg.Dissolve(); do { splitsubseg.SetSegOrg(newvertex); splitsubseg.NextSelf(); } while (splitsubseg.seg != Mesh.dummysub); do { opposubseg.SetSegOrg(newvertex); opposubseg.NextSelf(); } while (opposubseg.seg != Mesh.dummysub); // Inserting the vertex may have caused edge flips. We wish to rediscover // the edge connecting endpoint1 to the new intersection vertex. FindDirection(ref splittri, endpoint1); rightvertex = splittri.Dest(); leftvertex = splittri.Apex(); if ((leftvertex.x == endpoint1.x) && (leftvertex.y == endpoint1.y)) { splittri.OnextSelf(); } else if ((rightvertex.x != endpoint1.x) || (rightvertex.y != endpoint1.y)) { logger.Error("Topological inconsistency after splitting a segment.", "Mesh.SegmentIntersection()"); throw new Exception("Topological inconsistency after splitting a segment."); } // 'splittri' should have destination endpoint1. }
/// <summary> /// Check a subsegment to see if it is encroached; add it to the list if it is. /// </summary> /// <param name="testsubseg">The subsegment to check.</param> /// <returns>Returns a nonzero value if the subsegment is encroached.</returns> /// <remarks> /// A subsegment is encroached if there is a vertex in its diametral lens. /// For Ruppert's algorithm (-D switch), the "diametral lens" is the /// diametral circle. For Chew's algorithm (default), the diametral lens is /// just big enough to enclose two isosceles triangles whose bases are the /// subsegment. Each of the two isosceles triangles has two angles equal /// to 'b.minangle'. /// /// Chew's algorithm does not require diametral lenses at all--but they save /// time. Any vertex inside a subsegment's diametral lens implies that the /// triangle adjoining the subsegment will be too skinny, so it's only a /// matter of time before the encroaching vertex is deleted by Chew's /// algorithm. It's faster to simply not insert the doomed vertex in the /// first place, which is why I use diametral lenses with Chew's algorithm. /// </remarks> public int CheckSeg4Encroach(ref Osub testsubseg) { Otri neighbortri = default(Otri); Osub testsym = default(Osub); BadSubseg encroachedseg; double dotproduct; int encroached; int sides; Vertex eorg, edest, eapex; encroached = 0; sides = 0; eorg = testsubseg.Org(); edest = testsubseg.Dest(); // Check one neighbor of the subsegment. testsubseg.TriPivot(ref neighbortri); // Does the neighbor exist, or is this a boundary edge? if (neighbortri.triangle != Mesh.dummytri) { sides++; // Find a vertex opposite this subsegment. eapex = neighbortri.Apex(); // Check whether the apex is in the diametral lens of the subsegment // (the diametral circle if 'conformdel' is set). A dot product // of two sides of the triangle is used to check whether the angle // at the apex is greater than (180 - 2 'minangle') degrees (for // lenses; 90 degrees for diametral circles). dotproduct = (eorg.x - eapex.x) * (edest.x - eapex.x) + (eorg.y - eapex.y) * (edest.y - eapex.y); if (dotproduct < 0.0) { if (behavior.ConformingDelaunay || (dotproduct * dotproduct >= (2.0 * behavior.goodAngle - 1.0) * (2.0 * behavior.goodAngle - 1.0) * ((eorg.x - eapex.x) * (eorg.x - eapex.x) + (eorg.y - eapex.y) * (eorg.y - eapex.y)) * ((edest.x - eapex.x) * (edest.x - eapex.x) + (edest.y - eapex.y) * (edest.y - eapex.y)))) { encroached = 1; } } } // Check the other neighbor of the subsegment. testsubseg.Sym(ref testsym); testsym.TriPivot(ref neighbortri); // Does the neighbor exist, or is this a boundary edge? if (neighbortri.triangle != Mesh.dummytri) { sides++; // Find the other vertex opposite this subsegment. eapex = neighbortri.Apex(); // Check whether the apex is in the diametral lens of the subsegment // (or the diametral circle, if 'conformdel' is set). dotproduct = (eorg.x - eapex.x) * (edest.x - eapex.x) + (eorg.y - eapex.y) * (edest.y - eapex.y); if (dotproduct < 0.0) { if (behavior.ConformingDelaunay || (dotproduct * dotproduct >= (2.0 * behavior.goodAngle - 1.0) * (2.0 * behavior.goodAngle - 1.0) * ((eorg.x - eapex.x) * (eorg.x - eapex.x) + (eorg.y - eapex.y) * (eorg.y - eapex.y)) * ((edest.x - eapex.x) * (edest.x - eapex.x) + (edest.y - eapex.y) * (edest.y - eapex.y)))) { encroached += 2; } } } if (encroached > 0 && (behavior.NoBisect == 0 || ((behavior.NoBisect == 1) && (sides == 2)))) { // Add the subsegment to the list of encroached subsegments. // Be sure to get the orientation right. encroachedseg = new BadSubseg(); if (encroached == 1) { encroachedseg.encsubseg = testsubseg; encroachedseg.subsegorg = eorg; encroachedseg.subsegdest = edest; } else { encroachedseg.encsubseg = testsym; encroachedseg.subsegorg = edest; encroachedseg.subsegdest = eorg; } badsubsegs.Enqueue(encroachedseg); } return encroached; }
/// <summary> /// Check if given triangle is blinded by given segment. /// </summary> /// <param name="tri">Triangle.</param> /// <param name="seg">Segments</param> /// <returns>Returns true, if the triangle is blinded.</returns> private bool TriangleIsBlinded(ref Otri tri, ref Osub seg) { Point c, pt; Vertex torg = tri.Org(); Vertex tdest = tri.Dest(); Vertex tapex = tri.Apex(); Vertex sorg = seg.Org(); Vertex sdest = seg.Dest(); c = this.points[tri.triangle.id]; if (SegmentsIntersect(sorg, sdest, c, torg, out pt, true)) { return true; } if (SegmentsIntersect(sorg, sdest, c, tdest, out pt, true)) { return true; } if (SegmentsIntersect(sorg, sdest, c, tapex, out pt, true)) { return true; } return false; }
/// <summary> /// Test for equality of subsegments. /// </summary> public bool Equal(Osub o2) { return((seg == o2.seg) && (orient == o2.orient)); }
/// <summary> /// Bond a triangle to a subsegment. /// </summary> public void SegBond(ref Osub os) { triangle.subsegs[orient] = os; os.seg.triangles[os.orient] = this; }
/// <summary> /// Finds a subsegment abutting a triangle. /// </summary> public void SegPivot(ref Osub os) { os = triangle.subsegs[orient]; //sdecode(sptr, osub) }