public PlanktonMesh(PlanktonMesh source) { foreach (var v in source.Vertices) { this.Vertices.Add(new PlanktonVertex() { OutgoingHalfedge = v.OutgoingHalfedge, X = v.X, Y = v.Y, Z = v.Z }); } foreach (var f in source.Faces) { this.Faces.Add(new PlanktonFace() { FirstHalfedge = f.FirstHalfedge }); } foreach (var h in source.Halfedges) { this.Halfedges.Add(new PlanktonHalfedge() { StartVertex = h.StartVertex, AdjacentFace = h.AdjacentFace, NextHalfedge = h.NextHalfedge, PrevHalfedge = h.PrevHalfedge, }); } }
/// <summary> /// Creates a Rhino mesh from a Plankton halfedge mesh. /// Uses the face-vertex information available in the halfedge data structure. /// </summary> /// <returns>A <see cref="Mesh"/> which represents the source mesh (as best it can).</returns> /// <param name="source">A Plankton mesh to convert from.</param> /// <remarks>Any faces with five sides or more will be triangulated.</remarks> public static Mesh ToRhinoMesh(this PlanktonMesh source) { // could add different options for triangulating ngons later Mesh rMesh = new Mesh(); foreach (PlanktonVertex v in source.Vertices) { rMesh.Vertices.Add(v.X, v.Y, v.Z); } for (int i = 0; i < source.Faces.Count; i++) { int[] fvs = source.Faces.GetFaceVertices(i); if (fvs.Length == 3) { rMesh.Faces.AddFace(fvs[0], fvs[1], fvs[2]); } else if (fvs.Length == 4) { rMesh.Faces.AddFace(fvs[0], fvs[1], fvs[2], fvs[3]); } else if (fvs.Length > 4) { // triangulate about face center (fan) var fc = source.Faces.GetFaceCenter(i); rMesh.Vertices.Add(fc.X, fc.Y, fc.Z); for (int j = 0; j < fvs.Length; j++) { rMesh.Faces.AddFace(fvs[j], fvs[(j + 1) % fvs.Length], rMesh.Vertices.Count - 1); } } } rMesh.Normals.ComputeNormals(); return(rMesh); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object can be used to retrieve data from input parameters and /// to store data in output parameters.</param> protected override void SolveInstance(IGH_DataAccess DA) { PlanktonMesh P = new PlanktonMesh(); List<Point3d> Points = new List<Point3d>(); if ((!DA.GetData<PlanktonMesh>(0, ref P)) || (!DA.GetDataList(1, Points))) return; PlanktonMesh pMesh = P.ReplaceVertices(Points); DA.SetData(0, pMesh); }
/// <summary> /// Replaces the vertices of a PlanktonMesh with a new list of points /// </summary> /// <returns>A list of closed polylines representing the boundary edges of each face.</returns> /// <param name="source">A Plankton mesh.</param> /// <param name="points">A list of points.</param> public static PlanktonMesh ReplaceVertices(this PlanktonMesh source, List <Point3d> points) { PlanktonMesh pMesh = source; for (int i = 0; i < points.Count; i++) { pMesh.Vertices.SetVertex(i, points[i]); } return(pMesh); }
public static List<string> PrintVertices(PlanktonMesh mesh) { List<string> output = new List<string>(); for (int i = 0; i < mesh.Vertices.Count; i++) { string str = "Vertices[" + i.ToString() + "]="; str += mesh.Vertices[i].X.ToString() + "," + mesh.Vertices[i].Y.ToString() + "," + mesh.Vertices[i].Z.ToString(); output.Add(str); } return output; }
public static List<string> PrintHalfedges(PlanktonMesh mesh) { List<string> output = new List<string>(); output.Add("Format: StartVertex,AdjacentFace,NextHalfedge,PrevHalfedge"); for (int i = 0; i < mesh.Halfedges.Count; i++) { string str = "Halfedges[" + i.ToString() + "]="; str += mesh.Halfedges[i].StartVertex.ToString() + "," + mesh.Halfedges[i].AdjacentFace.ToString() + "," + mesh.Halfedges[i].NextHalfedge.ToString() + "," + mesh.Halfedges[i].PrevHalfedge.ToString(); output.Add(str); } return output; }
public static List<string> PrintFaces(PlanktonMesh mesh) { List<string> output = new List<string>(); for (int i = 0; i < mesh.Faces.Count; i++) { string str = "Faces[" + i.ToString() + "]="; int[] findex = mesh.Faces.GetFaceVertices(i); for (int j = 0; j < findex.Length; j++) { if (j > 0) str += ","; str += findex[j].ToString(); } output.Add(str); } return output; }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { PlanktonMesh P = new PlanktonMesh(); if (!DA.GetData<PlanktonMesh>(0, ref P)) return; List<Point3d> Positions = new List<Point3d>(); List<int> OutHEdge = new List<int>(); foreach (PlanktonVertex v in P.Vertices) { Positions.Add(new Point3f(v.X, v.Y, v.Z)); OutHEdge.Add(v.OutgoingHalfedge); } List<int> StartV = new List<int>(); List<int> AdjF = new List<int>(); List<int> Next = new List<int>(); List<int> Prev = new List<int>(); List<int> Pair = new List<int>(); for (int i = 0; i < P.Halfedges.Count; i++) { StartV.Add(P.Halfedges[i].StartVertex); AdjF.Add(P.Halfedges[i].AdjacentFace); Next.Add(P.Halfedges[i].NextHalfedge); Prev.Add(P.Halfedges[i].PrevHalfedge); Pair.Add(P.Halfedges.GetPairHalfedge(i)); } List<int> FaceEdge = new List<int>(); for (int i = 0; i < P.Faces.Count; i++) { FaceEdge.Add(P.Faces[i].FirstHalfedge); } DA.SetDataList(0, Positions); DA.SetDataList(1, OutHEdge); DA.SetDataList(2, StartV); DA.SetDataList(3, AdjF); DA.SetDataList(4, Next); DA.SetDataList(5, Prev); DA.SetDataList(6, Pair); DA.SetDataList(7, FaceEdge); }
/// <summary> /// Converts each face to a closed polyline. /// </summary> /// <returns>A list of closed polylines representing the boundary edges of each face.</returns> /// <param name="source">A Plankton mesh.</param> public static Polyline[] ToPolylines(this PlanktonMesh source) { int n = source.Faces.Count; Polyline[] polylines = new Polyline[n]; for (int i = 0; i < n; i++) { Polyline facePoly = new Polyline(); int[] vs = source.Faces.GetFaceVertices(i); for (int j = 0; j <= vs.Length; j++) { var v = source.Vertices[vs[j % vs.Length]]; facePoly.Add(v.X, v.Y, v.Z); } polylines[i] = facePoly; } return(polylines); }
public PlanktonMesh MeshFromPoints(List<PlanktonXYZ> pl, int u, int v) { if (u * v > pl.Count || u < 2 || v < 2) return null; PlanktonMesh mesh = new PlanktonMesh(); for (int i = 0; i < pl.Count; i++) { mesh.Vertices.Add(pl[i]); } for (int i = 1; i < u; i++) { for (int j = 1; j < v; j++) { mesh.Faces.AddFace( (j - 1) * u + i - 1, (j - 1) * u + i, (j) * u + i, (j) * u + i - 1); } } return mesh; }
/// <summary> /// Truncates the vertices of a mesh. /// </summary> /// <param name="t">Optional parameter for the normalised distance along each edge to control the amount of truncation.</param> /// <returns>A new mesh, the result of the truncation.</returns> public PlanktonMesh TruncateVertices(float t = 1f / 3) { // TODO: handle special cases (t = 0.0, t = 0.5, t > 0.5) var tMesh = new PlanktonMesh(this); var vxyz = tMesh.Vertices.Select(v => v.ToXYZ()).ToArray(); PlanktonXYZ v0, v1, v2; int[] oh; for (int i = 0; i < this.Vertices.Count; i++) { oh = this.Vertices.GetHalfedges(i); tMesh.Vertices.TruncateVertex(i); foreach (var h in oh) { v0 = vxyz[this.Halfedges[h].StartVertex]; v1 = vxyz[this.Halfedges.EndVertex(h)]; v2 = v0 + (v1 - v0) * t; tMesh.Vertices.SetVertex(tMesh.Halfedges[h].StartVertex, v2.X, v2.Y, v2.Z); } } return(tMesh); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { //------------------INPUT--------------------// PlanktonMesh pMesh = new PlanktonMesh(); DA.GetData(0, ref pMesh); bool projXY = false; DA.GetData(1, ref projXY); //------------------CALCULATE--------------------// PMeshExt pMeshE = new PMeshExt(pMesh); if (!pMeshE.isMeshTriangulated()) { this.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The mesh has to be triangulated"); } //Extract vertices from initial 3d pMesh Point3d[] verticesXYZ = pMeshE.convertVerticesToXYZ(); //If projected then create new pMesh if (projXY) { pMeshE = pMeshE.projectMeshToXY(); } Vector3d[] vertexAreas = pMeshE.calcVertexVoronoiAreas(projXY); //------------------OUTPUT--------------------// DA.SetDataList(0, verticesXYZ); DA.SetDataList(1, vertexAreas); }
public PlanktonMesh TriangleMeshFromPoints(List<PlanktonXYZ> pl, int t1, int t2) { PlanktonMesh mesh = new PlanktonMesh(); if (t1 < 1) return mesh; if (t2 <= t1) return mesh; int n = ((t1 + t2) * (t2 - t1 + 1)) / 2; if (n > pl.Count) return mesh; mesh.Vertices.AddVertices(pl); List<int> layer1; List<int> layer2 = new List<int>(); for (int i = 0; i < t1; i++) { layer2.Add(i); } for (int i = t1 - 1; i < t2; i++) { layer1 = new List<int>(layer2); for (int j = 0; j < layer2.Count; j++) { layer2[j] += i + 1; } layer2.Add(layer2[layer2.Count - 1] + 1); if (layer1.Count > 1) { for (int j = 0; j < layer1.Count - 1; j++) { mesh.Faces.AddFace(layer1[j], layer1[j + 1], layer2[j + 1]); } } for (int j = 0; j < layer1.Count; j++) { mesh.Faces.AddFace(layer2[j], layer1[j], layer2[j + 1]); } } return mesh; }
/// <summary> /// Truncates the vertices of a mesh. /// </summary> /// <param name="t">Optional parameter for the normalised distance along each edge to control the amount of truncation.</param> /// <returns>A new mesh, the result of the truncation.</returns> public PlanktonMesh TruncateVertices(float t = 1f/3) { // TODO: handle special cases (t = 0.0, t = 0.5, t > 0.5) var tMesh = new PlanktonMesh(this); var vxyz = tMesh.Vertices.Select(v => v.ToXYZ()).ToArray(); PlanktonXYZ v0, v1, v2; int[] oh; for (int i = 0; i < this.Vertices.Count; i++) { oh = this.Vertices.GetHalfedges(i); tMesh.Vertices.TruncateVertex(i); foreach (var h in oh) { v0 = vxyz[this.Halfedges[h].StartVertex]; v1 = vxyz[this.Halfedges.EndVertex(h)]; v2 = v0 + (v1 - v0) * t; tMesh.Vertices.SetVertex(tMesh.Halfedges[h].StartVertex, v2.X, v2.Y, v2.Z); } } return tMesh; }
public PlanktonMesh 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> /// Initializes a new instance of the <see cref="PlanktonHalfedgeList"/> class. /// Should be called from the mesh constructor. /// </summary> /// <param name="ownerMesh">The mesh to which this list of halfedges belongs.</param> internal PlanktonHalfEdgeList(PlanktonMesh owner) { this._list = new List <PlanktonHalfedge>(); this._mesh = owner; }
public PlanktonMesh MeshFromPoints(PlanktonXYZ p1, PlanktonXYZ p2, PlanktonXYZ p3) { PlanktonMesh mesh = new PlanktonMesh(); mesh.Vertices.Add(p1); mesh.Vertices.Add(p2); mesh.Vertices.Add(p3); mesh.Faces.AddFace(0, 1, 2); return mesh; }
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); }
private Point3d MidPt(PlanktonMesh P, int E) { Point3d Pos1 = P.Vertices[P.Halfedges[2 * E].StartVertex].ToPoint3d(); Point3d Pos2 = P.Vertices[P.Halfedges[2 * E + 1].StartVertex].ToPoint3d(); return (Pos1 + Pos2) * 0.5; }
private List<int> CompactByVertex(PlanktonMesh P, List<int> L) { List<int> L2 = new List<int>(); for (int i = 0; i < P.Vertices.Count; i++) { if (P.Vertices[i].IsUnused == false) { L2.Add(L[i]); } } return L2; }
private static Vector3d[] LaplacianSmooth(PlanktonMesh P, int W, double Strength) { int VertCount = P.Vertices.Count; Vector3d[] Smooth = new Vector3d[VertCount]; for (int i = 0; i < VertCount; i++) { if ((P.Vertices[i].IsUnused == false) && (P.Vertices.IsBoundary(i) == false)) { int[] Neighbours = P.Vertices.GetVertexNeighbours(i); Point3d Vertex = P.Vertices[i].ToPoint3d(); Point3d Centroid = new Point3d(); if (W == 0) { for (int j = 0; j < Neighbours.Length; j++) { Centroid = Centroid + P.Vertices[Neighbours[j]].ToPoint3d(); } Smooth[i] = ((Centroid * (1.0 / P.Vertices.GetValence(i))) - Vertex) * Strength; } if (W == 1) { //get the radial vectors of the 1-ring //get the vectors around the 1-ring //get the cotangent weights for each edge int valence = Neighbours.Length; Point3d[] NeighbourPts = new Point3d[valence]; Vector3d[] Radial = new Vector3d[valence]; Vector3d[] Around = new Vector3d[valence]; double[] CotWeight = new double[valence]; double WeightSum = 0; for (int j = 0; j < valence; j++) { NeighbourPts[j] = P.Vertices[Neighbours[j]].ToPoint3d(); Radial[j] = NeighbourPts[j] - Vertex; } for (int j = 0; j < valence; j++) { Around[j] = NeighbourPts[(j + 1) % valence] - NeighbourPts[j]; } for (int j = 0; j < Neighbours.Length; j++) { //get the cotangent weights int previous = (j + valence - 1) % valence; Vector3d Cross1 = Vector3d.CrossProduct(Radial[previous], Around[previous]); double Cross1Length = Cross1.Length; double Dot1 = Radial[previous] * Around[previous]; int next = (j + 1) % valence; Vector3d Cross2 = Vector3d.CrossProduct(Radial[next], Around[j]); double Cross2Length = Cross2.Length; double Dot2 = Radial[next] * Around[j]; CotWeight[j] = Math.Abs(Dot1 / Cross1Length) + Math.Abs(Dot2 / Cross2Length); WeightSum += CotWeight[j]; } double InvWeightSum = 1.0 / WeightSum; Vector3d ThisSmooth = new Vector3d(); for (int j = 0; j < Neighbours.Length; j++) { ThisSmooth = ThisSmooth + Radial[j] * CotWeight[j]; } Smooth[i] = ThisSmooth * InvWeightSum * Strength; } } } return Smooth; }
public PlanktonMesh TriangleMeshFromPoints(List<PlanktonXYZ> pl, int t) { //triangle MeshTopo Points From topo of the pyramid to the base PlanktonMesh mesh = new PlanktonMesh(); if (t < 2) return mesh; int n = ((1 + t) * t) / 2; if (n > pl.Count) return mesh; mesh.Vertices.AddVertices(pl); List<int> layer1; List<int> layer2 = new List<int>(); layer2.Add(0); for (int i = 0; i < t - 1; i++) { layer1 = new List<int>(layer2); for (int j = 0; j < layer2.Count; j++) { layer2[j] += i + 1; } layer2.Add(layer2[layer2.Count - 1] + 1); if (layer1.Count > 1) { for (int j = 0; j < layer1.Count - 1; j++) { mesh.Faces.AddFace(layer1[j], layer1[j + 1], layer2[j + 1]); } } for (int j = 0; j < layer1.Count; j++) { mesh.Faces.AddFace(layer2[j], layer1[j], layer2[j + 1]); } } return mesh; }
/// <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 CatmullClark(int iteration, List <int> fixVertices, List <int> fixEdges, List <int> fixFaces) { // Gene added catmull clark subdivision function, function written for Leopard. // maybe change this function to static, so can be easier. PlanktonMesh cMesh = new PlanktonMesh(this); // this iteration List <bool> isBoundaryVertices = new List <bool>(); // assign boundary vertices for (int i = 0; i < cMesh.Vertices.Count; i++) { if (cMesh.Vertices.IsBoundary(i)) { isBoundaryVertices.Add(true); } else { isBoundaryVertices.Add(false); } } // set vertices foreach (int v in fixVertices) { isBoundaryVertices[v] = true; } // set edges foreach (int e in fixEdges) { int id = e * 2; //int[] pEndsIndices = cMesh.Halfedges.GetVertices(edge); int pairIndex = cMesh.Halfedges.GetPairHalfedge(id); if (pairIndex < id) { int temp = id; id = pairIndex; pairIndex = temp; } int[] vts = cMesh.Halfedges.GetVertices(id); isBoundaryVertices[vts[0]] = true; isBoundaryVertices[vts[1]] = true; } // set face foreach (int f in fixFaces) { foreach (int v in cMesh.Faces.GetFaceVertices(f)) { isBoundaryVertices[v] = true; } } for (int iter = 0; iter < iteration; iter++) { PlanktonMesh subdMesh = new PlanktonMesh(); // current mesh for each iteration // add the original vertices for (int i = 0; i < cMesh.Vertices.Count; i++) { subdMesh.Vertices.Add(cMesh.Vertices[i].ToXYZ()); } // face centers #region face centers PlanktonXYZ[] faceCenter = new PlanktonXYZ[cMesh.Faces.Count]; for (int i = 0; i < cMesh.Faces.Count; i++) { faceCenter[i] = cMesh.Faces.GetFaceCenter(i); isBoundaryVertices.Add(false); subdMesh.Vertices.Add(cMesh.Faces.GetFaceCenter(i)); } #endregion // new edge points #region new edge points PlanktonXYZ[] newEdgePoints = new PlanktonXYZ[cMesh.Halfedges.Count / 2]; // pair half edges belong to one. for (int i = 0; i < cMesh.Halfedges.Count; i++) { if (i % 2 == 1) { continue; } int pairIndex = cMesh.Halfedges.GetPairHalfedge(i); int[] pEndsIndices = cMesh.Halfedges.GetVertices(i); int fA = cMesh.Halfedges[i].AdjacentFace; int fB = cMesh.Halfedges[pairIndex].AdjacentFace; PlanktonXYZ pStart, pEnd, fAC, fBC; // not boudary condition if (pEndsIndices[0] >= 0 && pEndsIndices[1] >= 0 && fA != -1 && fB != -1) { pStart = cMesh.Vertices[pEndsIndices[0]].ToXYZ(); pEnd = cMesh.Vertices[pEndsIndices[1]].ToXYZ(); fAC = faceCenter[fA]; fBC = faceCenter[fB]; if (isBoundaryVertices[pEndsIndices[0]] && isBoundaryVertices[pEndsIndices[1]]) { newEdgePoints[(int)i / 2] = (cMesh.Vertices[pEndsIndices[0]].ToXYZ() + cMesh.Vertices[pEndsIndices[1]].ToXYZ()) * 0.5f; isBoundaryVertices.Add(true); subdMesh.Vertices.Add(newEdgePoints[(int)i / 2]); } else if ((!isBoundaryVertices[pEndsIndices[0]] && isBoundaryVertices[pEndsIndices[1]]) || (isBoundaryVertices[pEndsIndices[0]] && !isBoundaryVertices[pEndsIndices[1]])) { newEdgePoints[(int)i / 2] = (cMesh.Vertices[pEndsIndices[0]].ToXYZ() + cMesh.Vertices[pEndsIndices[1]].ToXYZ()) * 0.5f * 0.5f + (pStart + pEnd + fAC + fBC) * 0.25f * 0.5f; isBoundaryVertices.Add(false); subdMesh.Vertices.Add(newEdgePoints[(int)i / 2]); } else { newEdgePoints[(int)i / 2] = (pStart + pEnd + fAC + fBC) * 0.25f; isBoundaryVertices.Add(false); subdMesh.Vertices.Add(newEdgePoints[(int)i / 2]); } } // boudary condition else { newEdgePoints[(int)i / 2] = (cMesh.Vertices[pEndsIndices[0]].ToXYZ() + cMesh.Vertices[pEndsIndices[1]].ToXYZ()) * 0.5f; isBoundaryVertices.Add(true); subdMesh.Vertices.Add(newEdgePoints[(int)i / 2]); } } #endregion // new vertex points #region new vertex points PlanktonXYZ[] newVertexPoints = new PlanktonXYZ[cMesh.Vertices.Count]; for (int i = 0; i < cMesh.Vertices.Count; i++) { int n = cMesh.Vertices.GetValence(i); // F int[] fIndices = cMesh.Vertices.GetVertexFaces(i); PlanktonXYZ fn = new PlanktonXYZ(); foreach (int f in fIndices) { if (f != -1) { fn += faceCenter[f]; } } fn *= (float)(1.0f / (n * n)); // E int[] vnIndices = cMesh.Vertices.GetVertexNeighbours(i); PlanktonXYZ vn = new PlanktonXYZ(); foreach (int e in vnIndices) { if (e != -1) { vn += cMesh.Vertices[e].ToXYZ(); } } vn *= (float)(1.0f / (n * n)); // V PlanktonXYZ v = cMesh.Vertices[i].ToXYZ() * (n - 2) * (1.0f / n); if (!isBoundaryVertices[i]) { newVertexPoints[i] = fn + vn + v; subdMesh.Vertices.SetVertex(i, newVertexPoints[i].X, newVertexPoints[i].Y, newVertexPoints[i].Z); } else { newVertexPoints[i] = cMesh.Vertices[i].ToXYZ(); subdMesh.Vertices.SetVertex(i, newVertexPoints[i].X, newVertexPoints[i].Y, newVertexPoints[i].Z); } } #endregion // add mesh face #region construct mesh for (int i = 0; i < cMesh.Faces.Count; i++) { int pMVC = cMesh.Vertices.Count; int fNewID = i + pMVC; int[] fVertices = cMesh.Faces.GetFaceVertices(i); int[] fEdgePts = cMesh.Faces.GetHalfedges(i); int fNum = cMesh.Faces.Count; int eNum = cMesh.Halfedges.Count / 2; if (fVertices.Length == 3) { subdMesh.Faces.AddFace(fNewID, fNum + fEdgePts[0] / 2 + pMVC, fVertices[1], fNum + fEdgePts[1] / 2 + pMVC); subdMesh.Faces.AddFace(fNewID, fNum + fEdgePts[1] / 2 + pMVC, fVertices[2], fNum + fEdgePts[2] / 2 + pMVC); subdMesh.Faces.AddFace(fNewID, fNum + fEdgePts[2] / 2 + pMVC, fVertices[0], fNum + fEdgePts[0] / 2 + pMVC); } else { subdMesh.Faces.AddFace(fNewID, fNum + fEdgePts[0] / 2 + pMVC, fVertices[1], fNum + fEdgePts[1] / 2 + pMVC); subdMesh.Faces.AddFace(fNewID, fNum + fEdgePts[1] / 2 + pMVC, fVertices[2], fNum + fEdgePts[2] / 2 + pMVC); subdMesh.Faces.AddFace(fNewID, fNum + fEdgePts[2] / 2 + pMVC, fVertices[3], fNum + fEdgePts[3] / 2 + pMVC); subdMesh.Faces.AddFace(fNewID, fNum + fEdgePts[3] / 2 + pMVC, fVertices[0], fNum + fEdgePts[0] / 2 + pMVC); } } #endregion cMesh = subdMesh; } return(cMesh); }
/// <summary> /// Initializes a new instance of the <see cref="PlanktonVertexList"/> class. /// Should be called from the mesh constructor. /// </summary> /// <param name="ownerMesh">The mesh to which this list of vertices belongs.</param> internal PlanktonVertexList(PlanktonMesh owner) { this._list = new List <PlanktonVertex>(); this._mesh = owner; }
public PMeshExt(PlanktonMesh source): base(source) { }
/// <summary> /// Clone the data structure without trigging a costly rebuild operation and garbage collection. /// </summary> public void CopyInto(PlanktonMesh clone) { this.Vertices.CopyInto(clone._vertices); this.Halfedges.CopyInto(clone._halfedges); this.Faces.CopyInto(clone._faces); }
protected override void SolveInstance(IGH_DataAccess DA) { PlanktonMesh P1 = null; if (!DA.GetData(0, ref P1)) return; List<double> RL = new List<double>() ; if (!DA.GetDataList(1, RL)) { return; } bool D = false; if (!DA.GetData(2, ref D)) { return; } if (D) { P1 = P1.Dual(); } PlanktonMesh P2 = new PlanktonMesh(); int vcount = P1.Vertices.Count; List<Vector3d> Normals = new List<Vector3d>(); List<int> Outer = new List<int>(); List<int> Inner = new List<int>(); List<int> Elbow = new List<int>(); for (int i = 0; i < vcount; i++) { Point3d Vertex = P1.Vertices[i].ToPoint3d(); Vector3d Normal = new Vector3d(); double AvgAngle = 0; double R = 0; if (RL.Count == 1) { R = RL[0]; } else { R = RL[i]; } int[] OutEdges = P1.Vertices.GetHalfedges(i); int[] Neighbours = P1.Vertices.GetVertexNeighbours(i); Vector3d[] OutVectors = new Vector3d[Neighbours.Length]; int Valence = P1.Vertices.GetValence(i); for (int j = 0; j < Valence; j++) { Vector3d OutVector = P1.Vertices[Neighbours[j]].ToPoint3d() - Vertex; OutVector.Unitize(); OutVectors[j] = OutVector; } for (int j = 0; j < Valence; j++) { if (P1.Halfedges[OutEdges[(j + 1) % Valence]].AdjacentFace != -1) {Normal += (Vector3d.CrossProduct(OutVectors[(j + 1) % Valence], OutVectors[j]));} } Normal.Unitize(); Normals.Add(Normal); for (int j = 0; j < Valence; j++) { AvgAngle += Vector3d.VectorAngle(Normal, OutVectors[j]); } AvgAngle = AvgAngle * (1.0 / Valence); double Offset = R / (Math.Sin(AvgAngle)); Outer.Add(P2.Vertices.Add(Vertex + (Normal * Offset))); //this adds the actual point to the mesh, as well as its index to Outer Inner.Add(P2.Vertices.Add(Vertex - (Normal * Offset))); } for (int i = 0; i < P1.Halfedges.Count; i++) { //get the 3 points of the angle int Prev = P1.Halfedges[i].PrevHalfedge; int Next = P1.Halfedges[i].NextHalfedge; int PrevV = P1.Halfedges[Prev].StartVertex; int NextV = P1.Halfedges[Next].StartVertex; int ThisV = P1.Halfedges[i].StartVertex; double R = 0; if (RL.Count == 1) { R = RL[0]; } else { R = RL[ThisV]; } Point3d PrevPt = P1.Vertices[PrevV].ToPoint3d(); Point3d NextPt = P1.Vertices[NextV].ToPoint3d(); Point3d ThisPt = P1.Vertices[ThisV].ToPoint3d(); //construct the point at the inside of the 'elbow' Vector3d Arm1 = PrevPt - ThisPt; Vector3d Arm2 = NextPt - ThisPt; Arm1.Unitize(); Arm2.Unitize(); double alpha = Vector3d.VectorAngle(Arm1, Arm2); Point3d ThisElbow; Vector3d Bisect = new Vector3d(); if (P1.Halfedges[i].AdjacentFace == -1) {Bisect = Vector3d.CrossProduct(Normals[ThisV], -1.0 * Arm1) + Vector3d.CrossProduct(Normals[ThisV], Arm2);} else {Bisect = Arm1 + Arm2;} Bisect.Unitize(); ThisElbow = ThisPt + Bisect * (R / Math.Sin(alpha * 0.5)); Elbow.Add(P2.Vertices.Add(ThisElbow)); } for (int i = 0; i < P1.Halfedges.Count; i++) { int Next = P1.Halfedges[i].NextHalfedge; int NextV = P1.Halfedges[Next].StartVertex; int ThisV = P1.Halfedges[i].StartVertex; P2.Faces.AddFace(Outer[ThisV], Outer[NextV], Elbow[Next], Elbow[i]); P2.Faces.AddFace(Elbow[i], Elbow[Next], Inner[NextV], Inner[ThisV]); } Mesh OutputMesh = P2.ToRhinoMesh(); DA.SetData(0, OutputMesh); }
protected override void SolveInstance(IGH_DataAccess DA) { ITargetLength TargetLength = null; bool reset = false; int Flip = 0; List<Curve> FC = new List<Curve>(); List<Point3d> FV = new List<Point3d>(); double FixT = 0.01; double PullStrength = 0.8; double SmoothStrength = 0.8; double LengthTol = 0.15; bool Minim = false; int Iters = 1; GH_ObjectWrapper Surf = new GH_ObjectWrapper(); DA.GetData<GH_ObjectWrapper>(0, ref Surf); GH_ObjectWrapper Obj = null; DA.GetData<GH_ObjectWrapper>(1, ref Obj); TargetLength = Obj.Value as ITargetLength; DA.GetDataList<Curve>(2, FC); DA.GetDataList<Point3d>(3, FV); DA.GetData<int>(4, ref Flip); DA.GetData<double>(5, ref PullStrength); DA.GetData<int>(6, ref Iters); DA.GetData<bool>(7, ref reset); if (PullStrength == 0) { Minim = true; } if (Surf.Value is GH_Mesh) { DA.GetData<Mesh>(0, ref M); M.Faces.ConvertQuadsToTriangles(); } else { double L = 1.0; MeshingParameters MeshParams = new MeshingParameters(); MeshParams.MaximumEdgeLength = 3 * L; MeshParams.MinimumEdgeLength = L; MeshParams.JaggedSeams = false; MeshParams.SimplePlanes = false; Brep SB = null; DA.GetData<Brep>(0, ref SB); Mesh[] BrepMeshes = Mesh.CreateFromBrep(SB, MeshParams); M = new Mesh(); foreach (var mesh in BrepMeshes) M.Append(mesh); } if (reset || initialized == false) { #region reset M.Faces.ConvertQuadsToTriangles(); P = M.ToPlanktonMesh(); initialized = true; AnchorV.Clear(); FeatureV.Clear(); FeatureE.Clear(); //Mark any vertices or edges lying on features for (int i = 0; i < P.Vertices.Count; i++) { Point3d Pt = P.Vertices[i].ToPoint3d(); AnchorV.Add(-1); for (int j = 0; j < FV.Count; j++) { if (Pt.DistanceTo(FV[j]) < FixT) { AnchorV[AnchorV.Count - 1] = j; } } FeatureV.Add(-1); for (int j = 0; j < FC.Count; j++) { double param = new double(); FC[j].ClosestPoint(Pt, out param); if (Pt.DistanceTo(FC[j].PointAt(param)) < FixT) { FeatureV[FeatureV.Count - 1] = j; } } } int EdgeCount = P.Halfedges.Count / 2; for (int i = 0; i < EdgeCount; i++) { FeatureE.Add(-1); int vStart = P.Halfedges[2 * i].StartVertex; int vEnd = P.Halfedges[2 * i + 1].StartVertex; Point3d PStart = P.Vertices[vStart].ToPoint3d(); Point3d PEnd = P.Vertices[vEnd].ToPoint3d(); for (int j = 0; j < FC.Count; j++) { double paramS = new double(); double paramE = new double(); Curve thisFC = FC[j]; thisFC.ClosestPoint(PStart, out paramS); thisFC.ClosestPoint(PEnd, out paramE); if ((PStart.DistanceTo(thisFC.PointAt(paramS)) < FixT) && (PEnd.DistanceTo(thisFC.PointAt(paramE)) < FixT)) { FeatureE[FeatureE.Count - 1] = j; } } } #endregion } else { for (int iter = 0; iter < Iters; iter++) { int EdgeCount = P.Halfedges.Count / 2; double[] EdgeLength = P.Halfedges.GetLengths(); List<bool> Visited = new List<bool>(); Vector3d[] Normals = new Vector3d[P.Vertices.Count]; for (int i = 0; i < P.Vertices.Count; i++) { Visited.Add(false); Normals[i] = Normal(P, i); } double t = LengthTol; //a tolerance for when to split/collapse edges double smooth = SmoothStrength; //smoothing strength double pull = PullStrength; //pull to target mesh strength // Split the edges that are too long for (int i = 0; i < EdgeCount; i++) { if (P.Halfedges[2 * i].IsUnused == false) { int vStart = P.Halfedges[2 * i].StartVertex; int vEnd = P.Halfedges[2 * i + 1].StartVertex; if ((Visited[vStart] == false) && (Visited[vEnd] == false)) { double L2 = TargetLength.Calculate(P, 2 * i); if (EdgeLength[2 * i] > (1 + t) * (4f / 3f) * L2) { int SplitHEdge = P.Halfedges.TriangleSplitEdge(2 * i); if (SplitHEdge != -1) { int SplitCenter = P.Halfedges[SplitHEdge].StartVertex; P.Vertices.SetVertex(SplitCenter, MidPt(P, i)); //update the feature information FeatureE.Add(FeatureE[i]); FeatureV.Add(FeatureE[i]); AnchorV.Add(-1); //2 additional new edges have also been created (or 1 if split was on a boundary) //mark these as non-features int CEdgeCount = P.Halfedges.Count / 2; while (FeatureE.Count < CEdgeCount) { FeatureE.Add(-1); } Visited.Add(true); int[] Neighbours = P.Vertices.GetVertexNeighbours(SplitCenter); foreach (int n in Neighbours) { Visited[n] = true; } } } } } } //Collapse the edges that are too short for (int i = 0; i < EdgeCount; i++) { if (P.Halfedges[2 * i].IsUnused == false) { int vStart = P.Halfedges[2 * i].StartVertex; int vEnd = P.Halfedges[2 * i + 1].StartVertex; if ((Visited[vStart] == false) && (Visited[vEnd] == false)) { if (!(AnchorV[vStart] != -1 && AnchorV[vEnd] != -1)) // if both ends are anchored, don't collapse { int Collapse_option = 0; //0 for none, 1 for collapse to midpt, 2 for towards start, 3 for towards end //if neither are anchorV if (AnchorV[vStart] == -1 && AnchorV[vEnd] == -1) { // if both on same feature (or neither on a feature) if (FeatureV[vStart] == FeatureV[vEnd]) { Collapse_option = 1; } // if start is on a feature and end isn't if ((FeatureV[vStart] != -1) && (FeatureV[vEnd] == -1)) { Collapse_option = 2; } // if end is on a feature and start isn't if ((FeatureV[vStart] == -1) && (FeatureV[vEnd] != -1)) { Collapse_option = 3; } } else // so one end must be an anchor { // if start is an anchor if (AnchorV[vStart] != -1) { // if both are on same feature, or if the end is not a feature if ((FeatureE[i] != -1) || (FeatureV[vEnd] == -1)) { Collapse_option = 2; } } // if end is an anchor if (AnchorV[vEnd] != -1) { // if both are on same feature, or if the start is not a feature if ((FeatureE[i] != -1) || (FeatureV[vStart] == -1)) { Collapse_option = 3; } } } Point3d Mid = MidPt(P, i); double L2 = TargetLength.Calculate(P, 2 * i); if ((Collapse_option != 0) && (EdgeLength[2 * i] < (1 - t) * 4f / 5f * L2)) { int Collapsed = -1; int CollapseRtn = -1; if (Collapse_option == 1) { Collapsed = P.Halfedges[2 * i].StartVertex; P.Vertices.SetVertex(Collapsed, MidPt(P, i)); CollapseRtn = P.Halfedges.CollapseEdge(2 * i); } if (Collapse_option == 2) { Collapsed = P.Halfedges[2 * i].StartVertex; CollapseRtn = P.Halfedges.CollapseEdge(2 * i); } if (Collapse_option == 3) { Collapsed = P.Halfedges[2 * i + 1].StartVertex; CollapseRtn = P.Halfedges.CollapseEdge(2 * i + 1); } if (CollapseRtn != -1) { int[] Neighbours = P.Vertices.GetVertexNeighbours(Collapsed); foreach (int n in Neighbours) { Visited[n] = true; } } } } } } } EdgeCount = P.Halfedges.Count / 2; if ((Flip == 0) && (PullStrength > 0)) { //Flip edges to reduce valence error for (int i = 0; i < EdgeCount; i++) { if (!P.Halfedges[2 * i].IsUnused && (P.Halfedges[2 * i].AdjacentFace != -1) && (P.Halfedges[2 * i + 1].AdjacentFace != -1) && (FeatureE[i] == -1) // don't flip feature edges ) { int Vert1 = P.Halfedges[2 * i].StartVertex; int Vert2 = P.Halfedges[2 * i + 1].StartVertex; int Vert3 = P.Halfedges[P.Halfedges[P.Halfedges[2 * i].NextHalfedge].NextHalfedge].StartVertex; int Vert4 = P.Halfedges[P.Halfedges[P.Halfedges[2 * i + 1].NextHalfedge].NextHalfedge].StartVertex; int Valence1 = P.Vertices.GetValence(Vert1); int Valence2 = P.Vertices.GetValence(Vert2); int Valence3 = P.Vertices.GetValence(Vert3); int Valence4 = P.Vertices.GetValence(Vert4); if (P.Vertices.NakedEdgeCount(Vert1) > 0) { Valence1 += 2; } if (P.Vertices.NakedEdgeCount(Vert2) > 0) { Valence2 += 2; } if (P.Vertices.NakedEdgeCount(Vert3) > 0) { Valence3 += 2; } if (P.Vertices.NakedEdgeCount(Vert4) > 0) { Valence4 += 2; } int CurrentError = Math.Abs(Valence1 - 6) + Math.Abs(Valence2 - 6) + Math.Abs(Valence3 - 6) + Math.Abs(Valence4 - 6); int FlippedError = Math.Abs(Valence1 - 7) + Math.Abs(Valence2 - 7) + Math.Abs(Valence3 - 5) + Math.Abs(Valence4 - 5); if (CurrentError > FlippedError) { P.Halfedges.FlipEdge(2 * i); } } } } else { //Flip edges based on angle for (int i = 0; i < EdgeCount; i++) { if (!P.Halfedges[2 * i].IsUnused && (P.Halfedges[2 * i].AdjacentFace != -1) && (P.Halfedges[2 * i + 1].AdjacentFace != -1) && (FeatureE[i] == -1) // don't flip feature edges ) { int Vert1 = P.Halfedges[2 * i].StartVertex; int Vert2 = P.Halfedges[2 * i + 1].StartVertex; int Vert3 = P.Halfedges[P.Halfedges[P.Halfedges[2 * i].NextHalfedge].NextHalfedge].StartVertex; int Vert4 = P.Halfedges[P.Halfedges[P.Halfedges[2 * i + 1].NextHalfedge].NextHalfedge].StartVertex; Point3d P1 = P.Vertices[Vert1].ToPoint3d(); Point3d P2 = P.Vertices[Vert2].ToPoint3d(); Point3d P3 = P.Vertices[Vert3].ToPoint3d(); Point3d P4 = P.Vertices[Vert4].ToPoint3d(); double A1 = Vector3d.VectorAngle(new Vector3d(P3 - P1), new Vector3d(P4 - P1)) + Vector3d.VectorAngle(new Vector3d(P4 - P2), new Vector3d(P3 - P2)); double A2 = Vector3d.VectorAngle(new Vector3d(P1 - P4), new Vector3d(P2 - P4)) + Vector3d.VectorAngle(new Vector3d(P2 - P3), new Vector3d(P1 - P3)); if (A2 > A1) { P.Halfedges.FlipEdge(2 * i); } } } } if (Minim) { Vector3d[] SmoothC = LaplacianSmooth(P, 1, smooth); for (int i = 0; i < P.Vertices.Count; i++) { if (AnchorV[i] == -1) // don't smooth feature vertices { P.Vertices.MoveVertex(i, 0.5 * SmoothC[i]); } } } Vector3d[] Smooth = LaplacianSmooth(P, 0, smooth); for (int i = 0; i < P.Vertices.Count; i++) { if (AnchorV[i] == -1) // don't smooth feature vertices { // make it tangential only Vector3d VNormal = Normal(P, i); double ProjLength = Smooth[i] * VNormal; Smooth[i] = Smooth[i] - (VNormal * ProjLength); P.Vertices.MoveVertex(i, Smooth[i]); if (P.Vertices.NakedEdgeCount(i) != 0)//special smoothing for feature edges { int[] Neighbours = P.Vertices.GetVertexNeighbours(i); int ncount = 0; Point3d Avg = new Point3d(); for (int j = 0; j < Neighbours.Length; j++) { if (P.Vertices.NakedEdgeCount(Neighbours[j]) != 0) { ncount++; Avg = Avg + P.Vertices[Neighbours[j]].ToPoint3d(); } } Avg = Avg * (1.0 / ncount); Vector3d move = Avg - P.Vertices[i].ToPoint3d(); move = move * smooth; P.Vertices.MoveVertex(i, move); } if (FeatureV[i] != -1)//special smoothing for feature edges { int[] Neighbours = P.Vertices.GetVertexNeighbours(i); int ncount = 0; Point3d Avg = new Point3d(); for (int j = 0; j < Neighbours.Length; j++) { if ((FeatureV[Neighbours[j]] == FeatureV[i]) || (AnchorV[Neighbours[j]] != -1)) { ncount++; Avg = Avg + P.Vertices[Neighbours[j]].ToPoint3d(); } } Avg = Avg * (1.0 / ncount); Vector3d move = Avg - P.Vertices[i].ToPoint3d(); move = move * smooth; P.Vertices.MoveVertex(i, move); } //projecting points onto the target along their normals if (pull > 0) { Point3d Point = P.Vertices[i].ToPoint3d(); Vector3d normal = Normal(P, i); Ray3d Ray1 = new Ray3d(Point, normal); Ray3d Ray2 = new Ray3d(Point, -normal); double RayPt1 = Rhino.Geometry.Intersect.Intersection.MeshRay(M, Ray1); double RayPt2 = Rhino.Geometry.Intersect.Intersection.MeshRay(M, Ray2); Point3d ProjectedPt; if ((RayPt1 < RayPt2) && (RayPt1 > 0) && (RayPt1 < 1.0)) { ProjectedPt = Point * (1 - pull) + pull * Ray1.PointAt(RayPt1); } else if ((RayPt2 < RayPt1) && (RayPt2 > 0) && (RayPt2 < 1.0)) { ProjectedPt = Point * (1 - pull) + pull * Ray2.PointAt(RayPt2); } else { ProjectedPt = Point * (1 - pull) + pull * M.ClosestPoint(Point); } P.Vertices.SetVertex(i, ProjectedPt); } if (FeatureV[i] != -1) //pull feature vertices onto feature curves { Point3d Point = P.Vertices[i].ToPoint3d(); Curve CF = FC[FeatureV[i]]; double param1 = 0.0; Point3d onFeature = new Point3d(); CF.ClosestPoint(Point, out param1); onFeature = CF.PointAt(param1); P.Vertices.SetVertex(i, onFeature); } } else { P.Vertices.SetVertex(i, FV[AnchorV[i]]); //pull anchor vertices onto their points } } //end new AnchorV = CompactByVertex(P, AnchorV); //compact the fixed points along with the vertices FeatureV = CompactByVertex(P, FeatureV); FeatureE = CompactByEdge(P, FeatureE); P.Compact(); //this cleans the mesh data structure of unused elements } } DA.SetData(0, P); }
/// <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; }
private List<int> CompactByEdge(PlanktonMesh P, List<int> L1) { List<int> L2 = new List<int>(); int EdgeCount = P.Halfedges.Count / 2; for (int i = 0; i < EdgeCount; i++) { if (P.Halfedges[2 * i].IsUnused == false) { L2.Add(L1[i]); } } return L2; }
/// <summary> /// Gets positions of vertices /// </summary> /// <returns>A list of Point3d</returns> /// <param name="source">A Plankton mesh.</param> public static IEnumerable <Point3d> GetPositions(this PlanktonMesh source) { return(Enumerable.Range(0, source.Vertices.Count).Select(i => source.Vertices[i].ToPoint3d())); }
private double CreaseAngle(PlanktonMesh P, int HE) { if (P.Halfedges[HE].IsUnused) { return -1; } else { int Pair = P.Halfedges.GetPairHalfedge(HE); if (P.Halfedges[HE].AdjacentFace == -1 || P.Halfedges[Pair].AdjacentFace == -1) { return 0; } else { int Vert1 = P.Halfedges[HE].StartVertex; int Vert2 = P.Halfedges[Pair].StartVertex; int Vert3 = P.Halfedges[P.Halfedges[P.Halfedges[HE].NextHalfedge].NextHalfedge].StartVertex; int Vert4 = P.Halfedges[P.Halfedges[P.Halfedges[Pair].NextHalfedge].NextHalfedge].StartVertex; Point3d P1 = P.Vertices[Vert1].ToPoint3d(); Point3d P2 = P.Vertices[Vert2].ToPoint3d(); Point3d P3 = P.Vertices[Vert3].ToPoint3d(); Point3d P4 = P.Vertices[Vert4].ToPoint3d(); Vector3d ThisEdge = P2 - P1; Vector3d Edge1 = P3 - P1; Vector3d Edge2 = P4 - P1; Vector3d Normal1 = Vector3d.CrossProduct(ThisEdge, Edge1); Vector3d Normal2 = Vector3d.CrossProduct(Edge2, ThisEdge); return (Vector3d.VectorAngle(Normal1, Normal2)); } } }
/// <summary> /// Initializes a new instance of the <see cref="PlanktonFaceList"/> class. /// Should be called from the mesh constructor. /// </summary> /// <param name="owner">The mesh to which this list of half-edges belongs.</param> internal PlanktonFaceList(PlanktonMesh owner) { this._list = new List <PlanktonFace>(); this._mesh = owner; }
private Vector3d Normal(PlanktonMesh P, int V) { Point3d Vertex = P.Vertices[V].ToPoint3d(); Vector3d Norm = new Vector3d(); int[] OutEdges = P.Vertices.GetHalfedges(V); int[] Neighbours = P.Vertices.GetVertexNeighbours(V); Vector3d[] OutVectors = new Vector3d[Neighbours.Length]; int Valence = P.Vertices.GetValence(V); for (int j = 0; j < Valence; j++) { OutVectors[j] = P.Vertices[Neighbours[j]].ToPoint3d() - Vertex; } for (int j = 0; j < Valence; j++) { if (P.Halfedges[OutEdges[(j + 1) % Valence]].AdjacentFace != -1) { Norm += (Vector3d.CrossProduct(OutVectors[(j + 1) % Valence], OutVectors[j])); } } Norm.Unitize(); return Norm; }
public PolyMesh() { geometry = new PlanktonMesh(); }