private static List <Point> GetResult(Partition partition) { var result = new List <Point>(); QuadEdge baseEdge = QuadEdge.ConnectLeft(partition.Left.Inverse, partition.Right); QuadEdge e = baseEdge.DestinationNext.Inverse; Point u = baseEdge.Destination; while (true) { while (e != baseEdge && !CanMoveForward(e.DestinationNext, baseEdge)) { u = e.Destination; e = e.DestinationNext.Inverse; } if (e != baseEdge) { result.Add(e.Origin); result.Add(e.Destination); } while (!CanMoveForward(e.OriginNext, baseEdge)) { result.Add(u); if (u == baseEdge.Destination) { baseEdge.Delete(); return(result); } e = e.OriginNext.Inverse; while (CanMoveForward(e.DestinationNext, baseEdge)) { e = e.DestinationNext; } u = e.Origin; result.Add(e.Origin); result.Add(e.Destination); } e = e.OriginNext; } }
/// <summary> /// Triangulate the set of points using a divide and conquer approach. /// </summary> private QuadEdge <T>[] Triangulate(Vec3[] pts) { QuadEdge <T> a, b, c, nextCand; // Only two points -> One edge if (pts.Length == 2) { a = QuadEdge <T> .MakeEdge(pts[0], pts[1]); return(new QuadEdge <T>[] { a, a.Sym }); } // Only tree points else if (pts.Length == 3) { a = QuadEdge <T> .MakeEdge(pts[0], pts[1]); b = QuadEdge <T> .MakeEdge(pts[1], pts[2]); QuadEdge <T> .Splice(a.Sym, b); // Closing triangle if (Geometry.Ccw(pts[0], pts[1], pts[2])) { c = QuadEdge <T> .Connect(b, a); return(new QuadEdge <T>[] { a, b.Sym }); } else if (Geometry.Ccw(pts[0], pts[2], pts[1])) { c = QuadEdge <T> .Connect(b, a); return(new QuadEdge <T>[] { c.Sym, c }); } else { // Points are collinear return(new QuadEdge <T>[] { a, b.Sym }); } } // SPLITTING // Divide them halfsize recursively int halfLength = (pts.Length + 1) / 2; QuadEdge <T>[] left = Triangulate(pts.Take(halfLength).ToArray()); QuadEdge <T>[] right = Triangulate(pts.Skip(halfLength).ToArray()); // MERGING // From left to right QuadEdge <T> ldo = left[0]; QuadEdge <T> ldi = left[1]; QuadEdge <T> rdi = right[0]; QuadEdge <T> rdo = right[1]; // Compute lower common tangent to be able to merge both triangulations bool crossEdgeNotFound = true; while (crossEdgeNotFound) { if (Geometry.LeftOf(rdi.Origin, ldi)) { ldi = ldi.Lnext; } else if (Geometry.RightOf(ldi.Origin, rdi)) { rdi = rdi.Rprev; } else { crossEdgeNotFound = false; } } // Start merging // 1) Creation of the baseEdge quad edge (See Fig.21) QuadEdge <T> baseEdge = QuadEdge <T> .Connect(rdi.Sym, ldi); if (ldi.Origin == ldo.Origin) { ldo = baseEdge.Sym; } if (rdi.Origin == rdo.Origin) { rdo = baseEdge; } // 2) Rising bubble (See Fig. 22) bool upperCommonTangentNotFound = true; while (upperCommonTangentNotFound) { // Locate the first L site (lCand.Destination) to be encountered // by the rising bubble, and delete L edges out of baseEdge.Destination // that fail the circle test. QuadEdge <T> lCand = baseEdge.Sym.Onext; if (IsValid(lCand, baseEdge)) { while (Geometry.InCircumCercle2D(lCand.Onext.Destination, baseEdge.Destination, baseEdge.Origin, lCand.Destination)) { nextCand = lCand.Onext; QuadEdge <T> .Delete(lCand); lCand = nextCand; } } // Same for the right part (Symetrically) QuadEdge <T> rCand = baseEdge.Oprev; if (IsValid(rCand, baseEdge)) { while (Geometry.InCircumCercle2D(rCand.Oprev.Destination, baseEdge.Destination, baseEdge.Origin, rCand.Destination)) { nextCand = rCand.Oprev; QuadEdge <T> .Delete(rCand); rCand = nextCand; } } // Upper common tangent is baseEdge if (!IsValid(lCand, baseEdge) && !IsValid(rCand, baseEdge)) { upperCommonTangentNotFound = false; } // Construct new cross edge between left and right // The next cross edge is to be connected to either lcand.Dest or rCand.Dest // If both are valid, then choose the appropriate one using the // Geometry.InCircumCercle2D test else if (!IsValid(lCand, baseEdge) || ( IsValid(rCand, baseEdge) && Geometry.InCircumCercle2D(rCand.Destination, lCand.Destination, lCand.Origin, rCand.Origin) ) ) { // Cross edge baseEdge added from rCand.Destination to basel.Destination baseEdge = QuadEdge <T> .Connect(rCand, baseEdge.Sym); } else { // Cross edge baseEdge added from baseEdge.Origin to lCand.Destination baseEdge = QuadEdge <T> .Connect(baseEdge.Sym, lCand.Sym); } } return(new QuadEdge <T>[] { ldo, rdo }); }
/// <summary> /// Insert a new site inside an existing delaunay triangulation. New site /// must be inside the convex hull of previoulsy added sites. /// Set <paramref name="safe"/> to true to first test if new site is correct. /// </summary> /// <param name="newPos">The position to of new site</param> /// <param name="edge">Edge used to start locate process. Can be used to speed up search.</param> /// <param name="safe">If true, check if <paramref name="newPos"/> inside the convex hull.</param> public bool Insert(Vec3 newPos, QuadEdge <T> edge = null, bool safe = false) { if (safe) { bool result = InsideConvexHull(newPos); if (!result) { // Cannot add site not already inside the convex hull return(false); } } // Start somewhere if no hint if (edge == null) { edge = _leftRightEdges[1]; } // Locate edge (must be inside the boundary) QuadEdge <T> foundE = Locate(newPos, edge, safe: false); // Site already triangulated if (Geometry.AlmostEquals(foundE.Origin, newPos) || Geometry.AlmostEquals(foundE.Destination, newPos)) { return(false); } // On an edge ? if (Geometry.AlmostColinear(foundE.Origin, foundE.Destination, newPos)) { var temp = foundE.Oprev; QuadEdge <T> .Delete(foundE); foundE = temp; } // Create new edge to connect new site to neighbors QuadEdge <T> baseE = QuadEdge <T> .MakeEdge(foundE.Origin, newPos); Vec3 first = baseE.Origin; QuadEdge <T> .Splice(baseE, foundE); // Up to 4 vertices if new site on an edge do { baseE = QuadEdge <T> .Connect(foundE, baseE.Sym); foundE = baseE.Oprev; } while (foundE.Destination != first); // Fill star shaped polygon and swap suspect edges // Adding a new point can break old condition about InCircle test foundE = baseE.Oprev; bool shouldExit = false; do { var tempE = foundE.Oprev; if (Geometry.RightOf(tempE.Destination, foundE) && Geometry.InCircumCercle2D(newPos, foundE.Origin, tempE.Destination, foundE.Destination)) { QuadEdge <T> .Swap(foundE); // tempE != foundE.Oprev after swap foundE = foundE.Oprev; } else if (foundE.Origin == first) { // No more suspect edge ... exit shouldExit = true; } else { // Get next suspect edge from top to bottom foundE = foundE.Onext.Lprev; } } while (!shouldExit); return(true); }
private static Partition Merge(Partition leftPartition, Partition rightPartition) { QuadEdge left = leftPartition.Left; //ldo QuadEdge right = rightPartition.Right; //rdo LowestCommonTangent(leftPartition, rightPartition, out QuadEdge lowLeft, out QuadEdge lowRight); //ldi, rdi QuadEdge edgeBase = QuadEdge.ConnectLeft(lowRight.Inverse, lowLeft); QuadEdge lcand = edgeBase.RightPrevious; QuadEdge rcand = edgeBase.OriginPrevious; if (edgeBase.Origin == right.Origin) { right = edgeBase; } if (edgeBase.Destination == left.Origin) { left = edgeBase.Inverse; } while (true) { QuadEdge temp = lcand.OriginNext; if (Point.IsCounterClockWise(edgeBase.Origin, temp.Destination, edgeBase.Destination)) { while (edgeBase.Origin.InCircle(lcand.Destination, temp.Destination, lcand.Origin)) { lcand.Delete(); lcand = temp; temp = lcand.OriginNext; } } temp = rcand.OriginPrevious; if (Point.IsCounterClockWise(edgeBase.Origin, temp.Destination, edgeBase.Destination)) { while (edgeBase.Destination.InCircle(temp.Destination, rcand.Destination, rcand.Origin)) { rcand.Delete(); rcand = temp; temp = rcand.OriginPrevious; } } bool leftValid = Point.IsCounterClockWise(edgeBase.Origin, lcand.Destination, edgeBase.Destination); bool rightValid = Point.IsCounterClockWise(edgeBase.Origin, rcand.Destination, edgeBase.Destination); if (!leftValid && !rightValid) { break; } if (!leftValid || rightValid && rcand.Destination.InCircle(lcand.Destination, lcand.Origin, rcand.Origin)) { edgeBase = QuadEdge.ConnectLeft(rcand, edgeBase.Inverse); rcand = edgeBase.Inverse.LeftNext; } else { edgeBase = QuadEdge.ConnectRight(lcand, edgeBase).Inverse; lcand = edgeBase.RightPrevious; } } return(new Partition { Left = left, Right = right }); }