public void TestOnext() { var triangles = CreateExampleMesh(); Otri t = default; // Start with the bottom right triangle. t.tri = triangles[2]; // Start with edge 1 -> 2. t.orient = 0; // Make sure we're on the correct edge. Assert.AreEqual(1, t.Org().ID); Assert.AreEqual(2, t.Dest().ID); t.Onext(); Assert.AreEqual(1, t.Org().ID); Assert.AreEqual(4, t.Dest().ID); Assert.AreEqual(1, t.tri.ID); t.Onext(); Assert.AreEqual(1, t.Org().ID); Assert.AreEqual(3, t.Dest().ID); Assert.AreEqual(0, t.tri.ID); // Out of mesh. t.Onext(); Assert.AreEqual(-1, t.tri.ID); }
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; } } } }
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); }
private void InvokePrimitive(string name) { if (name == "sym") { current.Sym(); } else if (name == "lnext") { current.Lnext(); } else if (name == "lprev") { current.Lprev(); } else if (name == "onext") { current.Onext(); } else if (name == "oprev") { current.Oprev(); } else if (name == "dnext") { current.Dnext(); } else if (name == "dprev") { current.Dprev(); } else if (name == "rnext") { current.Rnext(); } else if (name == "rprev") { current.Rprev(); } renderControl.Update(current); topoControlView.SetTriangle(current.Triangle); }
/// <summary> /// Construct Voronoi region for given vertex. /// </summary> /// <param name="vertex"></param> /// <returns>The circumcenter indices which make up the cell.</returns> private void ConstructVoronoiRegion(Vertex vertex) { VoronoiRegion region = new VoronoiRegion(vertex); regions.Add(region); List <Point> 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.triangle == Mesh.dummytri) { f_init.Oprev(ref f_prev); if (f_prev.triangle != Mesh.dummytri) { f_init.Copy(ref f_next); // Move one triangle clockwise f_init.OprevSelf(); f_init.Copy(ref f); } } // Go counterclockwise until we reach the border or the initial triangle. while (f_next.triangle != Mesh.dummytri) { // Add circumcenter of current triangle vpoints.Add(points[f.triangle.id]); if (f_next.Equal(f_init)) { // Voronoi cell is complete (bounded case). region.Add(vpoints); return; } f_next.Copy(ref f); f_next.OnextSelf(); } // Voronoi cell is unbounded region.Bounded = false; Vertex torg, tdest, tapex, intersection; int sid, n = mesh.triangles.Count; // Find the boundary segment id. f.Lprev(ref f_next); f_next.SegPivot(ref sub); sid = sub.seg.hash; // Last valid f lies at the boundary. Add the circumcenter. vpoints.Add(points[f.triangle.id]); // Check if the intersection with the bounding box has already been computed. if (rayPoints.ContainsKey(sid)) { vpoints.Add(rayPoints[sid]); } else { torg = f.Org(); tapex = f.Apex(); BoxRayIntersection(points[f.triangle.id], torg.y - tapex.y, tapex.x - torg.x, out intersection); // Set the correct id for the vertex intersection.id = n + rayIndex; points[n + rayIndex] = intersection; rayIndex++; vpoints.Add(intersection); rayPoints.Add(sid, 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.triangle != Mesh.dummytri) { vpoints.Add(points[f_prev.triangle.id]); f_prev.Copy(ref f); f_prev.OprevSelf(); } // Find the boundary segment id. f.SegPivot(ref sub); sid = sub.seg.hash; if (rayPoints.ContainsKey(sid)) { vpoints.Add(rayPoints[sid]); } else { // Intersection has not been computed yet. torg = f.Org(); tdest = f.Dest(); BoxRayIntersection(points[f.triangle.id], tdest.y - torg.y, torg.x - tdest.x, out intersection); // Set the correct id for the vertex intersection.id = n + rayIndex; points[n + rayIndex] = intersection; rayIndex++; vpoints.Add(intersection); rayPoints.Add(sid, intersection); } // Add the new points to the region (in counter-clockwise order) vpoints.Reverse(); region.Add(vpoints); }
/// <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 int Triangulate(Mesh mesh) { SweepLine.SweepEvent[] sweepEventArray; SweepLine.SweepEvent sweepEvent; Vertex vertex; Vertex vertex1; Vertex vertex2; Vertex vertex3; this.mesh = mesh; this.xminextreme = 10 * mesh.bounds.Xmin - 9 * mesh.bounds.Xmax; Otri otri = new Otri(); Otri otri1 = new Otri(); Otri otri2 = new Otri(); Otri otri3 = new Otri(); Otri otri4 = new Otri(); Otri otri5 = new Otri(); Otri otri6 = new Otri(); bool i = false; this.splaynodes = new List <SweepLine.SplayNode>(); SweepLine.SplayNode splayNode = null; this.CreateHeap(out sweepEventArray); int num = mesh.invertices; mesh.MakeTriangle(ref otri2); mesh.MakeTriangle(ref otri3); otri2.Bond(ref otri3); otri2.LnextSelf(); otri3.LprevSelf(); otri2.Bond(ref otri3); otri2.LnextSelf(); otri3.LprevSelf(); otri2.Bond(ref otri3); Vertex vertex4 = sweepEventArray[0].vertexEvent; this.HeapDelete(sweepEventArray, num, 0); num--; do { if (num == 0) { SimpleLog.Instance.Error("Input vertices are all identical.", "SweepLine.SweepLineDelaunay()"); throw new Exception("Input vertices are all identical."); } vertex = sweepEventArray[0].vertexEvent; this.HeapDelete(sweepEventArray, num, 0); num--; if (vertex4.x != vertex.x || vertex4.y != vertex.y) { continue; } if (Behavior.Verbose) { SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.", "SweepLine.SweepLineDelaunay().1"); } vertex.type = VertexType.UndeadVertex; Mesh mesh1 = mesh; mesh1.undeads = mesh1.undeads + 1; }while (vertex4.x == vertex.x && vertex4.y == vertex.y); otri2.SetOrg(vertex4); otri2.SetDest(vertex); otri3.SetOrg(vertex); otri3.SetDest(vertex4); otri2.Lprev(ref otri); Vertex vertex5 = vertex; while (num > 0) { SweepLine.SweepEvent sweepEvent1 = sweepEventArray[0]; this.HeapDelete(sweepEventArray, num, 0); num--; bool flag = true; if (sweepEvent1.xkey >= mesh.bounds.Xmin) { Vertex vertex6 = sweepEvent1.vertexEvent; if (vertex6.x != vertex5.x || vertex6.y != vertex5.y) { vertex5 = vertex6; splayNode = this.FrontLocate(splayNode, otri, vertex6, ref otri1, ref i); otri.Copy(ref otri1); for (i = false; !i && this.RightOfHyperbola(ref otri1, vertex6); i = otri1.Equal(otri)) { otri1.OnextSelf(); } this.Check4DeadEvent(ref otri1, sweepEventArray, ref num); otri1.Copy(ref otri5); otri1.Sym(ref otri4); mesh.MakeTriangle(ref otri2); mesh.MakeTriangle(ref otri3); Vertex vertex7 = otri5.Dest(); otri2.SetOrg(vertex7); otri2.SetDest(vertex6); otri3.SetOrg(vertex6); otri3.SetDest(vertex7); otri2.Bond(ref otri3); otri2.LnextSelf(); otri3.LprevSelf(); otri2.Bond(ref otri3); otri2.LnextSelf(); otri3.LprevSelf(); otri2.Bond(ref otri4); otri3.Bond(ref otri5); if (!i && otri5.Equal(otri)) { otri2.Copy(ref otri); } if (this.randomnation(SweepLine.SAMPLERATE) == 0) { splayNode = this.SplayInsert(splayNode, otri2, vertex6); } else if (this.randomnation(SweepLine.SAMPLERATE) == 0) { otri3.Lnext(ref otri6); splayNode = this.SplayInsert(splayNode, otri6, vertex6); } } else { if (Behavior.Verbose) { SimpleLog.Instance.Warning("A duplicate vertex appeared and was ignored.", "SweepLine.SweepLineDelaunay().2"); } vertex6.type = VertexType.UndeadVertex; Mesh mesh2 = mesh; mesh2.undeads = mesh2.undeads + 1; flag = false; } } else { Otri otri7 = sweepEvent1.otriEvent; otri7.Oprev(ref otri4); this.Check4DeadEvent(ref otri4, sweepEventArray, ref num); otri7.Onext(ref otri5); this.Check4DeadEvent(ref otri5, sweepEventArray, ref num); if (otri4.Equal(otri)) { otri7.Lprev(ref otri); } mesh.Flip(ref otri7); otri7.SetApex(null); otri7.Lprev(ref otri2); otri7.Lnext(ref otri3); otri2.Sym(ref otri4); if (this.randomnation(SweepLine.SAMPLERATE) == 0) { otri7.SymSelf(); vertex1 = otri7.Dest(); vertex2 = otri7.Apex(); vertex3 = otri7.Org(); splayNode = this.CircleTopInsert(splayNode, otri2, vertex1, vertex2, vertex3, sweepEvent1.ykey); } } if (!flag) { continue; } vertex1 = otri4.Apex(); vertex2 = otri2.Dest(); vertex3 = otri2.Apex(); double num1 = Primitives.CounterClockwise(vertex1, vertex2, vertex3); if (num1 > 0) { sweepEvent = new SweepLine.SweepEvent() { xkey = this.xminextreme, ykey = this.CircleTop(vertex1, vertex2, vertex3, num1), otriEvent = otri2 }; this.HeapInsert(sweepEventArray, num, sweepEvent); num++; otri2.SetOrg(new SweepLine.SweepEventVertex(sweepEvent)); } vertex1 = otri3.Apex(); vertex2 = otri3.Org(); vertex3 = otri5.Apex(); double num2 = Primitives.CounterClockwise(vertex1, vertex2, vertex3); if (num2 <= 0) { continue; } sweepEvent = new SweepLine.SweepEvent() { xkey = this.xminextreme, ykey = this.CircleTop(vertex1, vertex2, vertex3, num2), otriEvent = otri5 }; this.HeapInsert(sweepEventArray, num, sweepEvent); num++; otri5.SetOrg(new SweepLine.SweepEventVertex(sweepEvent)); } this.splaynodes.Clear(); otri.LprevSelf(); return(this.RemoveGhosts(ref otri)); }
/// <summary> /// Spread the virus from all infected triangles to any neighbors not /// protected by subsegments. Delete all infected triangles. /// </summary> /// <remarks> /// This is the procedure that actually creates holes and concavities. /// /// This procedure operates in two phases. The first phase identifies all /// the triangles that will die, and marks them as infected. They are /// marked to ensure that each triangle is added to the virus pool only /// once, so the procedure will terminate. /// /// The second phase actually eliminates the infected triangles. It also /// eliminates orphaned vertices. /// </remarks> void Plague() { Otri testtri = default(Otri); Otri neighbor = default(Otri); Osub neighborsubseg = default(Osub); Vertex testvertex; Vertex norg, ndest; bool killorg; // Loop through all the infected triangles, spreading the virus to // their neighbors, then to their neighbors' neighbors. for (int i = 0; i < viri.Count; i++) { // WARNING: Don't use foreach, mesh.viri list may get modified. testtri.triangle = viri[i]; // A triangle is marked as infected by messing with one of its pointers // to subsegments, setting it to an illegal value. Hence, we have to // temporarily uninfect this triangle so that we can examine its // adjacent subsegments. // TODO: Not true in the C# version (so we could skip this). testtri.Uninfect(); // Check each of the triangle's three neighbors. for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { // Find the neighbor. testtri.Sym(ref neighbor); // Check for a subsegment between the triangle and its neighbor. testtri.SegPivot(ref neighborsubseg); // Check if the neighbor is nonexistent or already infected. if ((neighbor.triangle == Mesh.dummytri) || neighbor.IsInfected()) { if (neighborsubseg.seg != Mesh.dummysub) { // There is a subsegment separating the triangle from its // neighbor, but both triangles are dying, so the subsegment // dies too. mesh.SubsegDealloc(neighborsubseg.seg); if (neighbor.triangle != Mesh.dummytri) { // Make sure the subsegment doesn't get deallocated again // later when the infected neighbor is visited. neighbor.Uninfect(); neighbor.SegDissolve(); neighbor.Infect(); } } } else { // The neighbor exists and is not infected. if (neighborsubseg.seg == Mesh.dummysub) { // There is no subsegment protecting the neighbor, so // the neighbor becomes infected. neighbor.Infect(); // Ensure that the neighbor's neighbors will be infected. viri.Add(neighbor.triangle); } else { // The neighbor is protected by a subsegment. // Remove this triangle from the subsegment. neighborsubseg.TriDissolve(); // The subsegment becomes a boundary. Set markers accordingly. if (neighborsubseg.seg.boundary == 0) { neighborsubseg.seg.boundary = 1; } norg = neighbor.Org(); ndest = neighbor.Dest(); if (norg.mark == 0) { norg.mark = 1; } if (ndest.mark == 0) { ndest.mark = 1; } } } } // Remark the triangle as infected, so it doesn't get added to the // virus pool again. testtri.Infect(); } foreach (var virus in viri) { testtri.triangle = virus; // Check each of the three corners of the triangle for elimination. // This is done by walking around each vertex, checking if it is // still connected to at least one live triangle. for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { testvertex = testtri.Org(); // Check if the vertex has already been tested. if (testvertex != null) { killorg = true; // Mark the corner of the triangle as having been tested. testtri.SetOrg(null); // Walk counterclockwise about the vertex. testtri.Onext(ref neighbor); // Stop upon reaching a boundary or the starting triangle. while ((neighbor.triangle != Mesh.dummytri) && (!neighbor.Equal(testtri))) { if (neighbor.IsInfected()) { // Mark the corner of this triangle as having been tested. neighbor.SetOrg(null); } else { // A live triangle. The vertex survives. killorg = false; } // Walk counterclockwise about the vertex. neighbor.OnextSelf(); } // If we reached a boundary, we must walk clockwise as well. if (neighbor.triangle == Mesh.dummytri) { // Walk clockwise about the vertex. testtri.Oprev(ref neighbor); // Stop upon reaching a boundary. while (neighbor.triangle != Mesh.dummytri) { if (neighbor.IsInfected()) { // Mark the corner of this triangle as having been tested. neighbor.SetOrg(null); } else { // A live triangle. The vertex survives. killorg = false; } // Walk clockwise about the vertex. neighbor.OprevSelf(); } } if (killorg) { // Deleting vertex testvertex.type = VertexType.UndeadVertex; mesh.undeads++; } } } // Record changes in the number of boundary edges, and disconnect // dead triangles from their neighbors. for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { testtri.Sym(ref neighbor); if (neighbor.triangle == Mesh.dummytri) { // There is no neighboring triangle on this edge, so this edge // is a boundary edge. This triangle is being deleted, so this // boundary edge is deleted. mesh.hullsize--; } else { // Disconnect the triangle from its neighbor. neighbor.Dissolve(); // There is a neighboring triangle on this edge, so this edge // becomes a boundary edge when this triangle is deleted. mesh.hullsize++; } } // Return the dead triangle to the pool of triangles. mesh.TriangleDealloc(testtri.triangle); } // Empty the virus pool. viri.Clear(); }
private void ConstructVoronoiRegion(Vertex vertex) { Vertex vertex1; Vertex vertex2; VoronoiRegion voronoiRegion = new VoronoiRegion(vertex); this.regions.Add(voronoiRegion); List <Point> points = new List <Point>(); Otri otri = new Otri(); Otri otri1 = new Otri(); Otri otri2 = new Otri(); Otri otri3 = new Otri(); Osub osub = new Osub(); vertex.tri.Copy(ref otri1); otri1.Copy(ref otri); otri1.Onext(ref otri2); if (otri2.triangle == Mesh.dummytri) { otri1.Oprev(ref otri3); if (otri3.triangle != Mesh.dummytri) { otri1.Copy(ref otri2); otri1.OprevSelf(); otri1.Copy(ref otri); } } while (otri2.triangle != Mesh.dummytri) { points.Add(this.points[otri.triangle.id]); if (otri2.Equal(otri1)) { voronoiRegion.Add(points); return; } otri2.Copy(ref otri); otri2.OnextSelf(); } voronoiRegion.Bounded = false; int count = this.mesh.triangles.Count; otri.Lprev(ref otri2); otri2.SegPivot(ref osub); int num = osub.seg.hash; points.Add(this.points[otri.triangle.id]); if (!this.rayPoints.ContainsKey(num)) { vertex1 = otri.Org(); Vertex vertex3 = otri.Apex(); this.BoxRayIntersection(this.points[otri.triangle.id], vertex1.y - vertex3.y, vertex3.x - vertex1.x, out vertex2); vertex2.id = count + this.rayIndex; this.points[count + this.rayIndex] = vertex2; this.rayIndex = this.rayIndex + 1; points.Add(vertex2); this.rayPoints.Add(num, vertex2); } else { points.Add(this.rayPoints[num]); } points.Reverse(); otri1.Copy(ref otri); otri.Oprev(ref otri3); while (otri3.triangle != Mesh.dummytri) { points.Add(this.points[otri3.triangle.id]); otri3.Copy(ref otri); otri3.OprevSelf(); } otri.SegPivot(ref osub); num = osub.seg.hash; if (!this.rayPoints.ContainsKey(num)) { vertex1 = otri.Org(); Vertex vertex4 = otri.Dest(); this.BoxRayIntersection(this.points[otri.triangle.id], vertex4.y - vertex1.y, vertex1.x - vertex4.x, out vertex2); vertex2.id = count + this.rayIndex; this.points[count + this.rayIndex] = vertex2; this.rayIndex = this.rayIndex + 1; points.Add(vertex2); this.rayPoints.Add(num, vertex2); } else { points.Add(this.rayPoints[num]); } points.Reverse(); voronoiRegion.Add(points); }
/// <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; var dummysub = mesh.dummysub; 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) { 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); newvertex.Id = mesh.hash_vtx++; mesh.vertices.Add(newvertex.Id, newvertex); // Insert the intersection vertex. This should always succeed. success = mesh.InsertVertex(newvertex, ref splittri, ref splitsubseg, false, false); if (success != InsertVertexResult.Successful) { throw new Exception("Failure to split a segment."); } // Record a triangle whose origin is the new vertex. newvertex.tri = splittri; if (mesh.steinerleft > 0) { mesh.steinerleft--; } // Divide the segment into two, and correct the segment endpoints. splitsubseg.Sym(); splitsubseg.Pivot(ref opposubseg); splitsubseg.Dissolve(dummysub); opposubseg.Dissolve(dummysub); do { splitsubseg.SetSegOrg(newvertex); splitsubseg.Next(); } while (splitsubseg.seg.hash != Mesh.DUMMY); do { opposubseg.SetSegOrg(newvertex); opposubseg.Next(); } while (opposubseg.seg.hash != Mesh.DUMMY); // 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.Onext(); } else if ((rightvertex.X != endpoint1.X) || (rightvertex.Y != endpoint1.Y)) { throw new Exception("Topological inconsistency after splitting a segment."); } // 'splittri' should have destination endpoint1. }
/// <summary> /// Find the first triangle on the path from one point to another. /// </summary> /// <param name="searchtri"></param> /// <param name="searchpoint"></param> /// <returns> /// The return value notes whether the destination or apex of the found /// triangle is collinear with the two points in question. /// </returns> /// <remarks> /// Finds the triangle that intersects a line segment drawn from the /// origin of 'searchtri' to the point 'searchpoint', and returns the result /// in 'searchtri'. The origin of 'searchtri' does not change, even though /// the triangle returned may differ from the one passed in. This routine /// is used to find the direction to move in to get from one point to /// another. /// </remarks> private FindDirectionResult FindDirection(ref Otri searchtri, Vertex searchpoint) { Otri checktri = default(Otri); Vertex startvertex; Vertex leftvertex, rightvertex; double leftccw, rightccw; bool leftflag, rightflag; startvertex = searchtri.Org(); rightvertex = searchtri.Dest(); leftvertex = searchtri.Apex(); // Is 'searchpoint' to the left? leftccw = RobustPredicates.CounterClockwise(searchpoint, startvertex, leftvertex); leftflag = leftccw > 0.0; // Is 'searchpoint' to the right? rightccw = RobustPredicates.CounterClockwise(startvertex, searchpoint, rightvertex); rightflag = rightccw > 0.0; if (leftflag && rightflag) { // 'searchtri' faces directly away from 'searchpoint'. We could go left // or right. Ask whether it's a triangle or a boundary on the left. searchtri.Onext(ref checktri); if (checktri.tri.Id == Mesh.DUMMY) { leftflag = false; } else { rightflag = false; } } while (leftflag) { // Turn left until satisfied. searchtri.Onext(); if (searchtri.tri.Id == Mesh.DUMMY) { throw new Exception("Unable to find a triangle on path."); } leftvertex = searchtri.Apex(); rightccw = leftccw; leftccw = RobustPredicates.CounterClockwise(searchpoint, startvertex, leftvertex); leftflag = leftccw > 0.0; } while (rightflag) { // Turn right until satisfied. searchtri.Oprev(); if (searchtri.tri.Id == Mesh.DUMMY) { throw new Exception("Unable to find a triangle on path."); } rightvertex = searchtri.Dest(); leftccw = rightccw; rightccw = RobustPredicates.CounterClockwise(startvertex, searchpoint, rightvertex); rightflag = rightccw > 0.0; } if (leftccw == 0.0) { return(FindDirectionResult.Leftcollinear); } if (rightccw == 0.0) { return(FindDirectionResult.Rightcollinear); } return(FindDirectionResult.Within); }
private void ConstructBoundaryBvdCell(Vertex vertex) { Vertex vertex1; Point point; VoronoiRegion voronoiRegion = new VoronoiRegion(vertex); this.regions.Add(voronoiRegion); Otri otri = new Otri(); Otri otri1 = new Otri(); Otri otri2 = new Otri(); Otri otri3 = new Otri(); Osub item = new Osub(); Osub osub = new Osub(); int count = this.mesh.triangles.Count; List <Point> points = new List <Point>(); vertex.tri.Copy(ref otri1); if (otri1.Org() != vertex) { throw new Exception("ConstructBoundaryBvdCell: inconsistent topology."); } otri1.Copy(ref otri); otri1.Onext(ref otri2); otri1.Oprev(ref otri3); if (otri3.triangle != Mesh.dummytri) { while (otri3.triangle != Mesh.dummytri && !otri3.Equal(otri1)) { otri3.Copy(ref otri); otri3.OprevSelf(); } otri.Copy(ref otri1); otri.Onext(ref otri2); } if (otri3.triangle == Mesh.dummytri) { point = new Point(vertex.x, vertex.y) { id = count + this.segIndex }; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); } Vertex vertex2 = otri.Org(); Vertex vertex3 = otri.Dest(); point = new Point((vertex2.X + vertex3.X) / 2, (vertex2.Y + vertex3.Y) / 2) { id = count + this.segIndex }; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); do { Point point1 = this.points[otri.triangle.id]; if (otri2.triangle != Mesh.dummytri) { Point point2 = this.points[otri2.triangle.id]; if (otri.triangle.infected) { item.seg = this.subsegMap[otri.triangle.hash]; Vertex vertex4 = item.SegOrg(); Vertex vertex5 = item.SegDest(); if (otri2.triangle.infected) { osub.seg = this.subsegMap[otri2.triangle.hash]; if (!item.Equal(osub)) { if (this.SegmentsIntersect(vertex4, vertex5, point1, point2, out point, true)) { point.id = count + this.segIndex; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); } if (this.SegmentsIntersect(osub.SegOrg(), osub.SegDest(), point1, point2, out point, true)) { point.id = count + this.segIndex; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); } } else if (this.SegmentsIntersect(vertex4, vertex5, new Point((vertex2.X + vertex3.X) / 2, (vertex2.Y + vertex3.Y) / 2), point2, out point, false)) { point.id = count + this.segIndex; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); } } else { vertex3 = otri.Dest(); vertex1 = otri.Apex(); if (this.SegmentsIntersect(vertex4, vertex5, new Point((vertex3.X + vertex1.X) / 2, (vertex3.Y + vertex1.Y) / 2), point1, out point, false)) { point.id = count + this.segIndex; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); } if (this.SegmentsIntersect(vertex4, vertex5, point1, point2, out point, true)) { point.id = count + this.segIndex; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); } } } else { points.Add(point1); if (otri2.triangle.infected) { osub.seg = this.subsegMap[otri2.triangle.hash]; if (this.SegmentsIntersect(osub.SegOrg(), osub.SegDest(), point1, point2, out point, true)) { point.id = count + this.segIndex; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); } } } otri2.Copy(ref otri); otri2.OnextSelf(); } else { if (!otri.triangle.infected) { points.Add(point1); } vertex2 = otri.Org(); vertex1 = otri.Apex(); point = new Point((vertex2.X + vertex1.X) / 2, (vertex2.Y + vertex1.Y) / 2) { id = count + this.segIndex }; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); break; } }while (!otri.Equal(otri1)); voronoiRegion.Add(points); }
private void ConstructBvdCell(Vertex vertex) { Point point; VoronoiRegion voronoiRegion = new VoronoiRegion(vertex); this.regions.Add(voronoiRegion); Otri otri = new Otri(); Otri otri1 = new Otri(); Otri otri2 = new Otri(); Osub item = new Osub(); Osub osub = new Osub(); int count = this.mesh.triangles.Count; List <Point> points = new List <Point>(); vertex.tri.Copy(ref otri1); if (otri1.Org() != vertex) { throw new Exception("ConstructBvdCell: inconsistent topology."); } otri1.Copy(ref otri); otri1.Onext(ref otri2); do { Point point1 = this.points[otri.triangle.id]; Point point2 = this.points[otri2.triangle.id]; if (otri.triangle.infected) { item.seg = this.subsegMap[otri.triangle.hash]; if (otri2.triangle.infected) { osub.seg = this.subsegMap[otri2.triangle.hash]; if (!item.Equal(osub)) { if (this.SegmentsIntersect(item.SegOrg(), item.SegDest(), point1, point2, out point, true)) { point.id = count + this.segIndex; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); } if (this.SegmentsIntersect(osub.SegOrg(), osub.SegDest(), point1, point2, out point, true)) { point.id = count + this.segIndex; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); } } } else if (this.SegmentsIntersect(item.SegOrg(), item.SegDest(), point1, point2, out point, true)) { point.id = count + this.segIndex; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); } } else { points.Add(point1); if (otri2.triangle.infected) { osub.seg = this.subsegMap[otri2.triangle.hash]; if (this.SegmentsIntersect(osub.SegOrg(), osub.SegDest(), point1, point2, out point, true)) { point.id = count + this.segIndex; this.points[count + this.segIndex] = point; this.segIndex = this.segIndex + 1; points.Add(point); } } } otri2.Copy(ref otri); otri2.OnextSelf(); }while (!otri.Equal(otri1)); voronoiRegion.Add(points); }
private void Plague() { Otri item = new Otri(); Otri otri = new Otri(); Osub osub = new Osub(); for (int i = 0; i < this.viri.Count; i++) { item.triangle = this.viri[i]; item.Uninfect(); item.orient = 0; while (item.orient < 3) { item.Sym(ref otri); item.SegPivot(ref osub); if (otri.triangle != Mesh.dummytri && !otri.IsInfected()) { if (osub.seg != Mesh.dummysub) { osub.TriDissolve(); if (osub.seg.boundary == 0) { osub.seg.boundary = 1; } Vertex vertex = otri.Org(); Vertex vertex1 = otri.Dest(); if (vertex.mark == 0) { vertex.mark = 1; } if (vertex1.mark == 0) { vertex1.mark = 1; } } else { otri.Infect(); this.viri.Add(otri.triangle); } } else if (osub.seg != Mesh.dummysub) { this.mesh.SubsegDealloc(osub.seg); if (otri.triangle != Mesh.dummytri) { otri.Uninfect(); otri.SegDissolve(); otri.Infect(); } } item.orient = item.orient + 1; } item.Infect(); } foreach (Triangle virus in this.viri) { item.triangle = virus; item.orient = 0; while (item.orient < 3) { Vertex vertex2 = item.Org(); if (vertex2 != null) { bool flag = true; item.SetOrg(null); item.Onext(ref otri); while (otri.triangle != Mesh.dummytri && !otri.Equal(item)) { if (!otri.IsInfected()) { flag = false; } else { otri.SetOrg(null); } otri.OnextSelf(); } if (otri.triangle == Mesh.dummytri) { item.Oprev(ref otri); while (otri.triangle != Mesh.dummytri) { if (!otri.IsInfected()) { flag = false; } else { otri.SetOrg(null); } otri.OprevSelf(); } } if (flag) { vertex2.type = VertexType.UndeadVertex; Mesh mesh = this.mesh; mesh.undeads = mesh.undeads + 1; } } item.orient = item.orient + 1; } item.orient = 0; while (item.orient < 3) { item.Sym(ref otri); if (otri.triangle != Mesh.dummytri) { otri.Dissolve(); Mesh mesh1 = this.mesh; mesh1.hullsize = mesh1.hullsize + 1; } else { Mesh mesh2 = this.mesh; mesh2.hullsize = mesh2.hullsize - 1; } item.orient = item.orient + 1; } this.mesh.TriangleDealloc(item.triangle); } this.viri.Clear(); }