From 8d1bd9fc79cedaeee2b9d45884a0317ddd8f209b Mon Sep 17 00:00:00 2001 From: Tom Svilans Date: Thu, 18 Feb 2021 12:21:18 +0100 Subject: [PATCH] fix(tobrep,glulamdata): reverted to old (faster) method of generating Breps, consolidated GlulamData components into one --- .../Cmpt_CreateGlulamDataFromWidthHeight.cs | 6 +- GluLamb.GH/Create/Cmpt_GlulamData.cs | 235 ++++++++++++++++-- GluLamb.GH/Properties/AssemblyInfo.cs | 2 +- GluLamb/Glulam/FreeformGlulam.cs | 80 +++++- GluLamb/Glulam/GlulamBase.cs | 4 +- GluLamb/Properties/AssemblyInfo.cs | 2 +- GluLamb/Structure/Element.cs | 5 + 7 files changed, 301 insertions(+), 33 deletions(-) diff --git a/GluLamb.GH/Create/Cmpt_CreateGlulamDataFromWidthHeight.cs b/GluLamb.GH/Create/Cmpt_CreateGlulamDataFromWidthHeight.cs index 0a08118..a06f353 100644 --- a/GluLamb.GH/Create/Cmpt_CreateGlulamDataFromWidthHeight.cs +++ b/GluLamb.GH/Create/Cmpt_CreateGlulamDataFromWidthHeight.cs @@ -1,4 +1,5 @@ -/* +#if OBSOLETE +/* * GluLamb * A constrained glulam modelling toolkit. * Copyright 2020 Tom Svilans @@ -159,4 +160,5 @@ public override Guid ComponentGuid get { return new Guid("3769C2D5-D4E3-4DDA-85E1-7C3C9865930D"); } } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/GluLamb.GH/Create/Cmpt_GlulamData.cs b/GluLamb.GH/Create/Cmpt_GlulamData.cs index 2f84d99..addd8fe 100644 --- a/GluLamb.GH/Create/Cmpt_GlulamData.cs +++ b/GluLamb.GH/Create/Cmpt_GlulamData.cs @@ -18,28 +18,176 @@ */ using System; +using System.Linq; +using System.Drawing; +using System.Windows.Forms; + +using Grasshopper; using Grasshopper.Kernel; +using Grasshopper.Kernel.Parameters; +using Grasshopper.Kernel.Special; + using Rhino.Geometry; namespace GluLamb.GH.Components { - public class Cmpt_GlulamData : GH_Component + public enum GlulamDataMethod + { + Section, + Lamella + } + + public class Cmpt_GlulamData : GH_Component, IGH_VariableParameterComponent { public Cmpt_GlulamData() : base("GlulamData", "GlulamData", "Create glulam data.", "GluLamb", "Create") { + DataMethod = GlulamDataMethod.Section; + + foreach (var parameter in parameters) + { + if (Params.Input.Any(x => x.Name == parameter.Name)) + Params.UnregisterInputParameter(Params.Input.First(x => x.Name == parameter.Name), true); + } + + AddParam(4); + AddParam(5); + AddParam(6); + AddParam(7); + + Params.OnParametersChanged(); + ExpireSolution(true); + } + + GH_ValueList valueList = null; + IGH_Param alignment_parameter = null; + + + public GlulamDataMethod DataMethod = GlulamDataMethod.Section; + readonly IGH_Param[] parameters = new IGH_Param[8] + { + new Param_Number() {Name = "LamellaX", NickName = "Lx", Description = "Lamella dimension in the X-axis.", Optional = true, Access = GH_ParamAccess.item}, + new Param_Number(){Name = "LamellaY", NickName = "Ly", Description = "Lamella dimension in the Y-axis.", Optional = true, Access = GH_ParamAccess.item }, + new Param_Integer(){Name = "NumX", NickName = "Nx", Description = "Number of lamellae in X-axis.", Optional = true, Access = GH_ParamAccess.item }, + new Param_Integer(){Name = "NumY", NickName = "Ny", Description = "Number of lamellae in Y-axis.", Optional = true, Access = GH_ParamAccess.item }, + new Param_Number() {Name = "Width (X)", NickName = "X", Description = "Section dimension in the X-axis.", Optional = true, Access = GH_ParamAccess.item }, + new Param_Number(){Name = "Height (Y)", NickName = "Y", Description = "Section dimension in the Y-axis.", Optional = true, Access = GH_ParamAccess.item }, + new Param_Number() {Name = "Curvature (X)", NickName = "Kx", Description = "Maximum curvature in the X-axis.", Optional = true, Access = GH_ParamAccess.item }, + new Param_Number(){Name = "Curvature (Y)", NickName = "Ky", Description = "Maximum curvature in the Y-axis.", Optional = true, Access = GH_ParamAccess.item }, + }; + + bool IGH_VariableParameterComponent.CanInsertParameter(GH_ParameterSide side, int index) => false; + bool IGH_VariableParameterComponent.CanRemoveParameter(GH_ParameterSide side, int index) => false; + IGH_Param IGH_VariableParameterComponent.CreateParameter(GH_ParameterSide side, int index) => null; + bool IGH_VariableParameterComponent.DestroyParameter(GH_ParameterSide side, int index) => false; + void IGH_VariableParameterComponent.VariableParameterMaintenance() { } + + private void AddParam(int index) + { + int insertIndex = Params.Input.Count; + for (int i = 0; i < Params.Input.Count; i++) + { + int otherIndex = Array.FindIndex(parameters, x => x.Name == Params.Input[i].Name); + if (otherIndex > index) + { + insertIndex = i; + break; + } + } + Params.RegisterInputParam(parameters[index], insertIndex); + } + + private void SetMethodLamella(object sender, EventArgs e) + { + DataMethod = GlulamDataMethod.Lamella; + + foreach (var parameter in parameters) + { + if (Params.Input.Any(x => x.Name == parameter.Name)) + Params.UnregisterInputParameter(Params.Input.First(x => x.Name == parameter.Name), true); + } + + AddParam(0); + AddParam(1); + AddParam(2); + AddParam(3); + + Params.OnParametersChanged(); + ExpireSolution(true); + } + private void SetMethodSection(object sender, EventArgs e) + { + DataMethod = GlulamDataMethod.Section; + + foreach (var parameter in parameters) + { + if (Params.Input.Any(x => x.Name == parameter.Name)) + Params.UnregisterInputParameter(Params.Input.First(x => x.Name == parameter.Name), true); + } + + AddParam(4); + AddParam(5); + AddParam(6); + AddParam(7); + + Params.OnParametersChanged(); + ExpireSolution(true); + } + + protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu) + { + Menu_AppendItem(menu, "Lamella", SetMethodLamella, true, DataMethod == GlulamDataMethod.Lamella); + Menu_AppendItem(menu, "Section", SetMethodSection, true, DataMethod == GlulamDataMethod.Section); + base.AppendAdditionalComponentMenuItems(menu); + } + + protected override void BeforeSolveInstance() + { + if (valueList == null) + { + if (alignment_parameter.Sources.Count == 0) + { + valueList = new GH_ValueList(); + } + else + { + foreach (var source in alignment_parameter.Sources) + { + if (source is GH_ValueList) valueList = source as GH_ValueList; + return; + } + } + + valueList.CreateAttributes(); + valueList.Attributes.Pivot = new PointF(this.Attributes.Pivot.X - 200, this.Attributes.Pivot.Y - 1); + valueList.ListItems.Clear(); + + var alignmentNames = Enum.GetNames(typeof(GlulamData.CrossSectionPosition)); + var alignmentValues = Enum.GetValues(typeof(GlulamData.CrossSectionPosition)); + + for (int i = 0; i < alignmentNames.Length; ++i) + { + valueList.ListItems.Add(new GH_ValueListItem(alignmentNames[i], $"{i}")); + } + + valueList.SelectItem(4); + + Instances.ActiveCanvas.Document.AddObject(valueList, false); + alignment_parameter.AddSource(valueList); + alignment_parameter.CollectData(); + } } protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) { - pManager.AddNumberParameter("LamW", "LW", "Lamella width.", GH_ParamAccess.item, 20.0); - pManager.AddNumberParameter("LamH", "LH", "Lamella height.", GH_ParamAccess.item, 20.0); - pManager.AddIntegerParameter("NumW", "NW", "Number of lamellas in X-direction.", GH_ParamAccess.item, 4); - pManager.AddIntegerParameter("NumH", "NH", "Number of lamellas in Y-direction.", GH_ParamAccess.item, 4); - pManager.AddIntegerParameter("Interpolation", "Int", "Interpolation method to use between glulam frames.", GH_ParamAccess.item, 2); - pManager.AddIntegerParameter("Samples", "S", "Number of samples along glulam length for generating cross-sections.", GH_ParamAccess.item, 100); + //pManager.AddIntegerParameter("Interpolation", "Int", "Interpolation method to use between glulam frames.", GH_ParamAccess.item, 2); + pManager.AddIntegerParameter("Samples", "S", "Resolution of length subdivision.", GH_ParamAccess.item, 100); + pManager.AddIntegerParameter("Alignment", "A", "Cross-section alignment as an integer value between 0 and 8.", GH_ParamAccess.item, 4); + + alignment_parameter = pManager[1]; + } protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) @@ -49,21 +197,70 @@ protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager protected override void SolveInstance(IGH_DataAccess DA) { - double lw = 0, lh = 0; - int nw = 0, nh = 0; - - int samples = 0; - int interpolation = 2; + int samples = 0, m_alignment = 4; + //int interpolation = 2; - DA.GetData("LamW", ref lw); - DA.GetData("LamH", ref lh); - DA.GetData("NumW", ref nw); - DA.GetData("NumH", ref nh); DA.GetData("Samples", ref samples); - DA.GetData("Interpolation", ref interpolation); + DA.GetData("Alignment", ref m_alignment); + + //DA.GetData("Interpolation", ref interpolation); + GlulamData data = null; + + if (DataMethod == GlulamDataMethod.Lamella) + { + double lamella_x = 20, lamella_y = 20; + int num_x = 4, num_y = 4; + + DA.GetData("LamellaX", ref lamella_x); + DA.GetData("LamellaY", ref lamella_y); + DA.GetData("NumX", ref num_x); + DA.GetData("NumY", ref num_y); + + data = new GlulamData(Math.Max(num_x, 1), Math.Max(num_y, 1), lamella_x, lamella_y, samples); + data.SectionAlignment = (GlulamData.CrossSectionPosition)m_alignment; + + //data.InterpolationType = (GlulamData.Interpolation)interpolation; + } + else if (DataMethod == GlulamDataMethod.Section) + { + double m_width = 80.0, m_height = 80.0; + double kx = 0, ky = 0; + DA.GetData("Width (X)", ref m_width); + DA.GetData("Height (Y)", ref m_height); + + DA.GetData("Curvature (X)", ref kx); + DA.GetData("Curvature (Y)", ref ky); + + if (m_width < 0) m_width = -m_width; + if (m_height < 0) m_height = -m_height; + + if (m_width == 0.0) m_width = 80.0; + if (m_height == 0.0) m_height = 80.0; + + double l_width, l_height; + if (kx > 0) + l_width = 1 / (Glulam.RadiusMultiplier * kx); + else + l_width = m_width; + + if (ky > 0) + l_height = 1 / (Glulam.RadiusMultiplier * ky); + else + l_height = m_height; + + int n_width = Math.Max((int)Math.Ceiling(m_width / l_width), 1); + int n_height = Math.Max((int)Math.Ceiling(m_height / l_height), 1); + + l_width = m_width / n_width; + l_height = m_height / n_height; + + data = new GlulamData(n_width, n_height, l_width, l_height, GlulamData.DefaultCurvatureSamples); + data.SectionAlignment = (GlulamData.CrossSectionPosition)m_alignment; + + //data.InterpolationType = (GlulamData.Interpolation)interpolation; + } + - GlulamData data = new GlulamData(Math.Max(nw, 1), Math.Max(nh, 1), lw, lh, samples); - data.InterpolationType = (GlulamData.Interpolation)interpolation; DA.SetData("GlulamData", new GH_GlulamData(data)); } diff --git a/GluLamb.GH/Properties/AssemblyInfo.cs b/GluLamb.GH/Properties/AssemblyInfo.cs index 3f3c3af..4c92982 100644 --- a/GluLamb.GH/Properties/AssemblyInfo.cs +++ b/GluLamb.GH/Properties/AssemblyInfo.cs @@ -15,6 +15,6 @@ [assembly: Guid("05279f4b-0f9a-4b26-84f0-938a942be2a3")] -[assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.2.*")] diff --git a/GluLamb/Glulam/FreeformGlulam.cs b/GluLamb/Glulam/FreeformGlulam.cs index 8204f59..ab189a4 100644 --- a/GluLamb/Glulam/FreeformGlulam.cs +++ b/GluLamb/Glulam/FreeformGlulam.cs @@ -43,7 +43,7 @@ public override void GenerateCrossSectionPlanes(int N, out Plane[] frames, out d { Curve curve = Centreline; - double multiplier = RhinoMath.UnitScale(Rhino.RhinoDoc.ActiveDoc.ModelUnitSystem, UnitSystem.Millimeters); + double multiplier = RhinoMath.UnitScale(UnitSystem.Millimeters, Rhino.RhinoDoc.ActiveDoc.ModelUnitSystem); //PolylineCurve discrete = curve.ToPolyline(Glulam.Tolerance * 10, Glulam.AngleTolerance, 0.0, 0.0); PolylineCurve discrete = curve.ToPolyline(multiplier * Tolerance, AngleTolerance, multiplier * MininumSegmentLength, curve.GetLength() / MinimumNumSegments); @@ -54,9 +54,7 @@ public override void GenerateCrossSectionPlanes(int N, out Plane[] frames, out d parameters = new double[N]; for (int i = 0; i < N; ++i) - { curve.ClosestPoint(discrete2[i], out parameters[i]); - } } else { @@ -168,8 +166,31 @@ public override Mesh ToMesh(double offset = 0.0, GlulamData.Interpolation interp //m_section_corners.Select(x => frames.Select(y => m.Vertices.Add(y.PointAt(x.X, x.Y)))); - for (int j = 0; j < m_corners.Length; ++j) - m.Vertices.Add(pplane.PointAt(m_corners[j].X, m_corners[j].Y)); + //for (int j = 0; j < m_corners.Length; ++j) + // m.Vertices.Add(pplane.PointAt(m_corners[j].X, m_corners[j].Y)); + + var start_profile = new Polyline(m_corners); + start_profile.Transform(Rhino.Geometry.Transform.PlaneToPlane(Plane.WorldXY, frames.First())); + var start_mesh = new Mesh(); + start_mesh.Vertices.AddVertices(start_profile); + + start_profile.Add(start_profile[0]); + start_mesh.Faces.AddFaces(start_profile.TriangulateClosedPolyline()); + start_mesh.Faces.ConvertTrianglesToQuads(RhinoMath.ToRadians(2), 0.875); + + var end_profile = new Polyline(m_corners); + end_profile.Transform(Rhino.Geometry.Transform.PlaneToPlane(Plane.WorldXY, frames.Last())); + var end_mesh = new Mesh(); + end_mesh.Vertices.AddVertices(end_profile); + + end_profile.Add(end_profile[0]); + end_mesh.Faces.AddFaces(end_profile.TriangulateClosedPolyline()); + end_mesh.Faces.ConvertTrianglesToQuads(RhinoMath.ToRadians(2), 0.875); + + m.Append(start_mesh); + m.Append(end_mesh); + + return m; m.TextureCoordinates.Add(0, 0); m.TextureCoordinates.Add(0, texHeight); @@ -236,14 +257,14 @@ public Polyline[] GetCrossSections(double offset = 0.0) return cross_sections; } +#if SLOWBREP public override Brep ToBrep(double offset = 0.0) { - /* + int N = Math.Max(Data.Samples, 6); GenerateCrossSectionPlanes(N, out Plane[] frames, out double[] parameters, Data.InterpolationType); - int numCorners = 4; GenerateCorners(offset); List[] crvPts = new List[numCorners]; @@ -267,7 +288,7 @@ public override Brep ToBrep(double offset = 0.0) crvPts[j].Add(temp); } } - */ + var edge_points = GetEdgePoints(offset); int numCorners = m_section_corners.Length; @@ -337,7 +358,50 @@ public override Brep ToBrep(double offset = 0.0) return brep; } +#else + + public override Brep ToBrep(double offset = 0.0) + { + int N = Math.Max(Data.Samples, 6); + + GenerateCrossSectionPlanes(N, out Plane[] frames, out double[] parameters, Data.InterpolationType); + var cross_section_profile = new Polyline(GenerateCorners(offset)); + cross_section_profile.Add(cross_section_profile[0]); + + Transform xform; + + var profiles = new List(); + + for (int i = 0; i < parameters.Length; ++i) + { + //frames[i] = frames[i].FlipAroundYAxis(); + xform = Rhino.Geometry.Transform.PlaneToPlane(Plane.WorldXY, frames[i]); + var temp_profile = cross_section_profile.Duplicate().ToNurbsCurve(); + temp_profile.Transform(xform); + profiles.Add(temp_profile); + } + + if (profiles.Count < 1) + throw new Exception("FreeformGlulam.ToBrep(): profiles was null"); + + var body = Brep.CreateFromLoft(profiles, Point3d.Unset, Point3d.Unset, LoftType.Tight, false); + var start_cap = Brep.CreatePlanarBreps(profiles.First(), Tolerance); + var end_cap = Brep.CreatePlanarBreps(profiles.Last(), Tolerance); + + if (body == null) + throw new Exception("FreeformGlulam.ToBrep(): body was null"); + else if (start_cap == null) + throw new Exception("FreeformGlulam.ToBrep(): start_cap was null"); + else if (end_cap == null) + throw new Exception("FreeformGlulam.ToBrep(): end_cap was null"); + + var joined = Brep.JoinBreps(body.Concat(start_cap).Concat(end_cap), Tolerance); + if (joined.Length > 0) + return joined[0]; + else throw new Exception("FreeformGlulam.ToBrep(): Joined Brep failed."); + } +#endif public List[] Test_GetBoundingBrepPoints() { int N = Math.Max(Data.Samples, 6); diff --git a/GluLamb/Glulam/GlulamBase.cs b/GluLamb/Glulam/GlulamBase.cs index ba5f0ef..b982f64 100644 --- a/GluLamb/Glulam/GlulamBase.cs +++ b/GluLamb/Glulam/GlulamBase.cs @@ -39,13 +39,13 @@ public abstract partial class Glulam : BeamBase public static double RadiusMultiplier = 200.0; // This is the Eurocode 5 formula: lamella thickness cannot exceed 1/200th of the curvature radius. public static int CurvatureSamples = 100; // Number of samples to samples curvature at. public static double RadiusTolerance = 0.00001; // For curvature calculations: curvature radius and lamella thickness cannot exceed this - public static double MininumSegmentLength = 50.0; // Minimum length of discretized segment when creating glulam geometry (mm). + public static double MininumSegmentLength = 30.0; // Minimum length of discretized segment when creating glulam geometry (mm). public static int MinimumNumSegments = 25; #region Static variables and methods public static double Tolerance = Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance; public static double OverlapTolerance = 1.0 * Rhino.RhinoMath.UnitScale(Rhino.RhinoDoc.ActiveDoc.ModelUnitSystem, Rhino.UnitSystem.Millimeters); - public static double AngleTolerance = Rhino.RhinoMath.ToRadians(5.0); + public static double AngleTolerance = Rhino.RhinoMath.ToRadians(2.5); #endregion diff --git a/GluLamb/Properties/AssemblyInfo.cs b/GluLamb/Properties/AssemblyInfo.cs index c260b8b..2859c4d 100644 --- a/GluLamb/Properties/AssemblyInfo.cs +++ b/GluLamb/Properties/AssemblyInfo.cs @@ -15,4 +15,4 @@ [assembly: Guid("322879B2-2F9A-4D5E-9E1C-D3901EDFFD1B")] -[assembly: AssemblyVersion("1.0.*")] \ No newline at end of file +[assembly: AssemblyVersion("1.2.*")] \ No newline at end of file diff --git a/GluLamb/Structure/Element.cs b/GluLamb/Structure/Element.cs index eebf6da..6ef6da1 100644 --- a/GluLamb/Structure/Element.cs +++ b/GluLamb/Structure/Element.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Rhino.Geometry; +using Rhino.Collections; using System.Threading.Tasks; @@ -13,6 +14,7 @@ public class Element public List Connections; protected Plane m_plane; public GeometryBase Geometry; + public ArchivableDictionary UserDictionary; public Element(string name = "") { @@ -20,6 +22,7 @@ public Element(string name = "") Name = name; m_plane = Plane.WorldXY; Geometry = null; + UserDictionary = new ArchivableDictionary(); } public Element(Plane handle, string name = "") @@ -27,6 +30,8 @@ public Element(Plane handle, string name = "") Connections = new List(); Name = name; m_plane = handle; + UserDictionary = new ArchivableDictionary(); + } public Plane Handle