public void Insert(HalfEdge lb, HalfEdge newHe) { newHe.EdgeListLeft = lb; newHe.EdgeListRight = lb.EdgeListRight; lb.EdgeListRight.EdgeListLeft = newHe; lb.EdgeListRight = newHe; }
protected void AddVertex(Face face, Vertex vertex) { base.AddVertex(vertex); Faces.Remove(face); HalfEdge h1 = face.HalfEdge; HalfEdge h2 = h1.Next; HalfEdge h3 = h2.Next; HalfEdge h4 = new HalfEdge(h1.Origin); HalfEdge h5 = new HalfEdge(h2.Origin); HalfEdge h6 = new HalfEdge(h3.Origin); HalfEdge h7 = new HalfEdge(vertex); HalfEdge h8 = new HalfEdge(vertex); HalfEdge h9 = new HalfEdge(vertex); HalfEdges.AddRange(new List<HalfEdge> {h4, h5, h6, h7, h8, h9}); h4.Twin = h7; h7.Twin = h4; h5.Twin = h8; h8.Twin = h5; h6.Twin = h9; h9.Twin = h6; // Set all next h1.Next = h5; h5.Prev = h1; h5.Next = h7; h7.Prev = h5; h7.Next = h1; h1.Prev = h7; h2.Next = h6; h6.Prev = h2; h6.Next = h8; h8.Prev = h6; h8.Next = h2; h2.Prev = h8; h3.Next = h4; h4.Prev = h3; h4.Next = h9; h9.Prev = h4; h9.Next = h3; h3.Prev = h9; Triangle t1 = new Triangle(h1); Triangle t2 = new Triangle(h2); Triangle t3 = new Triangle(h3); Faces.Add(t1); Faces.Add(t2); Faces.Add(t3); Tree.Add(vertex, t1, t2, t3); LogEntry logEntry = new LogEntry("Adding edges.", this); logEntry.Objects.Add(vertex); Log.Add(logEntry); }
internal static void ELinsert(HalfEdge lb, HalfEdge New) { New.Left = lb; New.Right = lb.Right; lb.Right.Left = New; lb.Right = New; }
public EdgeList( SiteList siteList) { _siteList = siteList; LeftEnd = new HalfEdge(null, Side.Left); RightEnd = new HalfEdge(null, Side.Left); LeftEnd.Right = RightEnd; RightEnd.Left = LeftEnd; }
public HalfEdge(Vertex v) { _origin = new Vertex(v.X, v.Y); Twin = null; Next = null; Prev = null; }
/// <summary> /// Half edge create /// </summary> internal static HalfEdge HEcreate(Edge e, int pm) { HalfEdge answer = new HalfEdge();//** Memory.getfree(hfl); answer.Edge = e; answer.ELpm = pm; answer.PQnext = null; answer.Vertex = null; answer.RefCount = 0; return answer; }
public void Delete(HalfEdge he) { if (he.Vertex != null) { HalfEdge last = _hash[Bucket(he)]; while (last.PriorityQueueNext != he) { last = last.PriorityQueueNext; } last.PriorityQueueNext = he.PriorityQueueNext; _count--; he.Vertex = null; } }
public void Insert(HalfEdge he, Site v, float offset) { he.Vertex = v; he.YStar = v.Coord.Y + offset; var last = _hash[Bucket(he)]; HalfEdge next; while ((next = last.PriorityQueueNext) != null && (he.YStar > next.YStar || he.YStar == next.YStar && v.Coord.X > next.Vertex.Coord.X)) { last = next; } he.PriorityQueueNext = last.PriorityQueueNext; last.PriorityQueueNext = he; _count++; }
public EdgeList(int sqrtNSites) { HashSize = 2*sqrtNSites; Hash = new HalfEdge[HashSize]; LeftEnd = HalfEdge.Create(null, LR.Left); RightEnd = HalfEdge.Create(null, LR.Left); LeftEnd.EdgeListLeft = null; LeftEnd.EdgeListRight = RightEnd; RightEnd.EdgeListLeft = LeftEnd; RightEnd.EdgeListRight = null; Hash[0] = LeftEnd; Hash[HashSize - 1] = RightEnd; }
public static void PQdelete(HalfEdge he) { HalfEdge last; if (he.Vertex != null) { last = PQhash[PQbucket(he)]; while (last.PQnext != he) { last = last.PQnext; } last.PQnext = he.PQnext; PQcount--; VoronoiGeometry.deref(he.Vertex); he.Vertex = null; } }
/// <summary> /// Inserts an edge not already present into the graph. /// </summary> /// <param name="orig">the edge origin location</param> /// <param name="dest">the edge destination location</param> /// <param name="eAdj">an existing edge with same orig (if any)</param> /// <returns>the created edge</returns> private HalfEdge Insert(Coordinate orig, Coordinate dest, HalfEdge eAdj) { // edge does not exist, so create it and insert in graph HalfEdge e = Create(orig, dest); if (eAdj != null) eAdj.Insert(e); else vertexMap.Add(orig, e); HalfEdge eAdjDest; bool eAdjDestFound = vertexMap.TryGetValue(dest, out eAdjDest); HalfEdge sym = e.Sym; if (eAdjDestFound) eAdjDest.Insert(sym); else vertexMap.Add(dest, sym); return e; }
public int Bucket(HalfEdge he) { int bucket; if (he.YStar < Geometry.Bounds.Top) bucket = 0; else if (he.YStar >= Geometry.Bounds.Bottom) bucket = _hashSize - 1; else bucket = (int) ((he.YStar - Geometry.Bounds.Top)/Geometry.DeltaY*_hashSize); if (bucket < 0) { bucket = 0; } if (bucket >= _hashSize) { bucket = _hashSize - 1; } if (bucket < _min) { _min = bucket; } return bucket; }
public static int PQbucket(HalfEdge he) { int bucket; if (he.ystar < VoronoiMain.ymin) bucket = 0; else if (he.ystar >= VoronoiMain.ymax) bucket = PQhashsize - 1; else bucket = (int)((he.ystar - VoronoiMain.ymin) / VoronoiGeometry.deltay * PQhashsize); if (bucket < 0) bucket = 0; if (bucket >= PQhashsize) bucket = PQhashsize - 1; if (bucket < PQmin) PQmin = bucket; return bucket; }
public static void PQinsert(HalfEdge he, Site v, double offset) { he.Vertex = v; VoronoiGeometry.Ref(v); he.ystar = v.Coord.Y + offset; HalfEdge last = PQhash[PQbucket(he)]; HalfEdge next; while ((next = last.PQnext) != null && (he.ystar > next.ystar || he.ystar == next.ystar && v.Coord.X > next.Vertex.Coord.X)) { last = next; } he.PQnext = last.PQnext; last.PQnext = he; PQcount++; }
internal static Site intersect(HalfEdge el1, HalfEdge el2) { Edge e1 = el1.Edge; Edge e2 = el2.Edge; Edge e; HalfEdge el; if (e1 == null || e2 == null) return null; if (e1.reg[1] == e2.reg[1]) return null; double d = e1.a * e2.b - e1.b * e2.a; if (-1E-10 < d && d < 1E-10) return null; double xint = (e1.c * e2.b - e2.c * e1.b) / d; double yint = (e2.c * e1.a - e1.c * e2.a) / d; if (e1.reg[1].Coord.Y < e2.reg[1].Coord.Y || e1.reg[1].Coord.Y == e2.reg[1].Coord.Y && e1.reg[1].Coord.X < e2.reg[1].Coord.X) { el = el1; e = e1; } else { el = el2; e = e2; } bool right_of_site = xint >= e.reg[1].Coord.X; if (right_of_site && el.ELpm == EndPoint.Left || !right_of_site && el.ELpm == EndPoint.Right) return null; Site v = new Site(); //** Memory.getfree( v.refCount = 0; v.Coord = new Point(xint, yint); return v; }
public static void ELinitialize() { ELhashsize = 2 * VoronoiGeometry.sqrt_nsites; ELhash = new HalfEdge[ELhashsize]; for (int i = 0; i < ELhashsize; i++) { ELhash[i] = null; } ELleftend = HEcreate(null, 0); ELrightend = HEcreate(null, 0); ELleftend.Left = null; ELleftend.Right = ELrightend; ELrightend.Left = ELleftend; ELrightend.Right = null; ELhash[0] = ELleftend; ELhash[ELhashsize - 1] = ELrightend; }
ushort[] FindOutlines(Vector2[] vertices, ushort[] triangles) { var halfEdges = new HashSet<HalfEdge> (); for (var i = 0; i < triangles.Length; i+=3) { for (var j = 0; j < 3; j++) { var t0 = triangles[j + i]; var t1 = triangles[(j + 1) % 3 + i]; var h = new HalfEdge(t0, t1); halfEdges.Add(h); } } var outlines = new Dictionary<ushort, HalfEdge> (); var t = (ushort)0; foreach (var h0 in halfEdges) { var h1 = h0.Opposite(); if (!halfEdges.Contains(h1)) outlines.Add(t = h0.t0, h0); } var result = new List<ushort> (outlines.Count * 2); while (outlines.Count > 0 && outlines.ContainsKey(t)) { var h = outlines[t]; outlines.Remove(t); result.Add(h.t0); t = h.t1; } var k = 0; while (k < result.Count) { var t0 = result[k]; var t1 = result[(k + 1) % outlines.Count]; var d = vertices[t0] - vertices[t1]; if (d.sqrMagnitude < (degenerationDistance * degenerationDistance)) result.RemoveAt(k); else k++; } return result.ToArray (); }
private void Flip(HalfEdge h) { HalfEdge h1 = h; HalfEdge h2 = h1.Next; HalfEdge h3 = h2.Next; HalfEdge h4 = h.Twin; HalfEdge h5 = h4.Next; HalfEdge h6 = h5.Next; if (h1.Face == null || h4.Face == null) return; LogEntry logEntry = new LogEntry("Flipping edge", this); logEntry.Objects.Add(new Edge(h.Origin, h.Next.Origin)); Log.Add(logEntry); // Remove old faces Faces.Remove(h.Face); Faces.Remove(h.Twin.Face); h1.Next = h6; h6.Prev = h1; h6.Next = h2; h2.Prev = h6; h2.Next = h1; h1.Prev = h2; h1.Origin = h3.Origin; h4.Next = h3; h3.Prev = h4; h3.Next = h5; h5.Prev = h3; h5.Next = h4; h4.Prev = h5; h4.Origin = h6.Origin; Faces.Add(new Triangle(h1)); Faces.Add(new Triangle(h1.Twin)); }
internal static Site Intersect(HalfEdge el1, HalfEdge el2) { var e1 = el1.Edge; var e2 = el2.Edge; if ((e1 == null) || e2 == null) { return null; } if (e1.Sites[LR.Right] == e2.Sites[LR.Right]) { return null; } var d = (e1.A*e2.B) - (e1.B*e2.A); if ((-1.0e-10 < d) && (d < 1.0e-10)) { return null; } var xint = (e1.C*e2.B - e2.C*e1.B)/d; var yint = (e2.C*e1.A - e1.C*e2.A)/d; HalfEdge el; Edge e; if ((e1.Sites[LR.Right].Coord.Y < e2.Sites[LR.Right].Coord.Y) || (e1.Sites[LR.Right].Coord.Y == e2.Sites[LR.Right].Coord.Y && e1.Sites[LR.Right].Coord.X < e2.Sites[LR.Right].Coord.X)) { el = el1; e = e1; } else { el = el2; e = e2; } var rightOfSite = (xint >= e.Sites[LR.Right].Coord.X); if ((rightOfSite && (el.LeftRight == LR.Left)) || (!rightOfSite && (el.LeftRight == LR.Right))) { return null; } var v = new Site(); v.Coord = new Vector2(xint, yint); return v; }
public Triangle(HalfEdge halfEdge) : base(halfEdge) { Vertex v1 = HalfEdge.Origin; Vertex v2 = HalfEdge.Next.Origin; Vertex v3 = HalfEdge.Next.Next.Origin; Vertex v4 = HalfEdge.Next.Next.Next.Origin; if (v1 == v2 || v2 == v3 || v1 == v3) throw new IncorrectTriangleException("Triangle does not has a correct 3 vertex loop."); if (v1 != v4) throw new IncorrectTriangleException("Triangle does not has a correct 3 vertex loop."); // Fix halfedges to this face halfEdge.Face = this; halfEdge.Next.Face = this; halfEdge.Next.Next.Face = this; // Add vertices to the array Vertices.Add(v1); Vertices.Add(v2); Vertices.Add(v3); }
public void Deleted(HalfEdge e) { Edges--; Assert.IsTrue(e.Primary); }
private void Populate(UnityEngine.Mesh mesh) { edges = new Dictionary<long, HalfEdge>(mesh.triangles.Length); faces = new HashSet<Face>(); vertexFaces = new Face[mesh.vertexCount]; vertices = new Vector3[mesh.vertexCount]; normals = new Vector3[mesh.vertexCount]; uvs = new Vector2[mesh.vertexCount]; verticesDelta = new HashSet<int>(); Array.Copy(mesh.vertices, vertices, mesh.vertexCount); Array.Copy(mesh.normals, normals, mesh.vertexCount); Array.Copy(mesh.uv, uvs, mesh.vertexCount); var tris = mesh.triangles; for (int i = 0; i < tris.Length; i+=3) { var edge0 = HashEdge(tris[i], tris[i + 1]); var edge1 = HashEdge(tris[i+1], tris[i + 2]); var edge2 = HashEdge(tris[i+2], tris[i]); var face = new Face(tris[i], tris[i+1], tris[i+2]); var he0 = new HalfEdge { face = face, point = tris[i] }; var he1 = new HalfEdge { face = face, point = tris[i+1] }; var he2 = new HalfEdge { face = face, point = tris[i+2] }; vertexFaces[tris[i]] = face; vertexFaces[tris[i+1]] = face; vertexFaces[tris[i+2]] = face; try { he0.nextEdge = he1; he1.nextEdge = he2; he2.nextEdge = he0; edges.Add(edge0, he0); edges.Add(edge1, he1); edges.Add(edge2, he2); faces.Add(face); } catch{} // catch (Exception ex) // { // MeshUtils.Log("Duplicated face detected."); // } } foreach (var halfEdge in edges.Values) { var p0 = halfEdge.point; var p1 = halfEdge.nextEdge.point; if (halfEdge.oppositeEdge == null) { var oppositeEdgeHash = HashEdge(p1, p0); HalfEdge oppositeHalfEdge; if (edges.TryGetValue(oppositeEdgeHash, out oppositeHalfEdge)) { halfEdge.oppositeEdge = oppositeHalfEdge; oppositeHalfEdge.oppositeEdge = halfEdge; } } } }
/// <summary> /// Tries to find an already existing halfedge for the given vertices /// </summary> /// <param name="origin"></param> /// <param name="target"></param> /// <param name="result">The matching halfedge, or null</param> /// <returns>true on success, false on failure</returns> public bool TryGetHalfEdgeBetweenVertices(Vertex origin, Vertex target, out HalfEdge result) { return(HalfEdgeQuery.TryGetHalfEdge(origin, target, out result)); }
void Mesh.IChangeListener.Deleted(HalfEdge e) { }
/// <summary> /// 右ハーフエッジについて(相互)接続します /// </summary> /// <param name="right"></param> internal void ConnectRight(HalfEdge right) { this.Right = right; right.HostEdge = this; }
//Alternative 1. Triangulate with some algorithm - then flip edges until we have a delaunay triangulation public static List <Triangle> TriangulateByFlippingEdges(List <Vector3> sites) { //Step 1. Triangulate the points with some algorithm //Vector3 to vertex List <Vertex> vertices = new List <Vertex>(); for (int i = 0; i < sites.Count; i++) { vertices.Add(new Vertex(sites[i])); } //Triangulate the convex hull of the sites List <Triangle> triangles = IncrementalTriangulationAlgorithm.TriangulatePoints(vertices); //List<Triangle> triangles = TriangulatePoints.IncrementalTriangulation(vertices); //List triangles = TriangulatePoints.TriangleSplitting(vertices); //Step 2. Change the structure from triangle to half-edge to make it faster to flip edges List <HalfEdge> halfEdges = TransformFromTriangleToHalfEdge(triangles); // // List<HalfEdge> halfEdges = TransformRepresentation.TransformFromTriangleToHalfEdge(triangles); //Step 3. Flip edges until we have a delaunay triangulation int safety = 0; int flippedEdges = 0; while (true) { safety += 1; if (safety > 100000) { Debug.Log("Stuck in endless loop"); break; } bool hasFlippedEdge = false; //Search through all edges to see if we can flip an edge for (int i = 0; i < halfEdges.Count; i++) { HalfEdge thisEdge = halfEdges[i]; //Is this edge sharing an edge, otherwise its a border, and then we cant flip the edge if (thisEdge.oppositeEdge == null) { continue; } //The vertices belonging to the two triangles, c-a are the edge vertices, b belongs to this triangle Vertex a = thisEdge.v; Vertex b = thisEdge.nextEdge.v; Vertex c = thisEdge.prevEdge.v; Vertex d = thisEdge.oppositeEdge.nextEdge.v; Vector2 aPos = a.GetPos2D_XZ(); Vector2 bPos = b.GetPos2D_XZ(); Vector2 cPos = c.GetPos2D_XZ(); Vector2 dPos = d.GetPos2D_XZ(); //Use the circle test to test if we need to flip this edge if (Geometry.IsPointInsideOutsideOrOnCircle(aPos, bPos, cPos, dPos) < 0f) { //Are these the two triangles that share this edge forming a convex quadrilateral? //Otherwise the edge cant be flipped if (Geometry.IsQuadrilateralConvex(aPos, bPos, cPos, dPos)) { //If the new triangle after a flip is not better, then dont flip //This will also stop the algoritm from ending up in an endless loop if (Geometry.IsPointInsideOutsideOrOnCircle(bPos, cPos, dPos, aPos) < 0f) { continue; } //Flip the edge flippedEdges += 1; hasFlippedEdge = true; FlipEdge(thisEdge); } } } //We have searched through all edges and havent found an edge to flip, so we have a Delaunay triangulation! if (!hasFlippedEdge) { //Debug.Log("Found a delaunay triangulation"); break; } } //Debug.Log("Flipped edges: " + flippedEdges); //Dont have to convert from half edge to triangle because the algorithm will modify the objects, which belongs to the //original triangles, so the triangles have the data we need return(triangles); }
//////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> Updates the Trapezoid which an endpoint of the edge is located in. </summary> /// /// <remarks> Note - we presume the caller is modifying trapezoids intersected by the side from /// left to right. The lNeighborTop and lNeighborBottom are the top and bottom /// trapezoids created by the edge splitting in the trapezoid to our left. This is /// so we can merge one of our upper or lower traps with the one on the left. Generally /// we can't merge right traps into left ones because the right trapezoids haven't been /// created yet. /// /// Darrell Plank, 2/12/2018. </remarks> /// /// <param name="oldTrap"> The trapNode which contains the left endpoint. </param> /// <param name="edge"> The edge whose left endpoint is in the trapezoid. </param> /// <param name="lNeighborTop"> The left neighbor's top trapezoid </param> /// <param name="lNeighborBottom"> The left neighbor's bottom trapezoid </param> //////////////////////////////////////////////////////////////////////////////////////////////////// public PlacementNode UpdateEndTrapezoid(Trapezoid oldTrap, HalfEdge edge, ref Trapezoid lNeighborTop, ref Trapezoid lNeighborBottom) { (var belowFace, var aboveFace) = GetFaces(edge); // The edge splits some portion of our trapezoid vertically. Determine the extent of that // region and which vertices bound it. var trapLeft = oldTrap.LeftVtx.X; var trapRight = oldTrap.RightVtx.X; var edgeLeft = edge.InitVertex.X; var edgeRight = edge.NextVertex.X; // "meets" in this context means the side shares a previously established vertex. // "exceeds" means "goes past" which means it splits through // the side of the trap. If neither of these is true, it ends in the proper // interior of the trapezoid. // We can use equality here since these will be coming from a shared vertex // ReSharper disable CompareOfFloatsByEqualityOperator var meetsLeft = trapLeft == edgeLeft; var meetsRight = trapRight == edgeRight; // ReSharper restore CompareOfFloatsByEqualityOperator // var exceedsLeft = trapLeft > edgeLeft; var exceedsRight = trapRight < edgeRight; var interiorLeft = trapLeft < edgeLeft; var interiorRight = trapRight > edgeRight; var midRightVtx = meetsRight || exceedsRight ? oldTrap.RightVtx : edge.NextVertex; var midLeftVtx = meetsLeft || exceedsLeft ? oldTrap.LeftVtx : edge.InitVertex; // The newly created traps to be inserted into the map Trapezoid above, below, left, right; // Create left trapezoid if necessary if (interiorLeft) { left = new Trapezoid { RightVtx = midLeftVtx, LeftVtx = oldTrap.LeftVtx, LeftTop = oldTrap.LeftTop, LeftBottom = oldTrap.LeftBottom, BottomEdge = oldTrap.BottomEdge, TopEdge = oldTrap.TopEdge, ContainingFace = oldTrap.ContainingFace }; oldTrap.LeftBottom?.LinkTo(oldTrap, left, true); oldTrap.LeftTop?.LinkTo(oldTrap, left, true); } else { left = null; } // Create right trapezoid if necessary if (interiorRight) { right = new Trapezoid { RightVtx = oldTrap.RightVtx, LeftVtx = midRightVtx, RightBottom = oldTrap.RightBottom, LeftBottom = oldTrap.LeftBottom, BottomEdge = oldTrap.BottomEdge, TopEdge = oldTrap.TopEdge, ContainingFace = oldTrap.ContainingFace }; oldTrap.RightBottom?.LinkTo(oldTrap, right, false); oldTrap.RightTop?.LinkTo(oldTrap, right, false); } else { right = null; } TrapNode aboveNode = null, belowNode = null; // Create top/bottom trapezoids (which is always necessary) if (exceedsLeft) { // These assumptions need to be met when merging below. This should all compile out // in the release build. Debug.Assert(oldTrap.LeftTop == null ^ oldTrap.LeftBottom == null); Debug.Assert(lNeighborBottom != null && lNeighborTop != null); if (oldTrap.LeftBottom == null) { // Old trap's left side comes up from vertex below and we're cutting it off so merge top traps above = lNeighborTop; above.RightVtx = midRightVtx; aboveNode = above.Node; // Create new bottom trap below = new Trapezoid { RightVtx = midRightVtx, LeftVtx = midLeftVtx, LeftTop = lNeighborBottom, TopEdge = edge, BottomEdge = oldTrap.BottomEdge, ContainingFace = belowFace }; lNeighborBottom.RightTop = below; } else { // Merge bottom traps below = lNeighborBottom; below.RightVtx = midRightVtx; belowNode = below.Node; // Create new top trap above = new Trapezoid { RightVtx = midRightVtx, LeftVtx = midLeftVtx, LeftBottom = lNeighborTop, BottomEdge = edge, TopEdge = oldTrap.TopEdge, ContainingFace = aboveFace }; lNeighborTop.RightBottom = above; } } else if (meetsLeft) { var leftOfTopTrap = oldTrap.LeftTop; var leftOfBottomTrap = oldTrap.LeftBottom; above = new Trapezoid { RightVtx = midRightVtx, LeftVtx = midLeftVtx, LeftTop = leftOfTopTrap, TopEdge = oldTrap.TopEdge, BottomEdge = edge, ContainingFace = aboveFace }; below = new Trapezoid { RightVtx = midRightVtx, LeftVtx = midLeftVtx, LeftBottom = leftOfBottomTrap, BottomEdge = oldTrap.BottomEdge, TopEdge = edge, ContainingFace = belowFace }; oldTrap.LeftTop?.LinkTo(oldTrap, above, true); oldTrap.LeftBottom?.LinkTo(oldTrap, below, true); } else { // Interior on left - i.e., we created a left trapezoid within the old trapezoid above = new Trapezoid { RightVtx = midRightVtx, LeftVtx = midLeftVtx, LeftTop = left, TopEdge = oldTrap.TopEdge, BottomEdge = edge, ContainingFace = aboveFace }; below = new Trapezoid { RightVtx = midRightVtx, LeftVtx = midLeftVtx, LeftBottom = left, BottomEdge = oldTrap.BottomEdge, TopEdge = edge, ContainingFace = belowFace }; // ReSharper disable once PossibleNullReferenceException left.RightTop = above; left.RightBottom = below; } if (meetsRight) { above.RightTop = oldTrap.RightTop; above.RightBottom = null; below.RightTop = null; below.RightBottom = oldTrap.RightBottom; oldTrap.RightTop?.LinkTo(oldTrap, above, false); oldTrap.RightBottom?.LinkTo(oldTrap, below, false); } else if (interiorRight) { above.RightTop = right; above.RightBottom = null; below.RightTop = null; below.RightBottom = right; right.LeftTop = above; right.LeftBottom = below; } // If exceedRight then we'll be backpatched in the next trap to the right. if (aboveNode == null) { aboveNode = new TrapNode(above); } if (belowNode == null) { belowNode = new TrapNode(below); } lNeighborTop = above; lNeighborBottom = below; // Okay - finally done with the map. Now to deal with the tree. // We always have the edge split to concern ourselves with PlacementNode repl = new YNode(edge.InitVertex.Position, edge.NextVertex.Position, aboveNode, belowNode); // Do we split off a right trap? if (interiorRight) { repl = new XNode(repl, new TrapNode(right), right.LeftVtx); //repl = new XNode(repl, new TrapNode(right), midRight); } // How about splitting off a left trap? if (interiorLeft) { repl = new XNode(new TrapNode(left), repl, left.RightVtx); //repl = new XNode(new TrapNode(left), repl, midLeft); } return(repl); }
public void Added(HalfEdge e) { Edges++; Assert.IsTrue(e.Primary); }
public static void Insert(HalfEdge lb, HalfEdge he) { he.Left = lb; he.Right = lb.Right; lb.Right.Left = he; lb.Right = he; }
public override HalfEdge <T> CreateHalfEdge(Vertex <T> vertex, HalfEdge <T> opposite, Face <T> face, HalfEdge <T> nextEdge) { return(new PrevEdgeHalfEdge <T>(vertex, opposite, face, nextEdge)); }
public static DcelMesh ToDCEL(Mesh mesh) { var dcel = new DcelMesh(); var vertices = new HVertex[mesh.vertices.Count]; var faces = new Face[mesh.triangles.Count]; dcel.HalfEdges.Capacity = 2 * mesh.NumberOfEdges; mesh.Renumber(); HVertex vertex; foreach (var v in mesh.vertices.Values) { vertex = new HVertex(v.x, v.y); vertex.id = v.id; vertex.label = v.label; vertices[v.id] = vertex; } // Maps a triangle to its 3 edges (used to set next pointers). var map = new List <HalfEdge> [mesh.triangles.Count]; Face face; foreach (var t in mesh.triangles) { face = new Face(null); face.id = t.id; faces[t.id] = face; map[t.id] = new List <HalfEdge>(3); } Otri tri = default(Otri), neighbor = default(Otri); TriangleNet.Geometry.Vertex org, dest; int id, nid, count = mesh.triangles.Count; HalfEdge edge, twin, next; var edges = dcel.HalfEdges; // Count half-edges (edge ids). int k = 0; // Maps a vertex to its leaving boundary edge. var boundary = new Dictionary <int, HalfEdge>(); foreach (var t in mesh.triangles) { id = t.id; tri.tri = t; for (int i = 0; i < 3; i++) { tri.orient = i; tri.Sym(ref neighbor); nid = neighbor.tri.id; if (id < nid || nid < 0) { face = faces[id]; // Get the endpoints of the current triangle edge. org = tri.Org(); dest = tri.Dest(); // Create half-edges. edge = new HalfEdge(vertices[org.id], face); twin = new HalfEdge(vertices[dest.id], nid < 0 ? Face.Empty : faces[nid]); map[id].Add(edge); if (nid >= 0) { map[nid].Add(twin); } else { boundary.Add(dest.id, twin); } // Set leaving edges. edge.origin.leaving = edge; twin.origin.leaving = twin; // Set twin edges. edge.twin = twin; twin.twin = edge; edge.id = k++; twin.id = k++; edges.Add(edge); edges.Add(twin); } } } // Set next pointers for each triangle face. foreach (var t in map) { edge = t[0]; next = t[1]; if (edge.twin.origin.id == next.origin.id) { edge.next = next; next.next = t[2]; t[2].next = edge; } else { edge.next = t[2]; next.next = edge; t[2].next = next; } } // Resolve boundary edges. foreach (var e in boundary.Values) { e.next = boundary[e.twin.origin.id]; } dcel.Vertices.AddRange(vertices); dcel.Faces.AddRange(faces); return(dcel); }
private void DrawHeEdges(DrawPen borderPen, DrawPen edgePen, double edgeThreshold, HeModel model) { bool drawBorder = !borderPen.IsNullPen; bool drawEdge = !edgePen.IsNullPen; if (!drawBorder && !drawEdge) { return; } DisableLight(); GL.LineWidth(1.0f); Color4 color = borderPen.Color4(); Color4 edgeColor = edgePen.Color4(); Vector3d shift = GetShiftForOutLine(); Vector3d p0; Vector3d p1; for (int i = 0; i < model.FaceStore.Count; i++) { HeFace f = model.FaceStore[i]; HalfEdge head = f.Head; HalfEdge c = head; HalfEdge pair; p0 = model.VertexStore.Ref(c.Vertex).vector *DC.WorldScale + shift; for (; ;) { bool drawAsEdge = false; pair = c.Pair; if (drawEdge) { if (pair == null) { drawAsEdge = true; } else { if (edgeThreshold != 0) { double s = CadMath.InnerProduct(model.NormalStore[c.Normal], model.NormalStore[pair.Normal]); if (Math.Abs(s) <= edgeThreshold) { drawAsEdge = true; } } } } p1 = model.VertexStore.Ref(c.Next.Vertex).vector *DC.WorldScale + shift; if (drawAsEdge) { GL.Color4(edgeColor); GL.Begin(PrimitiveType.Lines); GL.Vertex3(p0); GL.Vertex3(p1); GL.End(); } else { if (drawBorder) { GL.Color4(color); GL.Begin(PrimitiveType.Lines); GL.Vertex3(p0); GL.Vertex3(p1); GL.End(); } } p0 = p1; c = c.Next; if (c == head) { break; } } } }
/// <summary> /// Le suivant de l'opposé /// </summary> /// <param name="dart">Brin actuel</param> /// <returns>Retourne le brin du point suivant</returns> public HalfEdge NextDartOnPoint(HalfEdge dart) { return(dart.Opposite.Next); }
// Relie de points pour former une arête private void LinkTwoDegeneratedDarts(HalfEdge dart1, HalfEdge dart2) { dart1.SetHalfEdge(dart2, dart2, dart2); dart2.SetHalfEdge(dart1, dart1, dart1); }
public Site RightRegion(HalfEdge he) { if (he.Edge == null) { return _siteList.BottomSite; } return he.Side == Side.Left ? he.Edge.Region[Side.Right] : he.Edge.Region[Side.Left]; }
public void SplitMidpointEnd(HalfEdge e, Vertex mid) { throw new NotImplementedException(); }
protected override void ChangeBoundaryToInternalHook(HalfEdge edge) { _boundaryEdges.Remove(edge); }
internal static bool TrySplitFace(this Kernel kernel, HalfEdge start, HalfEdge end, out (Face, Face) parts) { parts = (null, null); // TODO: Error checks // get edges for face of both halfedges var edges = new EdgeIterator(start).ToArray(); // can't split triangles if (edges.Length <= 3) { return(false); } // get the index of the end edge inside the edges array var endIndex = Array.IndexOf(edges, end); // create the new ending halfedge for the start half var newEnd = new HalfEdge { Face = start.Face, Next = start, Previous = edges[endIndex - 1], Origin = end.Origin }; // create the new Face for the end half var newFace = new Face { Start = end }; // create a new starting edge for the end half var newStart = new HalfEdge { Face = newFace, Next = end, Previous = edges.Last(), Origin = start.Origin, Pair = newEnd }; // pair up the new end newEnd.Pair = newStart; // establish circular link for first face var firstFaceEdges = edges.Take(endIndex).ToList(); firstFaceEdges.Add(newEnd); EdgeLinker.LinkOrderedEdgeCollection(firstFaceEdges); // hacky re-assignment newEnd.Face.Start = newEnd.Next; // establish circular link for second face var secondFaceEdges = edges.Skip(endIndex).ToList(); secondFaceEdges.Insert(0, newStart); EdgeLinker.LinkOrderedEdgeCollection(secondFaceEdges); secondFaceEdges.ForEach(e => e.Face = newFace); // Add new edges to kernel kernel.Add(newEnd); kernel.Add(newStart); // add new face kernel.Insert(newFace); // prepare output parts = (start.Face, newFace); return(true); }
void Mesh.IChangeListener.Added(HalfEdge e) { }
private void Process(HalfEdge e) { HalfEdge eNode = e.PrevNode(); // if edge is in a ring, just process this edge if (eNode == null) eNode = e; StackEdges(eNode); // extract lines from node edges in stack BuildLines(); }
void Mesh.IChangeListener.SplitMidpointBegin(HalfEdge e, Vertex mid) { }
private void BuildRing(HalfEdge eStartRing) { CoordinateList line = new CoordinateList(); HalfEdge e = eStartRing; Coordinate orig = e.Orig; line.Add(orig.Clone(), false); // scan along the path until a node is found (if one exists) while (e.Sym.Degree() == 2) { HalfEdge eNext = e.Next; // check if edges form a ring - if so, we're done if (eNext == eStartRing) break; // add point to line, and move to next edge orig = eNext.Orig; line.Add(orig.Clone(), false); e = eNext; } // add final node Coordinate dest = e.Dest; line.Add(dest.Clone(), false); // store the scanned line AddLine(line); }
/// <summary> /// Inserts a <see cref="HalfEdge"/> instance into the kernel. /// For this to work the halfedge needs linking information present on its /// <see cref="HalfEdge.Next"/> and <see cref="HalfEdge.Previous" /> properties. /// If no linking information is present for the <see cref="HalfEdge.Pair"/>, a new pair edge /// will be created and inserted as a naked edge /// </summary> /// <param name="edge"></param> /// <returns>true on success, false on failure</returns> public bool Insert(HalfEdge edge) { return(_halfEdges.Insert(edge)); }
/// <summary> /// 左ハーフエッジについて(相互)接続します /// </summary> /// <param name="left"></param> internal void ConnectLeft(HalfEdge left) { this.Left = left; left.HostEdge = this; }
/// <summary> /// Adds a new face to the kernel /// </summary> /// <param name="positions"></param> public bool AddFace(IEnumerable <Vec3d> positions) { // No bad faces please :( if (positions.Count() < 3) { return(false); } // convert positions to vertices inside the mesh var vertices = VertexQuery.GetVerticesForPositions(_vertices, positions); // if the positions had stacked vertices, we might not be able to continue if (vertices.Count() < 3) { // iterate over position vertices foreach (var vertex in vertices) { // if the vertex was old, it will have an outgoing if (vertex.Outgoing != null) { continue; } // if it was new we need to remove it to restore state Remove(vertex); } return(false); } var edges = new List <HalfEdge>(); // iterate over all positions foreach (var vertex in vertices) { // create a new halfedge originating from the current vertex and linked to the new face var halfEdge = new HalfEdge { Origin = vertex, }; // test if the vertex already has an outgoing edge assigned if (vertex.Outgoing is null) { vertex.Outgoing = halfEdge; } edges.Add(halfEdge); } // Insert a face from the edges Insert(edges); // iterate over all edges and insert them foreach (var edge in edges) { // TODO: Maybe we should NOT skip linking checks here _halfEdges.Add(edge); } // test if first face if (this.FaceCount == 1) { var outerRing = new EdgeIterator( _halfEdges[0]) .Select(h => h.Pair) .Reverse() .ToList(); // establish circular link between outside half-edges EdgeLinker.LinkOrderedEdgeCollection(outerRing); } // TODO: Should be method of edgelinker instead // Make sure edge pairs either have a starting face or the ghost face // Iterators are lazy, calling `Count()` will execute the select body _ = edges.Where(e => e.Pair.Face == null).Select(e => e.Pair.Face = Kernel.Outside).Count(); return(true); // TODO: Make sure outer halfEdges are linked,too // TODO: Right now only inner halfEdges are linked circularly // TODO: This leads to crashes on faces with too many naked edges when creating vertexringiterators // TODO: For this we will need a 'ghost' face that encompasses all the space outside of the mesh faces // TODO: CLosed meshes will have an empty ghost face }
public void TestHasPrevious_HasNoPrevious() { HalfEdge edge = Auxilaries.RandomHalfEdge(); Assert.IsFalse(edge.HasPrevious); }
public float GetNearestEdge(Ray ray, out HalfEdge minEdge, bool excludeBackSidePolygons) { var localOrigin = Transform.InverseTransformPoint(ray.origin); var localDir = Transform.InverseTransformDirection(ray.direction); var localRay = new Ray(localOrigin, localDir); var minDistance = float.MaxValue; minEdge = null; foreach (var edge in edges.Values) { var p0 = vertices[edge.point]; var p1 = vertices[edge.nextEdge.point]; var squaredDist = MeshUtils.SegmentSegmentDistance2(p0, p1, localRay.origin, localRay.origin + localRay.direction*10000.0f); if (squaredDist < minDistance) { if (excludeBackSidePolygons) { var p2 = vertices[edge.nextEdge.nextEdge.point]; if (!Utils.Plane.GetSide(p0, p1, p2, localRay.origin)) { continue; } } minDistance = squaredDist; minEdge = edge; } } return minDistance; }
public void TestHasNext_HasNoNext() { HalfEdge edge = Auxilaries.RandomHalfEdge(); Assert.IsFalse(edge.HasNext); }
public BreakpointNode(Site leftSite, Site rightSite, HalfEdge halfEdge, bool isLeftBreakpoint) { LeftSite = leftSite; RightSite = rightSite; HalfEdge = halfEdge; IsLeftBreakpoint = isLeftBreakpoint; }
public void TestHasDestination_HasNoDestination() { HalfEdge edge = Auxilaries.RandomHalfEdge(); Assert.IsFalse(edge.HasDestination); }
public void TestHasIncidentFace_HasNoIncidentFace() { HalfEdge edge = Auxilaries.RandomHalfEdge(); Assert.IsFalse(edge.HasDestination); }
//From triangle where each triangle has one vertex to half edge public static List <HalfEdge> TransformFromTriangleToHalfEdge(List <Triangle> triangles) { //Make sure the triangles have the same orientation MeshOperations.OrientTrianglesClockwise(triangles); //First create a list with all possible half-edges List <HalfEdge> halfEdges = new List <HalfEdge>(triangles.Count * 3); for (int i = 0; i < triangles.Count; i++) { Triangle t = triangles[i]; HalfEdge he1 = new HalfEdge(t.v1); HalfEdge he2 = new HalfEdge(t.v2); HalfEdge he3 = new HalfEdge(t.v3); he1.nextEdge = he2; he2.nextEdge = he3; he3.nextEdge = he1; he1.prevEdge = he3; he2.prevEdge = he1; he3.prevEdge = he2; //The vertex needs to know of an edge going from it he1.v.halfEdge = he2; he2.v.halfEdge = he3; he3.v.halfEdge = he1; //The face the half-edge is connected to t.halfEdge = he1; he1.t = t; he2.t = t; he3.t = t; //Add the half-edges to the list halfEdges.Add(he1); halfEdges.Add(he2); halfEdges.Add(he3); } //Find the half-edges going in the opposite direction for (int i = 0; i < halfEdges.Count; i++) { HalfEdge he = halfEdges[i]; Vertex goingToVertex = he.v; Vertex goingFromVertex = he.prevEdge.v; for (int j = 0; j < halfEdges.Count; j++) { //Dont compare with itself if (i == j) { continue; } HalfEdge heOpposite = halfEdges[j]; //Is this edge going between the vertices in the opposite direction if (goingFromVertex.position == heOpposite.v.position && goingToVertex.position == heOpposite.prevEdge.v.position) { he.oppositeEdge = heOpposite; break; } } } return(halfEdges); }
public static void Delete(HalfEdge he) { he.Left.Right = he.Right; he.Right.Left = he.Left; he.Edge = null; }
protected override void AddBoundaryEdgeHook(HalfEdge edge) { _boundaryEdges.Add(edge); }
public Triangle(HalfEdge halfEdge) { this.halfEdge = halfEdge; }
//Flip an edge private static void FlipEdge(HalfEdge one) { //The data we need //This edge's triangle HalfEdge two = one.nextEdge; HalfEdge three = one.prevEdge; //The opposite edge's triangle HalfEdge four = one.oppositeEdge; HalfEdge five = one.oppositeEdge.nextEdge; HalfEdge six = one.oppositeEdge.prevEdge; //The vertices Vertex a = one.v; Vertex b = one.nextEdge.v; Vertex c = one.prevEdge.v; Vertex d = one.oppositeEdge.nextEdge.v; //Flip //Change vertex a.halfEdge = one.nextEdge; c.halfEdge = one.oppositeEdge.nextEdge; //Change half-edge //Half-edge - half-edge connections one.nextEdge = three; one.prevEdge = five; two.nextEdge = four; two.prevEdge = six; three.nextEdge = five; three.prevEdge = one; four.nextEdge = six; four.prevEdge = two; five.nextEdge = one; five.prevEdge = three; six.nextEdge = two; six.prevEdge = four; //Half-edge - vertex connection one.v = b; two.v = b; three.v = c; four.v = d; five.v = d; six.v = a; //Half-edge - triangle connection Triangle t1 = one.t; Triangle t2 = four.t; one.t = t1; three.t = t1; five.t = t1; two.t = t2; four.t = t2; six.t = t2; //Opposite-edges are not changing! //Triangle connection t1.v1 = b; t1.v2 = c; t1.v3 = d; t2.v1 = b; t2.v2 = d; t2.v3 = a; t1.halfEdge = three; t2.halfEdge = four; }
/// <summary> /// Builds a line starting from the given edge. /// The start edge origin is a node (valence = 1 or >= 3), /// unless it is part of a pure ring. /// </summary> /// <remarks> /// A pure ring has no other incident lines. /// In this case the start edge may occur anywhere on the ring. /// </remarks> /// <remarks> /// The line is built up to the next node encountered, /// or until the start edge is re-encountered /// (which happens if the edges form a ring). /// </remarks> /// <param name="eStart"></param> private void BuildLine(HalfEdge eStart) { CoordinateList line = new CoordinateList(); DissolveHalfEdge e = (DissolveHalfEdge)eStart; _ringStartEdge = null; MarkHalfEdge.MarkBoth(e); Coordinate orig = e.Orig; line.Add(orig.Clone(), false); // scan along the path until a node is found (if one exists) while (e.Sym.Degree() == 2) { UpdateRingStartEdge(e); DissolveHalfEdge eNext = (DissolveHalfEdge)e.Next; // check if edges form a ring - if so, we're done if (eNext == eStart) { BuildRing(_ringStartEdge); return; } // add point to line, and move to next edge orig = eNext.Orig; line.Add(orig.Clone(), false); e = eNext; MarkHalfEdge.MarkBoth(e); } // add final node Coordinate dest = e.Dest; line.Add(dest.Clone(), false); // queue up the final node edges StackEdges(e.Sym); // store the scanned line AddLine(line); }
public void TestEquals_NotEqualsDifferentIncidentFaces() { Vertex a = Auxilaries.RandomVertex(); Vertex b = Auxilaries.RandomVertex(); Vertex c = Auxilaries.RandomVertex(); HalfEdge abOther = new HalfEdge(a); HalfEdge abThis = new HalfEdge(a); HalfEdge ac = new HalfEdge(a); HalfEdge ba = new HalfEdge(b); HalfEdge bc = new HalfEdge(b); HalfEdge ca = new HalfEdge(c); HalfEdge cb = new HalfEdge(c); Vector3 normal = Auxilaries.RandomNormal(); Face thisFace = new Face(0, normal); thisFace.AddOuterComponent(abThis); thisFace.AddOuterComponent(bc); thisFace.AddOuterComponent(ca); Face otherFace = new Face(1, normal); otherFace.AddOuterComponent(abOther); abThis.IncidentFace = thisFace; abOther.IncidentFace = otherFace; abThis.Twin = ba; abOther.Twin = ba; ac.Twin = ca; ba.Twin = abThis; bc.Twin = bc; ca.Twin = ac; cb.Twin = bc; abThis.Next = bc; abOther.Next = bc; ac.Next = cb; ba.Next = ac; bc.Next = ca; ca.Next = abThis; cb.Next = ba; abThis.Previous = ca; abOther.Previous = ca; ac.Previous = ba; ba.Previous = cb; bc.Previous = abThis; ca.Previous = bc; cb.Previous = ac; Assert.AreNotEqual(abThis.GetHashCode(), abOther.GetHashCode()); Assert.IsFalse(abThis.Equals(abOther)); Assert.IsFalse(abOther.Equals(abThis)); }
/// <summary> /// Adds edges around this node to the stack. /// </summary> /// <param name="node"></param> private void StackEdges(HalfEdge node) { HalfEdge e = node; do { if (!MarkHalfEdge.IsMarked(e)) _nodeEdgeStack.Push(e); e = e.ONext; } while (e != node); }
public void TestHasTwin_HasNoTwin() { HalfEdge edge = Auxilaries.RandomHalfEdge(); Assert.IsFalse(edge.HasTwin); }