/// <summary> /// Creates a Plankton halfedge mesh from a Rhino mesh. /// Uses the topology of the Rhino mesh directly. /// </summary> /// <returns>A <see cref="PlanktonMesh"/> which represents the topology and geometry of the source mesh.</returns> /// <param name="source">A Rhino mesh to convert from.</param> public static PlanktonMesh ToPlanktonMesh(this Mesh source) { PlanktonMesh pMesh = new PlanktonMesh(); source.Vertices.CombineIdentical(true, true); source.Vertices.CullUnused(); source.UnifyNormals(); source.Weld(Math.PI); foreach (Point3f v in source.TopologyVertices) { pMesh.Vertices.Add(v.X, v.Y, v.Z); } for (int i = 0; i < source.Faces.Count; i++) { pMesh.Faces.Add(new PlanktonFace()); } for (int i = 0; i < source.TopologyEdges.Count; i++) { PlanktonHalfedge HalfA = new PlanktonHalfedge(); HalfA.StartVertex = source.TopologyEdges.GetTopologyVertices(i).I; if (pMesh.Vertices[HalfA.StartVertex].OutgoingHalfedge == -1) { pMesh.Vertices[HalfA.StartVertex].OutgoingHalfedge = pMesh.Halfedges.Count; } PlanktonHalfedge HalfB = new PlanktonHalfedge(); HalfB.StartVertex = source.TopologyEdges.GetTopologyVertices(i).J; if (pMesh.Vertices[HalfB.StartVertex].OutgoingHalfedge == -1) { pMesh.Vertices[HalfB.StartVertex].OutgoingHalfedge = pMesh.Halfedges.Count + 1; } bool[] Match; int[] ConnectedFaces = source.TopologyEdges.GetConnectedFaces(i, out Match); //Note for Steve Baer : This Match bool doesn't seem to work on triangulated meshes - it often returns true //for both faces, even for a properly oriented manifold mesh, which can't be right //So - making our own check for matching: //(I suspect the problem is related to C being the same as D for triangles, so best to //deal with them separately just to make sure) //loop through the vertices of the face until finding the one which is the same as the start of the edge //iff the next vertex around the face is the end of the edge then it matches. Match[0] = false; if (Match.Length > 1) { Match[1] = true; } int VertA = source.TopologyVertices.TopologyVertexIndex(source.Faces[ConnectedFaces[0]].A); int VertB = source.TopologyVertices.TopologyVertexIndex(source.Faces[ConnectedFaces[0]].B); int VertC = source.TopologyVertices.TopologyVertexIndex(source.Faces[ConnectedFaces[0]].C); int VertD = source.TopologyVertices.TopologyVertexIndex(source.Faces[ConnectedFaces[0]].D); if ((VertA == source.TopologyEdges.GetTopologyVertices(i).I) && (VertB == source.TopologyEdges.GetTopologyVertices(i).J)) { Match[0] = true; } if ((VertB == source.TopologyEdges.GetTopologyVertices(i).I) && (VertC == source.TopologyEdges.GetTopologyVertices(i).J)) { Match[0] = true; } if ((VertC == source.TopologyEdges.GetTopologyVertices(i).I) && (VertD == source.TopologyEdges.GetTopologyVertices(i).J)) { Match[0] = true; } if ((VertD == source.TopologyEdges.GetTopologyVertices(i).I) && (VertA == source.TopologyEdges.GetTopologyVertices(i).J)) { Match[0] = true; } //I don't think these next 2 should ever be needed, but just in case: if ((VertC == source.TopologyEdges.GetTopologyVertices(i).I) && (VertA == source.TopologyEdges.GetTopologyVertices(i).J)) { Match[0] = true; } if ((VertB == source.TopologyEdges.GetTopologyVertices(i).I) && (VertD == source.TopologyEdges.GetTopologyVertices(i).J)) { Match[0] = true; } if (Match[0] == true) { HalfA.AdjacentFace = ConnectedFaces[0]; if (pMesh.Faces[HalfA.AdjacentFace].FirstHalfedge == -1) { pMesh.Faces[HalfA.AdjacentFace].FirstHalfedge = pMesh.Halfedges.Count; } if (ConnectedFaces.Length > 1) { HalfB.AdjacentFace = ConnectedFaces[1]; if (pMesh.Faces[HalfB.AdjacentFace].FirstHalfedge == -1) { pMesh.Faces[HalfB.AdjacentFace].FirstHalfedge = pMesh.Halfedges.Count + 1; } } else { HalfB.AdjacentFace = -1; pMesh.Vertices[HalfB.StartVertex].OutgoingHalfedge = pMesh.Halfedges.Count + 1; } } else { HalfB.AdjacentFace = ConnectedFaces[0]; if (pMesh.Faces[HalfB.AdjacentFace].FirstHalfedge == -1) { pMesh.Faces[HalfB.AdjacentFace].FirstHalfedge = pMesh.Halfedges.Count + 1; } if (ConnectedFaces.Length > 1) { HalfA.AdjacentFace = ConnectedFaces[1]; if (pMesh.Faces[HalfA.AdjacentFace].FirstHalfedge == -1) { pMesh.Faces[HalfA.AdjacentFace].FirstHalfedge = pMesh.Halfedges.Count; } } else { HalfA.AdjacentFace = -1; pMesh.Vertices[HalfA.StartVertex].OutgoingHalfedge = pMesh.Halfedges.Count; } } pMesh.Halfedges.Add(HalfA); pMesh.Halfedges.Add(HalfB); } for (int i = 0; i < (pMesh.Halfedges.Count); i += 2) { int[] EndNeighbours = source.TopologyVertices.ConnectedTopologyVertices(pMesh.Halfedges[i + 1].StartVertex, true); for (int j = 0; j < EndNeighbours.Length; j++) { if (EndNeighbours[j] == pMesh.Halfedges[i].StartVertex) { int EndOfNextHalfedge = EndNeighbours[(j - 1 + EndNeighbours.Length) % EndNeighbours.Length]; int StartOfPrevOfPairHalfedge = EndNeighbours[(j + 1) % EndNeighbours.Length]; int NextEdge = source.TopologyEdges.GetEdgeIndex(pMesh.Halfedges[i + 1].StartVertex, EndOfNextHalfedge); int PrevPairEdge = source.TopologyEdges.GetEdgeIndex(pMesh.Halfedges[i + 1].StartVertex, StartOfPrevOfPairHalfedge); if (source.TopologyEdges.GetTopologyVertices(NextEdge).I == pMesh.Halfedges[i + 1].StartVertex) { pMesh.Halfedges[i].NextHalfedge = NextEdge * 2; } else { pMesh.Halfedges[i].NextHalfedge = NextEdge * 2 + 1; } if (source.TopologyEdges.GetTopologyVertices(PrevPairEdge).J == pMesh.Halfedges[i + 1].StartVertex) { pMesh.Halfedges[i + 1].PrevHalfedge = PrevPairEdge * 2; } else { pMesh.Halfedges[i + 1].PrevHalfedge = PrevPairEdge * 2 + 1; } break; } } int[] StartNeighbours = source.TopologyVertices.ConnectedTopologyVertices(pMesh.Halfedges[i].StartVertex, true); for (int j = 0; j < StartNeighbours.Length; j++) { if (StartNeighbours[j] == pMesh.Halfedges[i + 1].StartVertex) { int EndOfNextOfPairHalfedge = StartNeighbours[(j - 1 + StartNeighbours.Length) % StartNeighbours.Length]; int StartOfPrevHalfedge = StartNeighbours[(j + 1) % StartNeighbours.Length]; int NextPairEdge = source.TopologyEdges.GetEdgeIndex(pMesh.Halfedges[i].StartVertex, EndOfNextOfPairHalfedge); int PrevEdge = source.TopologyEdges.GetEdgeIndex(pMesh.Halfedges[i].StartVertex, StartOfPrevHalfedge); if (source.TopologyEdges.GetTopologyVertices(NextPairEdge).I == pMesh.Halfedges[i].StartVertex) { pMesh.Halfedges[i + 1].NextHalfedge = NextPairEdge * 2; } else { pMesh.Halfedges[i + 1].NextHalfedge = NextPairEdge * 2 + 1; } if (source.TopologyEdges.GetTopologyVertices(PrevEdge).J == pMesh.Halfedges[i].StartVertex) { pMesh.Halfedges[i].PrevHalfedge = PrevEdge * 2; } else { pMesh.Halfedges[i].PrevHalfedge = PrevEdge * 2 + 1; } break; } } } return(pMesh); }
public PlanktonMesh Dual() { // hack for open meshes // TODO: improve this ugly method if (this.IsClosed() == false) { var dual = new PlanktonMesh(); // create vertices from face centers for (int i = 0; i < this.Faces.Count; i++) { dual.Vertices.Add(this.Faces.GetFaceCenter(i)); } // create faces from the adjacent face indices of non-boundary vertices for (int i = 0; i < this.Vertices.Count; i++) { if (this.Vertices.IsBoundary(i)) { continue; } dual.Faces.AddFace(this.Vertices.GetVertexFaces(i)); } return(dual); } // can later add options for other ways of defining face centres (barycenter/circumcenter etc) // won't work yet with naked boundaries PlanktonMesh P = this; PlanktonMesh D = new PlanktonMesh(); //for every primal face, add the barycenter to the dual's vertex list //dual vertex outgoing HE is primal face's start HE //for every vertex of the primal, add a face to the dual //dual face's startHE is primal vertex's outgoing's pair for (int i = 0; i < P.Faces.Count; i++) { var fc = P.Faces.GetFaceCenter(i); D.Vertices.Add(new PlanktonVertex(fc.X, fc.Y, fc.Z)); int[] FaceHalfedges = P.Faces.GetHalfedges(i); for (int j = 0; j < FaceHalfedges.Length; j++) { if (P.Halfedges[P.Halfedges.GetPairHalfedge(FaceHalfedges[j])].AdjacentFace != -1) { // D.Vertices[i].OutgoingHalfedge = FaceHalfedges[j]; D.Vertices[D.Vertices.Count - 1].OutgoingHalfedge = P.Halfedges.GetPairHalfedge(FaceHalfedges[j]); break; } } } for (int i = 0; i < P.Vertices.Count; i++) { if (P.Vertices.NakedEdgeCount(i) == 0) { int df = D.Faces.Add(PlanktonFace.Unset); // D.Faces[i].FirstHalfedge = P.PairHalfedge(P.Vertices[i].OutgoingHalfedge); D.Faces[df].FirstHalfedge = P.Vertices[i].OutgoingHalfedge; } } // dual halfedge start V is primal AdjacentFace // dual halfedge AdjacentFace is primal end V // dual nextHE is primal's pair's prev // dual prevHE is primal's next's pair // halfedge pairs stay the same for (int i = 0; i < P.Halfedges.Count; i++) { if ((P.Halfedges[i].AdjacentFace != -1) & (P.Halfedges[P.Halfedges.GetPairHalfedge(i)].AdjacentFace != -1)) { PlanktonHalfedge DualHE = PlanktonHalfedge.Unset; PlanktonHalfedge PrimalHE = P.Halfedges[i]; //DualHE.StartVertex = PrimalHE.AdjacentFace; DualHE.StartVertex = P.Halfedges[P.Halfedges.GetPairHalfedge(i)].AdjacentFace; if (P.Vertices.NakedEdgeCount(PrimalHE.StartVertex) == 0) { //DualHE.AdjacentFace = P.Halfedges[P.PairHalfedge(i)].StartVertex; DualHE.AdjacentFace = PrimalHE.StartVertex; } else { DualHE.AdjacentFace = -1; } //This will currently fail with open meshes... //one option could be to build the dual with all halfedges, but mark some as dead //if they connect to vertex -1 //mark the 'external' faces all as -1 (the ones that are dual to boundary verts) //then go through and if any next or prevs are dead hes then replace them with the next one around //this needs to be done repeatedly until no further change //DualHE.NextHalfedge = P.Halfedges[P.PairHalfedge(i)].PrevHalfedge; DualHE.NextHalfedge = P.Halfedges.GetPairHalfedge(PrimalHE.PrevHalfedge); //DualHE.PrevHalfedge = P.PairHalfedge(PrimalHE.NextHalfedge); DualHE.PrevHalfedge = P.Halfedges[P.Halfedges.GetPairHalfedge(i)].NextHalfedge; D.Halfedges.Add(DualHE); } } return(D); }
/// <summary> /// Creates a Plankton halfedge mesh from a Rhino mesh. /// Uses the topology of the Rhino mesh directly. /// </summary> /// <returns>A <see cref="PlanktonMesh"/> which represents the topology and geometry of the source mesh.</returns> /// <param name="source">A Rhino mesh to convert from.</param> public static PlanktonMesh ToPlanktonMesh(this Mesh source) { PlanktonMesh pMesh = new PlanktonMesh(); source.Vertices.CombineIdentical(true, true); source.Vertices.CullUnused(); source.UnifyNormals(); source.Weld(Math.PI); foreach (Point3f v in source.TopologyVertices) { pMesh.Vertices.Add(v.X, v.Y, v.Z); } for (int i = 0; i < source.Faces.Count; i++) { pMesh.Faces.Add(new PlanktonFace()); } for (int i = 0; i < source.TopologyEdges.Count; i++) { PlanktonHalfedge HalfA = new PlanktonHalfedge(); HalfA.StartVertex = source.TopologyEdges.GetTopologyVertices(i).I; if (pMesh.Vertices [HalfA.StartVertex].OutgoingHalfedge == -1) { pMesh.Vertices [HalfA.StartVertex].OutgoingHalfedge = pMesh.Halfedges.Count; } PlanktonHalfedge HalfB = new PlanktonHalfedge(); HalfB.StartVertex = source.TopologyEdges.GetTopologyVertices(i).J; if (pMesh.Vertices [HalfB.StartVertex].OutgoingHalfedge == -1) { pMesh.Vertices [HalfB.StartVertex].OutgoingHalfedge = pMesh.Halfedges.Count + 1; } bool[] Match; int[] ConnectedFaces = source.TopologyEdges.GetConnectedFaces(i, out Match); //Note for Steve Baer : This Match bool doesn't seem to work on triangulated meshes - it often returns true //for both faces, even for a properly oriented manifold mesh, which can't be right //So - making our own check for matching: //(I suspect the problem is related to C being the same as D for triangles, so best to //deal with them separately just to make sure) //loop through the vertices of the face until finding the one which is the same as the start of the edge //iff the next vertex around the face is the end of the edge then it matches. Match[0] = false; if (Match.Length > 1) {Match[1] = true;} int VertA = source.TopologyVertices.TopologyVertexIndex(source.Faces[ConnectedFaces[0]].A); int VertB = source.TopologyVertices.TopologyVertexIndex(source.Faces[ConnectedFaces[0]].B); int VertC = source.TopologyVertices.TopologyVertexIndex(source.Faces[ConnectedFaces[0]].C); int VertD = source.TopologyVertices.TopologyVertexIndex(source.Faces[ConnectedFaces[0]].D); if ((VertA == source.TopologyEdges.GetTopologyVertices(i).I) && (VertB == source.TopologyEdges.GetTopologyVertices(i).J)) { Match[0] = true; } if ((VertB == source.TopologyEdges.GetTopologyVertices(i).I) && (VertC == source.TopologyEdges.GetTopologyVertices(i).J)) { Match[0] = true; } if ((VertC == source.TopologyEdges.GetTopologyVertices(i).I) && (VertD == source.TopologyEdges.GetTopologyVertices(i).J)) { Match[0] = true; } if ((VertD == source.TopologyEdges.GetTopologyVertices(i).I) && (VertA == source.TopologyEdges.GetTopologyVertices(i).J)) { Match[0] = true; } //I don't think these next 2 should ever be needed, but just in case: if ((VertC == source.TopologyEdges.GetTopologyVertices(i).I) && (VertA == source.TopologyEdges.GetTopologyVertices(i).J)) { Match[0] = true; } if ((VertB == source.TopologyEdges.GetTopologyVertices(i).I) && (VertD == source.TopologyEdges.GetTopologyVertices(i).J)) { Match[0] = true; } if (Match[0] == true) { HalfA.AdjacentFace = ConnectedFaces[0]; if (pMesh.Faces[HalfA.AdjacentFace].FirstHalfedge == -1) { pMesh.Faces[HalfA.AdjacentFace].FirstHalfedge = pMesh.Halfedges.Count; } if (ConnectedFaces.Length > 1) { HalfB.AdjacentFace = ConnectedFaces[1]; if (pMesh.Faces[HalfB.AdjacentFace].FirstHalfedge == -1) { pMesh.Faces[HalfB.AdjacentFace].FirstHalfedge = pMesh.Halfedges.Count + 1; } } else { HalfB.AdjacentFace = -1; pMesh.Vertices[HalfB.StartVertex].OutgoingHalfedge = pMesh.Halfedges.Count + 1; } } else { HalfB.AdjacentFace = ConnectedFaces[0]; if (pMesh.Faces[HalfB.AdjacentFace].FirstHalfedge == -1) { pMesh.Faces[HalfB.AdjacentFace].FirstHalfedge = pMesh.Halfedges.Count + 1; } if (ConnectedFaces.Length > 1) { HalfA.AdjacentFace = ConnectedFaces[1]; if (pMesh.Faces[HalfA.AdjacentFace].FirstHalfedge == -1) { pMesh.Faces[HalfA.AdjacentFace].FirstHalfedge = pMesh.Halfedges.Count; } } else { HalfA.AdjacentFace = -1; pMesh.Vertices[HalfA.StartVertex].OutgoingHalfedge = pMesh.Halfedges.Count; } } pMesh.Halfedges.Add(HalfA); pMesh.Halfedges.Add(HalfB); } for (int i = 0; i < (pMesh.Halfedges.Count); i += 2) { int[] EndNeighbours = source.TopologyVertices.ConnectedTopologyVertices(pMesh.Halfedges[i + 1].StartVertex, true); for (int j = 0; j < EndNeighbours.Length; j++) { if(EndNeighbours[j] == pMesh.Halfedges[i].StartVertex) { int EndOfNextHalfedge = EndNeighbours[(j - 1 + EndNeighbours.Length) % EndNeighbours.Length]; int StartOfPrevOfPairHalfedge = EndNeighbours[(j + 1) % EndNeighbours.Length]; int NextEdge = source.TopologyEdges.GetEdgeIndex(pMesh.Halfedges[i + 1].StartVertex,EndOfNextHalfedge); int PrevPairEdge = source.TopologyEdges.GetEdgeIndex(pMesh.Halfedges[i + 1].StartVertex,StartOfPrevOfPairHalfedge); if (source.TopologyEdges.GetTopologyVertices(NextEdge).I == pMesh.Halfedges[i + 1].StartVertex) { pMesh.Halfedges[i].NextHalfedge = NextEdge * 2; } else { pMesh.Halfedges[i].NextHalfedge = NextEdge * 2 + 1; } if (source.TopologyEdges.GetTopologyVertices(PrevPairEdge).J == pMesh.Halfedges[i + 1].StartVertex) { pMesh.Halfedges[i + 1].PrevHalfedge = PrevPairEdge * 2; } else { pMesh.Halfedges[i + 1].PrevHalfedge = PrevPairEdge * 2+1; } break; } } int[] StartNeighbours = source.TopologyVertices.ConnectedTopologyVertices(pMesh.Halfedges[i].StartVertex, true); for (int j = 0; j < StartNeighbours.Length; j++) { if (StartNeighbours[j] == pMesh.Halfedges[i+1].StartVertex) { int EndOfNextOfPairHalfedge = StartNeighbours[(j - 1 + StartNeighbours.Length) % StartNeighbours.Length]; int StartOfPrevHalfedge = StartNeighbours[(j + 1) % StartNeighbours.Length]; int NextPairEdge = source.TopologyEdges.GetEdgeIndex(pMesh.Halfedges[i].StartVertex, EndOfNextOfPairHalfedge); int PrevEdge = source.TopologyEdges.GetEdgeIndex(pMesh.Halfedges[i].StartVertex, StartOfPrevHalfedge); if (source.TopologyEdges.GetTopologyVertices(NextPairEdge).I == pMesh.Halfedges[i].StartVertex) { pMesh.Halfedges[i + 1].NextHalfedge = NextPairEdge * 2; } else { pMesh.Halfedges[i + 1].NextHalfedge = NextPairEdge * 2 + 1; } if (source.TopologyEdges.GetTopologyVertices(PrevEdge).J == pMesh.Halfedges[i].StartVertex) { pMesh.Halfedges[i].PrevHalfedge = PrevEdge * 2; } else { pMesh.Halfedges[i].PrevHalfedge = PrevEdge * 2 + 1; } break; } } } return pMesh; }