public Mesh GetRhinoMesh() { return(ptMesh.ToRhinoMesh()); }
/// <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) { //Global variables PlanktonMesh pMesh = new PlanktonMesh(); DA.GetData <PlanktonMesh>(0, ref pMesh); Matrix mV = null; DA.GetData(1, ref mV); int vCount = mV.ColumnCount; List <Vector3d> displDirections = new List <Vector3d>(); DA.GetDataList(2, displDirections); List <double> weights = new List <double>(); DA.GetDataList(3, weights); if (weights.Count != vCount) { weights = new List <double>(); for (int i = 0; i < vCount; i++) { weights.Add(1.0); } this.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, String.Format("The number of weights must equal {0}. Uniform weights are set as default", vCount)); } double scale = 1.0; DA.GetData(4, ref scale); //---------------------------------------------------------------------------------------- //Calculate nodal values from linear combination of eigenvectors double[] nodalValues = nodalLinearCombination(weights, mV); //Map nodal values to displacement vectors Vector3d[] displacements = mapToDisplacements(nodalValues, displDirections, scale); //Map nodal values to mesh vertex colours Color[] vertexColours = mapToColour(nodalValues); //New mesh from displacements PlanktonMesh pMeshNew = new PlanktonMesh(pMesh); for (int i = 0; i < pMeshNew.Vertices.Count; i++) { pMeshNew.Vertices[i].X += (float)displacements[i].X; pMeshNew.Vertices[i].Y += (float)displacements[i].Y; pMeshNew.Vertices[i].Z += (float)displacements[i].Z; } //Convert to Rhino Mesh and add vertex colouring if the same number of vertices exist Mesh rhinoMesh = pMeshNew.ToRhinoMesh(); if (rhinoMesh.Vertices.Count == pMesh.Vertices.Count) { rhinoMesh.VertexColors.CreateMonotoneMesh(Color.White); for (int j = 0; j < rhinoMesh.VertexColors.Count; j++) { rhinoMesh.VertexColors[j] = vertexColours[j]; } } else { this.AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Mesh colouring failed due to a different number of vertices in the Rhino Mesh and PlanktonMesh. This happens if n-gons with n>4 exist"); } //---------------------------------------------------------------------------------------- //Output DA.SetData(0, rhinoMesh); }
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 Result RunCommand(RhinoDoc doc, RunMode mode) { // TODO: start here modifying the behaviour of your command. // --- RhinoApp.WriteLine("MeshMachine WIP test", EnglishName); Brep SB; using (GetObject getBrep = new GetObject()) { getBrep.SetCommandPrompt("Please select the brep to remesh"); getBrep.Get(); SB = getBrep.Object(0).Brep(); } //GetNumber TargetLength = new GetNumber(); //double L = TargetLength.Number(); Rhino.Input.Custom.GetNumber gn = new Rhino.Input.Custom.GetNumber(); gn.SetCommandPrompt("Specify a Target Edge Length"); gn.SetLowerLimit(0.5, false); gn.Get(); if (gn.CommandResult() != Rhino.Commands.Result.Success) { return(gn.CommandResult()); } double L = gn.Number(); //Point3d pt0; //using (GetPoint getPointAction = new GetPoint()) //{ // getPointAction.SetCommandPrompt("Please select the start point"); // if (getPointAction.Get() != GetResult.Point) // { // RhinoApp.WriteLine("No start point was selected."); // return getPointAction.CommandResult(); // } // pt0 = getPointAction.Point(); //} //Point3d pt1; //using (GetPoint getPointAction = new GetPoint()) //{ // getPointAction.SetCommandPrompt("Please select the end point"); // getPointAction.SetBasePoint(pt0, true); // getPointAction.DynamicDraw += // (sender, e) => e.Display.DrawLine(pt0, e.CurrentPoint, System.Drawing.Color.DarkRed); // if (getPointAction.Get() != GetResult.Point) // { // RhinoApp.WriteLine("No end point was selected."); // return getPointAction.CommandResult(); // } // pt1 = getPointAction.Point(); //} //doc.Objects.AddLine(pt0, pt1); PlanktonMesh P = new PlanktonMesh(); List <int> AnchorV = new List <int>(); List <int> FeatureV = new List <int>(); List <int> FeatureE = new List <int>(); double FixT = 0.00001; double LengthTol = 0.15; //a tolerance for when to split/collapse edges double SmoothStrength = 0.8; //smoothing strength double PullStrength = 0.8; //pull to target mesh strength double CurvDep = 0; int Flip = 1; MeshingParameters MeshParams = new MeshingParameters(); MeshParams.MaximumEdgeLength = 3 * L; MeshParams.MinimumEdgeLength = L; MeshParams.JaggedSeams = false; MeshParams.SimplePlanes = false; Mesh[] BrepMeshes = Mesh.CreateFromBrep(SB, MeshParams); Mesh M = new Mesh(); foreach (var mesh in BrepMeshes) { M.Append(mesh); } M.Faces.ConvertQuadsToTriangles(); P = M.ToPlanktonMesh(); var FC = new List <Curve>(); foreach (BrepEdge E in SB.Edges) { if (!E.IsSmoothManifoldEdge(0.01)) { FC.Add(E.ToNurbsCurve()); } } var Corners = SB.Vertices; List <Point3d> FV = new List <Point3d>(); foreach (Point Pt in Corners) { FV.Add(Pt.Location); } //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; } } } for (int iter = 0; iter < 30; iter++) { 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] = Util.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 = L; Point3d Mid = Util.MidPt(P, i); //if (CurvDep > 0) //{ // double NormDiff = Vector3d.VectorAngle(Normals[vStart], Normals[vEnd]); // L2 = Math.Min((1.0 / (3.0 * NormDiff) * L), 5 * L); // if (CurvDep != 1) // { // L2 = L2 * (CurvDep) + L * (1.0 - CurvDep); // } //} //if (BoundScale != 1.0) //{ // double MinDist = 99954; // for (int j = 0; j < FC.Count; j++) // { // double param = new double(); // FC[j].ClosestPoint(Mid, out param); // double ThisDist = Mid.DistanceTo(FC[j].PointAt(param)); // if (ThisDist < MinDist) // { MinDist = ThisDist; } // } // if (MinDist < BoundDist) // { // L2 = L2 * BoundScale + (MinDist / BoundDist) * (L2 * (1 - BoundScale)); // } //} //if (SizP.Count > 0) //{ // L2 = WeightedCombo(Mid, SizP, SizV, WExp, L2, BGW); // // L2 = (WL * (1.0 - BGW)) + (BGW * L2); //} 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, Util.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; } } } double L2 = L; Point3d Mid = Util.MidPt(P, i); if (CurvDep > 0) { double NormDiff = Vector3d.VectorAngle(Normals[vStart], Normals[vEnd]); L2 = Math.Min((1.0 / (3.0 * NormDiff) * L), 5 * L); if (CurvDep != 1) { L2 = L2 * (CurvDep) + L * (1.0 - CurvDep); } } //if (BoundScale != 1.0) //{ // double MinDist = 99954; // for (int j = 0; j < FC.Count; j++) // { // double param = new double(); // FC[j].ClosestPoint(Mid, out param); // double ThisDist = Mid.DistanceTo(FC[j].PointAt(param)); // if (ThisDist < MinDist) // { MinDist = ThisDist; } // } // if (MinDist < BoundDist) // { // L2 = L2 * BoundScale + (MinDist / BoundDist) * (L2 * (1 - BoundScale)); // } //} //if (SizP.Count > 0) //{ // L2 = WeightedCombo(Mid, SizP, SizV, WExp, L2, BGW); // //double WL = WeightedCombo(Mid, SizP, SizV, WExp); // //L2 = (WL * (1.0 - BGW)) + (BGW * L2); //} 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, Util.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 = Util.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 = Util.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 = Util.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 } } AnchorV = Util.CompactByVertex(P, AnchorV); //compact the fixed points along with the vertices FeatureV = Util.CompactByVertex(P, FeatureV); FeatureE = Util.CompactByEdge(P, FeatureE); P.Compact(); //this cleans the mesh data structure of unused elements } Mesh MR = P.ToRhinoMesh(); MR.Unweld(0.4, true); doc.Objects.AddMesh(MR); doc.Views.Redraw(); RhinoApp.WriteLine("The {0} command added one mesh to the document.", EnglishName); // --- return(Result.Success); }
protected override void SolveInstance(IGH_DataAccess DA) { GH_Structure <IGH_GeometricGoo> iMeshTree = new GH_Structure <IGH_GeometricGoo>(); GH_Structure <GH_Integer> iFaceIndices = new GH_Structure <GH_Integer>(); GH_Structure <GH_Number> iThicknesses = new GH_Structure <GH_Number>(); GH_Structure <GH_Number> iExtrudes = new GH_Structure <GH_Number>(); DA.GetDataTree <IGH_GeometricGoo>(0, out iMeshTree); DA.GetDataTree <GH_Integer>(1, out iFaceIndices); DA.GetDataTree <GH_Number>(2, out iThicknesses); DA.GetDataTree <GH_Number>(3, out iExtrudes); GH_Structure <IGH_GeometricGoo> oNewMeshTree = new GH_Structure <IGH_GeometricGoo>(); for (int i = 0; i < iMeshTree.PathCount; i++) { GH_Path pathMesh = iMeshTree.get_Path(i); for (int j = 0; j < iMeshTree.Branches[i].Count; j++) { Mesh mesh; if (!iMeshTree.Branches[i][j].CastTo <Mesh>(out mesh)) { continue; } else { PlanktonMesh pMesh = mesh.ToPlanktonMesh(); PlanktonMesh pSMesh = new PlanktonMesh(); List <Point3d[]> faceOffset = new List <Point3d[]>(); List <int> facesIDs = new List <int>(); List <double> thicks = new List <double>(); List <double> offsets = new List <double>(); #region value initial for (int vi = 0; vi < iFaceIndices.PathCount; vi++) { if (pathMesh != iFaceIndices.get_Path(vi)) { continue; } else { foreach (var sv in iFaceIndices[vi].ToList()) { int number; sv.CastTo <int>(out number); facesIDs.Add(number); } } } for (int vi = 0; vi < iThicknesses.PathCount; vi++) { if (pathMesh != iThicknesses.get_Path(vi)) { continue; } else { foreach (var sv in iThicknesses[vi].ToList()) { double number; sv.CastTo <double>(out number); thicks.Add(number); } } } for (int vi = 0; vi < iExtrudes.PathCount; vi++) { if (pathMesh != iExtrudes.get_Path(vi)) { continue; } else { foreach (var sv in iExtrudes[vi].ToList()) { double number; sv.CastTo <double>(out number); offsets.Add(number); } } } #endregion #region Create windows vertices for (int k = 0; k < pMesh.Faces.Count; k++) { if (!facesIDs.Contains(k)) { continue; } int id = facesIDs.IndexOf(k); Point3d center = pMesh.Faces.GetFaceCenter(k).ToPoint3d(); int[] fVertices = pMesh.Faces.GetFaceVertices(k); Point3d[] centerVertices = new Point3d[fVertices.Length]; //Vector3d normal = pMesh.Faces.GetFaceNormal(k).ToVector3d(); //!!!!!!!!!!!!!!!!! plankton vertices normal have bugs!!!!!!! for (int fv = 0; fv < fVertices.Length; fv++) { Point3d v = pMesh.Vertices[fVertices[fv]].ToPoint3d(); Vector3d vec = center - v; Point3d v2 = new Point3d(); if (fv == fVertices.Length - 1) { v2 = pMesh.Vertices[fVertices[0]].ToPoint3d(); } else { v2 = pMesh.Vertices[fVertices[fv + 1]].ToPoint3d(); } int thickID = (id >= thicks.Count) ? thicks.Count - 1 : id; int offsetID = (id >= offsets.Count) ? offsets.Count - 1 : id; Vector3d normal = Vector3d.CrossProduct(vec, v - v2); normal.Unitize(); centerVertices[fv] = v + vec * thicks[thickID] + normal * offsets[offsetID]; } faceOffset.Add(centerVertices); } #endregion // add vertices #region add Vertices foreach (PlanktonVertex v in pMesh.Vertices) { pSMesh.Vertices.Add(v.ToPoint3d()); } #endregion #region add frame vertices // add offset vertices foreach (Point3d[] pts in faceOffset) { foreach (Point3d p in pts) { pSMesh.Vertices.Add(p); } } #endregion // add mesh int vc = 0; // vertice counter for (int k = 0; k < pMesh.Faces.Count; k++) { if (!facesIDs.Contains(k)) { int[] fVertices = pMesh.Faces.GetFaceVertices(k); if (fVertices.Length == 3) { pSMesh.Faces.AddFace(fVertices[0], fVertices[1], fVertices[2]); } else { pSMesh.Faces.AddFace(fVertices[0], fVertices[1], fVertices[2], fVertices[3]); } } else { int[] fVertices = pMesh.Faces.GetFaceVertices(k); if (fVertices.Length == 3) { pSMesh.Faces.AddFace(fVertices[0], fVertices[1], pMesh.Vertices.Count + vc + 1, pMesh.Vertices.Count + vc + 0); pSMesh.Faces.AddFace(fVertices[1], fVertices[2], pMesh.Vertices.Count + vc + 2, pMesh.Vertices.Count + vc + 1); pSMesh.Faces.AddFace(fVertices[2], fVertices[0], pMesh.Vertices.Count + vc + 0, pMesh.Vertices.Count + vc + 2); if (m_cap) { pSMesh.Faces.AddFace(pMesh.Vertices.Count + vc + 0, pMesh.Vertices.Count + vc + 1, pMesh.Vertices.Count + vc + 2); } vc += 3; } else { pSMesh.Faces.AddFace(fVertices[0], fVertices[1], pMesh.Vertices.Count + vc + 1, pMesh.Vertices.Count + vc + 0); pSMesh.Faces.AddFace(fVertices[1], fVertices[2], pMesh.Vertices.Count + vc + 2, pMesh.Vertices.Count + vc + 1); pSMesh.Faces.AddFace(fVertices[2], fVertices[3], pMesh.Vertices.Count + vc + 3, pMesh.Vertices.Count + vc + 2); pSMesh.Faces.AddFace(fVertices[3], fVertices[0], pMesh.Vertices.Count + vc + 0, pMesh.Vertices.Count + vc + 3); if (m_cap) { pSMesh.Faces.AddFace(pMesh.Vertices.Count + vc + 0, pMesh.Vertices.Count + vc + 1, pMesh.Vertices.Count + vc + 2, pMesh.Vertices.Count + vc + 3); } vc += 4; } } } oNewMeshTree.Append( GH_Convert.ToGeometricGoo(pSMesh.ToRhinoMesh()), pathMesh); } } } DA.SetDataTree(0, oNewMeshTree); }
public static Mesh ReMesh(Mesh mesh, double targetLength) { double L = targetLength; PlanktonMesh P = new PlanktonMesh(); List <int> AnchorV = new List <int>(); List <int> FeatureV = new List <int>(); List <int> FeatureE = new List <int>(); double FixT = 0.00001; double LengthTol = 0.1; //a tolerance for when to split/collapse edges double SmoothStrength = 0.8; //smoothing strength double PullStrength = 0.8; //pull to target mesh strength double CurvDep = 0; int Flip = 0; int iterations = 200; Mesh M = mesh.DuplicateMesh(); M.Faces.ConvertQuadsToTriangles(); P = M.ToPlanktonMesh(); var FC = new List <Curve>(); var boundaries = M.GetNakedEdges(); var curves = boundaries.Select(p => p.ToPolylineCurve()); FC.AddRange(curves); List <Point3d> FV = new List <Point3d>(); foreach (var boundary in boundaries) { for (int i = 0; i < boundary.Count; i++) { int j = i == boundary.Count - 1 ? 0 : i + 1; int k = i == 0 ? boundary.Count - 1 : i - 1; Point3d p = boundary[i]; Point3d pa = boundary[j]; Point3d pb = boundary[k]; Vector3d va = pa - p; Vector3d vb = pb - p; double angle = Vector3d.VectorAngle(va, vb); if (angle < Math.PI * 0.6666) { FV.Add(p); } } } //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; } } } for (int iter = 0; iter < iterations; iter++) { 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] = Util.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 = L; Point3d Mid = Util.MidPt(P, i); //if (CurvDep > 0) //{ // double NormDiff = Vector3d.VectorAngle(Normals[vStart], Normals[vEnd]); // L2 = Math.Min((1.0 / (3.0 * NormDiff) * L), 5 * L); // if (CurvDep != 1) // { // L2 = L2 * (CurvDep) + L * (1.0 - CurvDep); // } //} //if (BoundScale != 1.0) //{ // double MinDist = 99954; // for (int j = 0; j < FC.Count; j++) // { // double param = new double(); // FC[j].ClosestPoint(Mid, out param); // double ThisDist = Mid.DistanceTo(FC[j].PointAt(param)); // if (ThisDist < MinDist) // { MinDist = ThisDist; } // } // if (MinDist < BoundDist) // { // L2 = L2 * BoundScale + (MinDist / BoundDist) * (L2 * (1 - BoundScale)); // } //} //if (SizP.Count > 0) //{ // L2 = WeightedCombo(Mid, SizP, SizV, WExp, L2, BGW); // // L2 = (WL * (1.0 - BGW)) + (BGW * L2); //} 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, Util.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; } } } double L2 = L; Point3d Mid = Util.MidPt(P, i); if (CurvDep > 0) { double NormDiff = Vector3d.VectorAngle(Normals[vStart], Normals[vEnd]); L2 = Math.Min((1.0 / (3.0 * NormDiff) * L), 5 * L); if (CurvDep != 1) { L2 = L2 * (CurvDep) + L * (1.0 - CurvDep); } } //if (BoundScale != 1.0) //{ // double MinDist = 99954; // for (int j = 0; j < FC.Count; j++) // { // double param = new double(); // FC[j].ClosestPoint(Mid, out param); // double ThisDist = Mid.DistanceTo(FC[j].PointAt(param)); // if (ThisDist < MinDist) // { MinDist = ThisDist; } // } // if (MinDist < BoundDist) // { // L2 = L2 * BoundScale + (MinDist / BoundDist) * (L2 * (1 - BoundScale)); // } //} //if (SizP.Count > 0) //{ // L2 = WeightedCombo(Mid, SizP, SizV, WExp, L2, BGW); // //double WL = WeightedCombo(Mid, SizP, SizV, WExp); // //L2 = (WL * (1.0 - BGW)) + (BGW * L2); //} 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, Util.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 = Util.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 = Util.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 = Util.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 } } AnchorV = Util.CompactByVertex(P, AnchorV); //compact the fixed points along with the vertices FeatureV = Util.CompactByVertex(P, FeatureV); FeatureE = Util.CompactByEdge(P, FeatureE); P.Compact(); //this cleans the mesh data structure of unused elements } Mesh MR = P.ToRhinoMesh(); MR.Unweld(0.4, true); return(MR); }