private void BuildCache(Vertex vertex, bool vertices) { cache.Clear(); Otri init = vertex.tri; Otri next = default(Otri); Otri prev = default(Otri); init.Copy(ref next); // Move counter-clockwise around the vertex. while (next.tri.id != TriangleNetMesh.DUMMY) { cache.Add(next); next.Copy(ref prev); next.Onext(); if (next.Equals(init)) { break; } } if (next.tri.id == TriangleNetMesh.DUMMY) { // We reached the boundary. To get all adjacent triangles, start // again at init triangle and now move clockwise. init.Copy(ref next); if (vertices) { // Don't forget to add the vertex lying on the boundary. prev.Lnext(); cache.Add(prev); } next.Oprev(); while (next.tri.id != TriangleNetMesh.DUMMY) { cache.Insert(0, next); next.Oprev(); if (next.Equals(init)) { break; } } } }
/// <summary> /// Removes ghost triangles. /// </summary> /// <param name="startghost"></param> /// <returns>Number of vertices on the hull.</returns> int RemoveGhosts(ref Otri startghost) { Otri searchedge = default(Otri); Otri dissolveedge = default(Otri); Otri deadtriangle = default(Otri); Vertex markorg; int hullsize; bool noPoly = !mesh.behavior.Poly; var dummytri = mesh.dummytri; // Find an edge on the convex hull to start point location from. startghost.Lprev(ref searchedge); searchedge.Sym(); dummytri.neighbors[0] = searchedge; // Remove the bounding box and count the convex hull edges. startghost.Copy(ref dissolveedge); hullsize = 0; do { hullsize++; dissolveedge.Lnext(ref deadtriangle); dissolveedge.Lprev(); dissolveedge.Sym(); // If no PSLG is involved, set the boundary markers of all the vertices // on the convex hull. If a PSLG is used, this step is done later. if (noPoly) { // Watch out for the case where all the input vertices are collinear. if (dissolveedge.tri.id != Mesh.DUMMY) { markorg = dissolveedge.Org(); if (markorg.label == 0) { markorg.label = 1; } } } // Remove a bounding triangle from a convex hull triangle. dissolveedge.Dissolve(dummytri); // Find the next bounding triangle. deadtriangle.Sym(ref dissolveedge); // Delete the bounding triangle. mesh.TriangleDealloc(deadtriangle.tri); } while (!dissolveedge.Equals(startghost)); return(hullsize); }
SplayNode FrontLocate(SplayNode splayroot, Otri bottommost, Vertex searchvertex, ref Otri searchtri, ref bool farright) { bool farrightflag; bottommost.Copy(ref searchtri); splayroot = Splay(splayroot, searchvertex, ref searchtri); farrightflag = false; while (!farrightflag && RightOfHyperbola(ref searchtri, searchvertex)) { searchtri.Onext(); farrightflag = searchtri.Equals(bottommost); } farright = farrightflag; return(splayroot); }
/// <summary> /// Construct Voronoi region for given vertex. /// </summary> /// <param name="region"></param> private void ConstructCell(VoronoiRegion region) { var vertex = region.Generator as Vertex; var vpoints = new List <Point>(); Otri f = default(Otri); Otri f_init = default(Otri); Otri f_next = default(Otri); Otri f_prev = default(Otri); Osub sub = default(Osub); // Call f_init a triangle incident to x vertex.tri.Copy(ref f_init); f_init.Copy(ref f); f_init.Onext(ref f_next); // Check if f_init lies on the boundary of the triangulation. if (f_next.tri.id == Mesh.DUMMY) { f_init.Oprev(ref f_prev); if (f_prev.tri.id != Mesh.DUMMY) { f_init.Copy(ref f_next); // Move one triangle clockwise f_init.Oprev(); f_init.Copy(ref f); } } // Go counterclockwise until we reach the border or the initial triangle. while (f_next.tri.id != Mesh.DUMMY) { // Add circumcenter of current triangle vpoints.Add(points[f.tri.id]); region.AddNeighbor(f.tri.id, regions[f.Apex().id]); if (f_next.Equals(f_init)) { // Voronoi cell is complete (bounded case). region.Add(vpoints); return; } f_next.Copy(ref f); f_next.Onext(); } // Voronoi cell is unbounded region.Bounded = false; Vertex torg, tdest, tapex; Point intersection; int sid, n = mesh.triangles.Count; // Find the boundary segment id (we use this id to number the endpoints of infinit rays). f.Lprev(ref f_next); f_next.Pivot(ref sub); sid = sub.seg.hash; // Last valid f lies at the boundary. Add the circumcenter. vpoints.Add(points[f.tri.id]); region.AddNeighbor(f.tri.id, regions[f.Apex().id]); // Check if the intersection with the bounding box has already been computed. if (!rayPoints.TryGetValue(sid, out intersection)) { torg = f.Org(); tapex = f.Apex(); intersection = IntersectionHelper.BoxRayIntersection(bounds, points[f.tri.id], torg.y - tapex.y, tapex.x - torg.x); // Set the correct id for the vertex intersection.id = n + rayIndex; points[n + rayIndex] = intersection; rayIndex++; rayPoints.Add(sid, intersection); } vpoints.Add(intersection); // Now walk from f_init clockwise till we reach the boundary. vpoints.Reverse(); f_init.Copy(ref f); f.Oprev(ref f_prev); while (f_prev.tri.id != Mesh.DUMMY) { vpoints.Add(points[f_prev.tri.id]); region.AddNeighbor(f_prev.tri.id, regions[f_prev.Apex().id]); f_prev.Copy(ref f); f_prev.Oprev(); } // Find the boundary segment id. f.Pivot(ref sub); sid = sub.seg.hash; if (!rayPoints.TryGetValue(sid, out intersection)) { // Intersection has not been computed yet. torg = f.Org(); tdest = f.Dest(); intersection = IntersectionHelper.BoxRayIntersection(bounds, points[f.tri.id], tdest.y - torg.y, torg.x - tdest.x); // Set the correct id for the vertex intersection.id = n + rayIndex; rayPoints.Add(sid, intersection); points[n + rayIndex] = intersection; rayIndex++; } vpoints.Add(intersection); region.AddNeighbor(intersection.id, regions[f.Dest().id]); // Add the new points to the region (in counter-clockwise order) vpoints.Reverse(); region.Add(vpoints); }
private void ConstructBoundaryCell(Vertex vertex) { VoronoiRegion region = new VoronoiRegion(vertex); regions.Add(region); Otri f = default(Otri); Otri f_init = default(Otri); Otri f_next = default(Otri); Otri f_prev = default(Otri); Osub sf = default(Osub); Osub sfn = default(Osub); Vertex torg, tdest, tapex, sorg, sdest; Point cc_f, cc_f_next, p; int n = _TriangleNetMesh.triangles.Count; // Call P the polygon (cell) in construction List <Point> vpoints = new List <Point>(); // Call f_init a triangle incident to x vertex.tri.Copy(ref f_init); if (f_init.Org() != vertex) { throw new Exception("ConstructBoundaryCell: inconsistent topology."); } // Let f be initialized to f_init f_init.Copy(ref f); // Call f_next the next triangle counterclockwise around x f_init.Onext(ref f_next); f_init.Oprev(ref f_prev); // Is the border to the left? if (f_prev.tri.id != TriangleNetMesh.DUMMY) { // Go clockwise until we reach the border (or the initial triangle) while (f_prev.tri.id != TriangleNetMesh.DUMMY && !f_prev.Equals(f_init)) { f_prev.Copy(ref f); f_prev.Oprev(); } f.Copy(ref f_init); f.Onext(ref f_next); } if (f_prev.tri.id == TriangleNetMesh.DUMMY) { // For vertices on the domain boundaray, add the vertex. For // internal boundaries don't add it. p = new Point(vertex.x, vertex.y); p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } // Add midpoint of start triangles' edge. torg = f.Org(); tdest = f.Dest(); p = new Point((torg.x + tdest.x) / 2, (torg.y + tdest.y) / 2); p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); // repeat ... until f = f_init do { // Call Lffnext the line going through the circumcenters of f and f_next cc_f = points[f.tri.id]; if (f_next.tri.id == TriangleNetMesh.DUMMY) { if (!f.tri.infected) { // Add last circumcenter vpoints.Add(cc_f); } // Add midpoint of last triangles' edge (chances are it has already // been added, so post process cell to remove duplicates???) torg = f.Org(); tapex = f.Apex(); p = new Point((torg.x + tapex.x) / 2, (torg.y + tapex.y) / 2); p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); break; } cc_f_next = points[f_next.tri.id]; // if f is tagged non-blind then if (!f.tri.infected) { // Insert the circumcenter of f into P vpoints.Add(cc_f); if (f_next.tri.infected) { // Call S_fnext the constrained edge blinding f_next sfn.seg = subsegMap[f_next.tri.hash]; // Insert point Lf,f_next /\ Sf_next into P if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } } } else { // Call Sf the constrained edge blinding f sf.seg = subsegMap[f.tri.hash]; sorg = sf.Org(); sdest = sf.Dest(); // if f_next is tagged non-blind then if (!f_next.tri.infected) { tdest = f.Dest(); tapex = f.Apex(); // Both circumcenters lie on the blinded side, but we // have to add the intersection with the segment. // Center of f edge dest->apex Point bisec = new Point((tdest.x + tapex.x) / 2, (tdest.y + tapex.y) / 2); // Find intersection of seg with line through f's bisector and circumcenter if (SegmentsIntersect(sorg, sdest, bisec, cc_f, out p, false)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } // Insert point Lf,f_next /\ Sf into P if (SegmentsIntersect(sorg, sdest, cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } } else { // Call Sf_next the constrained edge blinding f_next sfn.seg = subsegMap[f_next.tri.hash]; // if Sf != Sf_next then if (!sf.Equal(sfn)) { // Insert Lf,fnext /\ Sf and Lf,fnext /\ Sfnext into P if (SegmentsIntersect(sorg, sdest, cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } } else { // Both circumcenters lie on the blinded side, but we // have to add the intersection with the segment. // Center of f_next edge org->dest Point bisec = new Point((torg.x + tdest.x) / 2, (torg.y + tdest.y) / 2); // Find intersection of seg with line through f_next's bisector and circumcenter if (SegmentsIntersect(sorg, sdest, bisec, cc_f_next, out p, false)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } } } } // f <- f_next f_next.Copy(ref f); // Call f_next the next triangle counterclockwise around x f_next.Onext(); }while (!f.Equals(f_init)); // Output: Bounded Voronoi cell of x in counterclockwise order. region.Add(vpoints); }
private void ConstructCell(Vertex vertex) { VoronoiRegion region = new VoronoiRegion(vertex); regions.Add(region); Otri f = default(Otri); Otri f_init = default(Otri); Otri f_next = default(Otri); Osub sf = default(Osub); Osub sfn = default(Osub); Point cc_f, cc_f_next, p; int n = _TriangleNetMesh.triangles.Count; // Call P the polygon (cell) in construction List <Point> vpoints = new List <Point>(); // Call f_init a triangle incident to x vertex.tri.Copy(ref f_init); if (f_init.Org() != vertex) { throw new Exception("ConstructCell: inconsistent topology."); } // Let f be initialized to f_init f_init.Copy(ref f); // Call f_next the next triangle counterclockwise around x f_init.Onext(ref f_next); // repeat ... until f = f_init do { // Call Lffnext the line going through the circumcenters of f and f_next cc_f = points[f.tri.id]; cc_f_next = points[f_next.tri.id]; // if f is tagged non-blind then if (!f.tri.infected) { // Insert the circumcenter of f into P vpoints.Add(cc_f); if (f_next.tri.infected) { // Call S_fnext the constrained edge blinding f_next sfn.seg = subsegMap[f_next.tri.hash]; // Insert point Lf,f_next /\ Sf_next into P if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } } } else { // Call Sf the constrained edge blinding f sf.seg = subsegMap[f.tri.hash]; // if f_next is tagged non-blind then if (!f_next.tri.infected) { // Insert point Lf,f_next /\ Sf into P if (SegmentsIntersect(sf.Org(), sf.Dest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } } else { // Call Sf_next the constrained edge blinding f_next sfn.seg = subsegMap[f_next.tri.hash]; // if Sf != Sf_next then if (!sf.Equal(sfn)) { // Insert Lf,fnext /\ Sf and Lf,fnext /\ Sfnext into P if (SegmentsIntersect(sf.Org(), sf.Dest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true)) { p.id = n + segIndex++; segPoints.Add(p); vpoints.Add(p); } } } } // f <- f_next f_next.Copy(ref f); // Call f_next the next triangle counterclockwise around x f_next.Onext(); }while (!f.Equals(f_init)); // Output: Bounded Voronoi cell of x in counterclockwise order. region.Add(vpoints); }
public IMesh Triangulate(IList <Vertex> points, Configuration config) { this.predicates = config.Predicates(); this.mesh = new Mesh(config); this.mesh.TransferNodes(points); // Nonexistent x value used as a flag to mark circle events in sweepline // Delaunay algorithm. xminextreme = 10 * mesh.bounds.Left - 9 * mesh.bounds.Right; SweepEvent[] eventheap; SweepEvent nextevent; SweepEvent newevent; SplayNode splayroot; Otri bottommost = default(Otri); Otri searchtri = default(Otri); Otri fliptri; Otri lefttri = default(Otri); Otri righttri = default(Otri); Otri farlefttri = default(Otri); Otri farrighttri = default(Otri); Otri inserttri = default(Otri); Vertex firstvertex, secondvertex; Vertex nextvertex, lastvertex; Vertex connectvertex; Vertex leftvertex, midvertex, rightvertex; double lefttest, righttest; int heapsize; bool check4events, farrightflag = false; splaynodes = new List <SplayNode>(); splayroot = null; heapsize = points.Count; CreateHeap(out eventheap, heapsize);//, out events, out freeevents); mesh.MakeTriangle(ref lefttri); mesh.MakeTriangle(ref righttri); lefttri.Bond(ref righttri); lefttri.Lnext(); righttri.Lprev(); lefttri.Bond(ref righttri); lefttri.Lnext(); righttri.Lprev(); lefttri.Bond(ref righttri); firstvertex = eventheap[0].vertexEvent; HeapDelete(eventheap, heapsize, 0); heapsize--; do { if (heapsize == 0) { Log.Instance.Error("Input vertices are all identical.", "SweepLine.Triangulate()"); throw new Exception("Input vertices are all identical."); } secondvertex = eventheap[0].vertexEvent; HeapDelete(eventheap, heapsize, 0); heapsize--; if ((firstvertex.x == secondvertex.x) && (firstvertex.y == secondvertex.y)) { if (Log.Verbose) { Log.Instance.Warning("A duplicate vertex appeared and was ignored (ID " + secondvertex.id + ").", "SweepLine.Triangulate().1"); } secondvertex.type = VertexType.UndeadVertex; mesh.undeads++; } } while ((firstvertex.x == secondvertex.x) && (firstvertex.y == secondvertex.y)); lefttri.SetOrg(firstvertex); lefttri.SetDest(secondvertex); righttri.SetOrg(secondvertex); righttri.SetDest(firstvertex); lefttri.Lprev(ref bottommost); lastvertex = secondvertex; while (heapsize > 0) { nextevent = eventheap[0]; HeapDelete(eventheap, heapsize, 0); heapsize--; check4events = true; if (nextevent.xkey < mesh.bounds.Left) { fliptri = nextevent.otriEvent; fliptri.Oprev(ref farlefttri); Check4DeadEvent(ref farlefttri, eventheap, ref heapsize); fliptri.Onext(ref farrighttri); Check4DeadEvent(ref farrighttri, eventheap, ref heapsize); if (farlefttri.Equals(bottommost)) { fliptri.Lprev(ref bottommost); } mesh.Flip(ref fliptri); fliptri.SetApex(null); fliptri.Lprev(ref lefttri); fliptri.Lnext(ref righttri); lefttri.Sym(ref farlefttri); if (randomnation(SAMPLERATE) == 0) { fliptri.Sym(); leftvertex = fliptri.Dest(); midvertex = fliptri.Apex(); rightvertex = fliptri.Org(); splayroot = CircleTopInsert(splayroot, lefttri, leftvertex, midvertex, rightvertex, nextevent.ykey); } } else { nextvertex = nextevent.vertexEvent; if ((nextvertex.x == lastvertex.x) && (nextvertex.y == lastvertex.y)) { if (Log.Verbose) { Log.Instance.Warning("A duplicate vertex appeared and was ignored (ID " + nextvertex.id + ").", "SweepLine.Triangulate().2"); } nextvertex.type = VertexType.UndeadVertex; mesh.undeads++; check4events = false; } else { lastvertex = nextvertex; splayroot = FrontLocate(splayroot, bottommost, nextvertex, ref searchtri, ref farrightflag); //bottommost.Copy(ref searchtri); //farrightflag = false; //while (!farrightflag && RightOfHyperbola(ref searchtri, nextvertex)) //{ // searchtri.OnextSelf(); // farrightflag = searchtri.Equal(bottommost); //} Check4DeadEvent(ref searchtri, eventheap, ref heapsize); searchtri.Copy(ref farrighttri); searchtri.Sym(ref farlefttri); mesh.MakeTriangle(ref lefttri); mesh.MakeTriangle(ref righttri); connectvertex = farrighttri.Dest(); lefttri.SetOrg(connectvertex); lefttri.SetDest(nextvertex); righttri.SetOrg(nextvertex); righttri.SetDest(connectvertex); lefttri.Bond(ref righttri); lefttri.Lnext(); righttri.Lprev(); lefttri.Bond(ref righttri); lefttri.Lnext(); righttri.Lprev(); lefttri.Bond(ref farlefttri); righttri.Bond(ref farrighttri); if (!farrightflag && farrighttri.Equals(bottommost)) { lefttri.Copy(ref bottommost); } if (randomnation(SAMPLERATE) == 0) { splayroot = SplayInsert(splayroot, lefttri, nextvertex); } else if (randomnation(SAMPLERATE) == 0) { righttri.Lnext(ref inserttri); splayroot = SplayInsert(splayroot, inserttri, nextvertex); } } } if (check4events) { leftvertex = farlefttri.Apex(); midvertex = lefttri.Dest(); rightvertex = lefttri.Apex(); lefttest = predicates.CounterClockwise(leftvertex, midvertex, rightvertex); if (lefttest > 0.0) { newevent = new SweepEvent(); newevent.xkey = xminextreme; newevent.ykey = CircleTop(leftvertex, midvertex, rightvertex, lefttest); newevent.otriEvent = lefttri; HeapInsert(eventheap, heapsize, newevent); heapsize++; lefttri.SetOrg(new SweepEventVertex(newevent)); } leftvertex = righttri.Apex(); midvertex = righttri.Org(); rightvertex = farrighttri.Apex(); righttest = predicates.CounterClockwise(leftvertex, midvertex, rightvertex); if (righttest > 0.0) { newevent = new SweepEvent(); newevent.xkey = xminextreme; newevent.ykey = CircleTop(leftvertex, midvertex, rightvertex, righttest); newevent.otriEvent = farrighttri; HeapInsert(eventheap, heapsize, newevent); heapsize++; farrighttri.SetOrg(new SweepEventVertex(newevent)); } } } splaynodes.Clear(); bottommost.Lprev(); this.mesh.hullsize = RemoveGhosts(ref bottommost); return(this.mesh); }
/// <summary> /// Remove the "infinite" bounding triangle, setting boundary markers as appropriate. /// </summary> /// <returns>Returns the number of edges on the convex hull of the triangulation.</returns> /// <remarks> /// The triangular bounding box has three boundary triangles (one for each /// side of the bounding box), and a bunch of triangles fanning out from /// the three bounding box vertices (one triangle for each edge of the /// convex hull of the inner mesh). This routine removes these triangles. /// </remarks> int RemoveBox() { Otri deadtriangle = default(Otri); Otri searchedge = default(Otri); Otri checkedge = default(Otri); Otri nextedge = default(Otri), finaledge = default(Otri), dissolveedge = default(Otri); Vertex markorg; int hullsize; bool noPoly = !_TriangleNetMesh.behavior.Poly; // Find a boundary triangle. nextedge.tri = _TriangleNetMesh.dummytri; nextedge.orient = 0; nextedge.Sym(); // Mark a place to stop. nextedge.Lprev(ref finaledge); nextedge.Lnext(); nextedge.Sym(); // Find a triangle (on the boundary of the vertex set) that isn't // a bounding box triangle. nextedge.Lprev(ref searchedge); searchedge.Sym(); // Check whether nextedge is another boundary triangle // adjacent to the first one. nextedge.Lnext(ref checkedge); checkedge.Sym(); if (checkedge.tri.id == TriangleNetMesh.DUMMY) { // Go on to the next triangle. There are only three boundary // triangles, and this next triangle cannot be the third one, // so it's safe to stop here. searchedge.Lprev(); searchedge.Sym(); } // Find a new boundary edge to search from, as the current search // edge lies on a bounding box triangle and will be deleted. _TriangleNetMesh.dummytri.neighbors[0] = searchedge; hullsize = -2; while (!nextedge.Equals(finaledge)) { hullsize++; nextedge.Lprev(ref dissolveedge); dissolveedge.Sym(); // If not using a PSLG, the vertices should be marked now. // (If using a PSLG, markhull() will do the job.) if (noPoly) { // Be careful! One must check for the case where all the input // vertices are collinear, and thus all the triangles are part of // the bounding box. Otherwise, the setvertexmark() call below // will cause a bad pointer reference. if (dissolveedge.tri.id != TriangleNetMesh.DUMMY) { markorg = dissolveedge.Org(); if (markorg.label == 0) { markorg.label = 1; } } } // Disconnect the bounding box triangle from the mesh triangle. dissolveedge.Dissolve(_TriangleNetMesh.dummytri); nextedge.Lnext(ref deadtriangle); deadtriangle.Sym(ref nextedge); // Get rid of the bounding box triangle. _TriangleNetMesh.TriangleDealloc(deadtriangle.tri); // Do we need to turn the corner? if (nextedge.tri.id == TriangleNetMesh.DUMMY) { // Turn the corner. dissolveedge.Copy(ref nextedge); } } _TriangleNetMesh.TriangleDealloc(finaledge.tri); return(hullsize); }