/// <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) { // 1. Declare placeholder variables List <Curve> struts = new List <Curve>(); double radius = 0; // 2. Attempt to fetch data inputs if (!DA.GetDataList(0, struts)) { return; } if (!DA.GetData(1, ref radius)) { return; } // 3. Validate data if (struts == null || struts.Count == 0) { return; } if (radius <= 0) { return; } // 4. Set some variables int sides = 6; // Number of sides on each strut double tol = RhinoDoc.ActiveDoc.ModelAbsoluteTolerance; // 5. Instantiate ExoMesh object // This constructor cleans the curve network (removes duplicates), and formats it as an ExoMesh. ExoMesh exoMesh = new ExoMesh(struts); //==================================================================================== // PART A - Compute radii // Set the start/end radii of each sleeve, based on user input. //==================================================================================== // C0. Set radii foreach (ExoSleeve sleeve in exoMesh.Sleeves) { sleeve.StartRadius = radius; sleeve.EndRadius = radius; } //==================================================================================== // PART B - Compute plate offsets // Each plate is offset from its parent node, to avoid mesh overlaps. // We also ensure that the no plates are engulfed by the hulls, so we're looking for // a convex plate layout. If any plate vertex gets engulfed, meshing will fail. //==================================================================================== // B0. Loop over nodes for (int i = 0; i < exoMesh.Hulls.Count; i++) { // If node has only 1 strut, skip it if (exoMesh.Hulls[i].SleeveIndices.Count < 2) { continue; } // Compute the offsets required to avoid plate overlaps bool success = exoMesh.ComputeOffsets(i, tol); // To improve convex hull shape at 'sharp' nodes, we add an extra plate exoMesh.FixSharpNodes(i, sides); } // IDEA : add a new loop here that adjusts radii to avoid overlapping struts //==================================================================================== // PART C - Construct sleeve meshes and hull points // //==================================================================================== // E0. Loop over all sleeves for (int i = 0; i < exoMesh.Sleeves.Count; i++) { Mesh sleeveMesh = exoMesh.MakeSleeve(i, sides); // append the new sleeve mesh to the full lattice mesh exoMesh.Mesh.Append(sleeveMesh); } //==================================================================================== // PART D - Construct hull meshes // Generates convex hulls, then removes the faces that lie on the plates. //==================================================================================== // D0. Loop over all hulls for (int i = 0; i < exoMesh.Hulls.Count; i++) { ExoHull node = exoMesh.Hulls[i]; int plateCount = exoMesh.Hulls[i].PlateIndices.Count; // If node has a single plate, create an endmesh if (plateCount < 2) { Mesh endMesh = exoMesh.MakeEndFace(i, sides); exoMesh.Mesh.Append(endMesh); } // If node has more than 1 plate, create a hullmesh else { Mesh hullMesh = exoMesh.MakeConvexHull(i, sides, tol, true); exoMesh.Mesh.Append(hullMesh); } } // 6. Post-process the final mesh. exoMesh.Mesh.Vertices.CombineIdentical(true, true); exoMesh.Mesh.FaceNormals.ComputeFaceNormals(); exoMesh.Mesh.UnifyNormals(); exoMesh.Mesh.Normals.ComputeNormals(); // 7. Set output DA.SetData(0, exoMesh.Mesh); }
/// <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) { // 1. Declare placeholder variables List <Curve> struts = new List <Curve>(); string gradientString = null; double maxRadius = 0; double minRadius = 0; // 2. Attempt to fetch data inputs if (!DA.GetDataList(0, struts)) { return; } if (!DA.GetData(1, ref gradientString)) { return; } if (!DA.GetData(2, ref maxRadius)) { return; } if (!DA.GetData(3, ref minRadius)) { return; } // 3. Validate data if (struts == null || struts.Count == 0) { return; } if (maxRadius <= 0 || minRadius <= 0) { return; } // 4. Set some variables int sides = 6; // Number of sides on each strut double tol = RhinoDoc.ActiveDoc.ModelAbsoluteTolerance; // 5. Instantiate ExoMesh object // This constructor cleans the curve network (removes duplicates), and formats it as an ExoMesh. ExoMesh exoMesh = new ExoMesh(struts); //==================================================================================== // PART A - Compute radii // Set the start/end radii of each sleeve, based on spatial gradient. //==================================================================================== // A0. Prepare bounding box domain for normalized gradient string BoundingBox fullBox = new BoundingBox(); foreach (ExoSleeve sleeve in exoMesh.Sleeves) { var strutBox = sleeve.Curve.GetBoundingBox(Plane.WorldXY); fullBox.Union(strutBox); } double boxSizeX = fullBox.Max.X - fullBox.Min.X; double boxSizeY = fullBox.Max.Y - fullBox.Min.Y; double boxSizeZ = fullBox.Max.Z - fullBox.Min.Z; gradientString = GH_ExpressionSyntaxWriter.RewriteForEvaluator(gradientString); // A1. Set radii foreach (ExoSleeve sleeve in exoMesh.Sleeves) { // Start node ExoHull node = exoMesh.Hulls[sleeve.HullPair.I]; var parser = new Grasshopper.Kernel.Expressions.GH_ExpressionParser(); parser.AddVariable("x", (node.Point3d.X - fullBox.Min.X) / boxSizeX); parser.AddVariable("y", (node.Point3d.Y - fullBox.Min.Y) / boxSizeY); parser.AddVariable("z", (node.Point3d.Z - fullBox.Min.Z) / boxSizeZ); sleeve.StartRadius = minRadius + (parser.Evaluate(gradientString)._Double) * (maxRadius - minRadius); parser.ClearVariables(); // End node node = exoMesh.Hulls[sleeve.HullPair.J]; parser.AddVariable("x", (node.Point3d.X - fullBox.Min.X) / boxSizeX); parser.AddVariable("y", (node.Point3d.Y - fullBox.Min.Y) / boxSizeY); parser.AddVariable("z", (node.Point3d.Z - fullBox.Min.Z) / boxSizeZ); sleeve.EndRadius = minRadius + (parser.Evaluate(gradientString)._Double) * (maxRadius - minRadius); parser.ClearVariables(); } //==================================================================================== // PART B - Compute plate offsets // Each plate is offset from its parent node, to avoid mesh overlaps. // We also need to ensure that no plates are engulfed by the hulls, so we're // looking for a convex plate layout. If any plate vertex gets engulfed, meshing will fail. //==================================================================================== // B0. Loop over nodes for (int i = 0; i < exoMesh.Hulls.Count; i++) { // If node has only 1 strut, skip it if (exoMesh.Hulls[i].SleeveIndices.Count < 2) { continue; } // Compute the offsets required to avoid plate overlaps bool success = exoMesh.ComputeOffsets(i, tol); // To improve convex hull shape at 'sharp' nodes, we add an extra plate exoMesh.FixSharpNodes(i, sides); } // IDEA : add a new loop here that adjusts radii to avoid overlapping struts //==================================================================================== // PART C - Construct sleeve meshes and hull points // //==================================================================================== // C0. Loop over all sleeves for (int i = 0; i < exoMesh.Sleeves.Count; i++) { Mesh sleeveMesh = exoMesh.MakeSleeve(i, sides); // Append the new sleeve mesh to the full lattice mesh exoMesh.Mesh.Append(sleeveMesh); } //==================================================================================== // PART D - Construct hull meshes // Generates convex hulls, then removes the faces that lie on the plates. //==================================================================================== // D0. Loop over all hulls for (int i = 0; i < exoMesh.Hulls.Count; i++) { ExoHull node = exoMesh.Hulls[i]; int plateCount = exoMesh.Hulls[i].PlateIndices.Count; // If node has a single plate, create an endmesh if (plateCount < 2) { Mesh endMesh = exoMesh.MakeEndFace(i, sides); exoMesh.Mesh.Append(endMesh); } // If node has more than 1 plate, create a hullmesh else { Mesh hullMesh = exoMesh.MakeConvexHull(i, sides, tol, true); exoMesh.Mesh.Append(hullMesh); } } // 6. Post-process the final mesh. exoMesh.Mesh.Vertices.CombineIdentical(true, true); exoMesh.Mesh.FaceNormals.ComputeFaceNormals(); exoMesh.Mesh.UnifyNormals(); exoMesh.Mesh.Normals.ComputeNormals(); // 7. Set output DA.SetData(0, exoMesh.Mesh); }