From 74999ba026dcd4197b5c46d595eca6cfe648080f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20S=C3=B8nderskov=20Schaltz?= Date: Thu, 19 Aug 2021 18:35:31 +0200 Subject: [PATCH 1/2] cleanup --- .../DataAccessHelper.cs | 65 +- Grasshopper_Doodles_Public/GH_Grid.cs | 8 +- Grasshopper_Doodles_Public/GH_GridViewer.cs | 678 ++++++++---------- .../GH_SectionType_ List.cs | 73 ++ .../GH_SectionType_Inverval.cs | 79 ++ .../GH_SectionType_Range.cs | 79 ++ Grasshopper_Doodles_Public/GradientParser.cs | 109 +-- .../Grasshopper_Doodles_Public.csproj | 4 + Grasshopper_Doodles_Public/Grid.cs | 16 +- Grasshopper_Doodles_Public/InputSelector.cs | 167 +++++ 10 files changed, 842 insertions(+), 436 deletions(-) create mode 100644 Grasshopper_Doodles_Public/GH_SectionType_ List.cs create mode 100644 Grasshopper_Doodles_Public/GH_SectionType_Inverval.cs create mode 100644 Grasshopper_Doodles_Public/GH_SectionType_Range.cs create mode 100644 Grasshopper_Doodles_Public/InputSelector.cs diff --git a/Grasshopper_Doodles_Public/DataAccessHelper.cs b/Grasshopper_Doodles_Public/DataAccessHelper.cs index 3f9a7a1..5bf8e86 100644 --- a/Grasshopper_Doodles_Public/DataAccessHelper.cs +++ b/Grasshopper_Doodles_Public/DataAccessHelper.cs @@ -15,6 +15,8 @@ namespace Grasshopper_Doodles_Public /// static class DataAccessHelper { + + static string msg = "\nThis might be because your component is outdated. Try to drag a new component of this type to the canvas and see if it helps :-)"; // Source: https://github.com/arendvw/clipper/tree/master/ClipperComponents/Helpers // Author: Arend van Waart arend@studioavw.nl @@ -44,7 +46,15 @@ public static T Fetch(this IGH_DataAccess da, int position) { var temp = default(T); - da.GetData(position, ref temp); + try + { + da.GetData(position, ref temp); + + } + catch (System.IndexOutOfRangeException e) + { + throw new IndexOutOfRangeException($"Input parameter not found at position {position}" + msg, e); + } return temp; } /// @@ -57,7 +67,15 @@ public static T Fetch(this IGH_DataAccess da, int position) public static T Fetch(this IGH_DataAccess da, string name) { var temp = default(T); - da.GetData(name, ref temp); + try + { + da.GetData(name, ref temp); + + } + catch (System.IndexOutOfRangeException e) + { + throw new IndexOutOfRangeException($"Input parameter not found: {name}" + msg, e); + } return temp; } @@ -71,7 +89,15 @@ public static T Fetch(this IGH_DataAccess da, string name) public static List FetchList(this IGH_DataAccess da, int position) { var temp = new List(); - da.GetDataList(position, temp); + try + { + da.GetDataList(position, temp); + + } + catch (System.IndexOutOfRangeException e) + { + throw new IndexOutOfRangeException($"Input parameter not found at position {position}" + msg, e); + } return temp; } @@ -85,7 +111,15 @@ public static List FetchList(this IGH_DataAccess da, int position) public static List FetchList(this IGH_DataAccess da, string name) { var temp = new List(); - da.GetDataList(name, temp); + try + { + da.GetDataList(name, temp); + + } + catch (System.IndexOutOfRangeException e) + { + throw new IndexOutOfRangeException($"Input parameter not found: {name}" + msg, e); + } return temp; } /// @@ -97,7 +131,16 @@ public static List FetchList(this IGH_DataAccess da, string name) /// public static GH_Structure FetchTree(this IGH_DataAccess da, int position) where T : IGH_Goo { - da.GetDataTree(position, out GH_Structure temp); + GH_Structure temp = default(GH_Structure); + try + { + + da.GetDataTree(position, out temp); + } + catch (System.IndexOutOfRangeException e) + { + throw new IndexOutOfRangeException($"Input parameter not found at position {position}" + msg, e); + } return temp; } @@ -110,7 +153,17 @@ public static GH_Structure FetchTree(this IGH_DataAccess da, int position) /// public static GH_Structure FetchTree(this IGH_DataAccess da, string name) where T : IGH_Goo { - da.GetDataTree(name, out GH_Structure temp); + + GH_Structure temp = default(GH_Structure); + try + { + da.GetDataTree(name, out temp); + + } + catch (System.IndexOutOfRangeException e) + { + throw new IndexOutOfRangeException($"Input parameter not found: {name}" + msg, e); + } return temp; } } diff --git a/Grasshopper_Doodles_Public/GH_Grid.cs b/Grasshopper_Doodles_Public/GH_Grid.cs index fd0bf7c..0b61f77 100644 --- a/Grasshopper_Doodles_Public/GH_Grid.cs +++ b/Grasshopper_Doodles_Public/GH_Grid.cs @@ -19,7 +19,7 @@ public class GhcGrid : GH_Component /// public GhcGrid() : base("Grid", "Grid", - "GraceHopper Grid\nUse this for analysis (Doodles_HLA).\nTodo:Use Clipper for offset and self intersect.", + "GraceHopper Grid\nUse this for analysis (Doodles_HLA). Based on https://github.com/HenningLarsenArchitects/Grasshopper_Doodles_Public", Constants.GH_TAB, Constants.GH_GENERIC_SUBTAB) { } @@ -44,6 +44,8 @@ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pM pManager[ps].Optional = true; // TODO: https://discourse.ladybug.tools/t/none-uniform-grid/2361/11 + pManager.AddBooleanParameter("GoLarge", "GoLarge", "Set to true if you accept grids larger than 60000 points. This is a safety check.", GH_ParamAccess.item, false); + @@ -57,6 +59,7 @@ protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager.AddGenericParameter("Grids", "G", "Output grids. GreenScenario.Geometry.Grid class", GH_ParamAccess.list); pManager.AddMeshParameter("Mesh", "M", "Meshes", GH_ParamAccess.list); pManager.AddGenericParameter("Points", "Pt", "Simulation points (center of each face)", GH_ParamAccess.tree); + //pManager.AddTextParameter("msg", "m", "msg", GH_ParamAccess.item); } @@ -73,6 +76,7 @@ protected override void SolveInstance(IGH_DataAccess DA) var offset = Units.ConvertFromMeter(DA.Fetch("Vertical Offset [m]")); var useCenters = DA.Fetch("IsoCurves"); var geometries = DA.FetchList("Geometry"); + var goLarge = DA.Fetch("GoLarge"); DataTree centers = new DataTree(); List myGrids = new List(); @@ -126,7 +130,7 @@ protected override void SolveInstance(IGH_DataAccess DA) for (int i = 0; i < breps.Count; i++) { - myGrids.Add(new Grid(breps[i], gridSize, useCenters: useCenters)); + myGrids.Add(new Grid(breps[i], gridSize, useCenters: useCenters, goLarge)); } for (int i = 0; i < myGrids.Count; i++) diff --git a/Grasshopper_Doodles_Public/GH_GridViewer.cs b/Grasshopper_Doodles_Public/GH_GridViewer.cs index cb410ec..b11228c 100644 --- a/Grasshopper_Doodles_Public/GH_GridViewer.cs +++ b/Grasshopper_Doodles_Public/GH_GridViewer.cs @@ -28,13 +28,11 @@ public GhGridViewer() public override GH_Exposure Exposure => GH_Exposure.primary; // puts the grid component in top. - int inputGradient, inputSelectorMin, inputSelecterMax, inputStepSize, inputStep = 0; - int steps = 0; - double stepSize = 0; + int i_inputGradient, i_inputSelectorMin, i_inputSelecterMax, i_inputSelectorSectionType = 0; //ToolStripMenuItem menuItemCaps = new ToolStripMenuItem(); - private bool caps = false; + //private bool caps = true; //public bool Caps //{ @@ -66,37 +64,47 @@ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pM pManager.AddNumberParameter("Results", "Results", "Results", GH_ParamAccess.tree); //2 - inputGradient = pManager.AddColourParameter("Gradient", "Gradient", "", GH_ParamAccess.item); - pManager[inputGradient].Optional = true; + i_inputGradient = pManager.AddColourParameter("_Gradient", "_Gradient", "Gradient.\nPro tip:You can set the min and max in the gradient component! :-)", GH_ParamAccess.item); + pManager[i_inputGradient].Optional = true; - //3 - var inputRange = pManager.AddTextParameter("Range", "Range*", "input a domain '2 to 10' or a number as max '10'\nBUGGY ON THE ISOCURVES. LEAVE DEFAULT.", GH_ParamAccess.item, String.Empty); - pManager[inputRange].Optional = true; + ////3 + //var inputRange = pManager.AddTextParameter("GradientRange", "GradientRange*", "Range of the colors", GH_ParamAccess.item, String.Empty); + //pManager[inputRange].Optional = true; //4 //pManager.AddBooleanParameter("Cap", "Cap*", "Cap min and max?\nBUGGY ON THE ISOCURVES. LEAVE DEFAULT.", GH_ParamAccess.item, false); + + //5 - inputSelectorMin = pManager.AddColourParameter("-", "-", "MinColor", GH_ParamAccess.item, Color.Purple); - pManager[inputSelectorMin].Optional = true; + i_inputSelectorMin = pManager.AddColourParameter("_MinColor", "_MinColor", "MinColor input to cap colors below the gradient max", GH_ParamAccess.item, Color.DarkGray); + pManager[i_inputSelectorMin].Optional = true; //6 - inputSelecterMax = pManager.AddColourParameter("-", "-", "MaxColor", GH_ParamAccess.item, Color.Pink); - pManager[inputSelecterMax].Optional = true; + i_inputSelecterMax = pManager.AddColourParameter("_MaxColor", "_MaxColor", "MaxColor input to cap colors above the gradient max", GH_ParamAccess.item, Color.LightGray); + pManager[i_inputSelecterMax].Optional = true; //7 - inputStepSize = pManager.AddIntegerParameter("StepSize", "S", "Steps of colors, default = 10", GH_ParamAccess.item, 1); - pManager[inputStepSize].Optional = true; - var cfParam = pManager[inputStepSize] as Param_Integer; - cfParam.AddNamedValue("Auto", 0); - cfParam.AddNamedValue("0.1", -1); - cfParam.AddNamedValue("1", 1); - cfParam.AddNamedValue("10", 10); - cfParam.AddNamedValue("100", 100); + i_inputSelectorSectionType = pManager.AddGenericParameter("_Section Type", "_Section Type", "Connect a section type component", GH_ParamAccess.item); + pManager[i_inputSelectorSectionType].Optional = true; + + + + //pManager.AddIntegerParameter("MaxCount", "MaxCount", "MaxCount", GH_ParamAccess.item); + + ////7 + //inputStepSize = pManager.AddIntegerParameter("StepSize", "S", "Steps of colors, default = 10", GH_ParamAccess.item, 1); + //pManager[inputStepSize].Optional = true; + //var cfParam = pManager[inputStepSize] as Param_Integer; + //cfParam.AddNamedValue("Auto", 0); + //cfParam.AddNamedValue("0.1", -1); + //cfParam.AddNamedValue("1", 1); + //cfParam.AddNamedValue("10", 10); + //cfParam.AddNamedValue("100", 100); //8 - inputStep = pManager.AddIntegerParameter("Steps", "S", "Total steps. This will overrule the StepSize. Do not connect both!", GH_ParamAccess.item, 1); - pManager[inputStep].Optional = true; + //inputStep = pManager.AddIntegerParameter("Steps", "S", "Total steps. This will overrule the StepSize. Do not connect both!", GH_ParamAccess.item, 1); + //pManager[inputStep].Optional = true; } @@ -106,7 +114,8 @@ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pM /// protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) { - pManager.AddMeshParameter("coloredMesh", "coloredMesh", "mesh", GH_ParamAccess.list); + int cm = pManager.AddMeshParameter("coloredMesh", "coloredMesh", "mesh", GH_ParamAccess.list); + pManager.HideParameter(cm); pManager.AddMeshParameter("layeredMesh", "layeredMesh", "mesh", GH_ParamAccess.tree); @@ -120,6 +129,14 @@ protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager int m = pManager.AddMeshParameter("TempMeshes", "TM", "planes", GH_ParamAccess.list); pManager.HideParameter(m); + + int co = pManager.AddColourParameter("Colors", "Colors", "Colors", GH_ParamAccess.tree); + pManager[co].DataMapping = GH_DataMapping.Flatten; + + int va = pManager.AddTextParameter("Values", "Values", "Values", GH_ParamAccess.tree); + pManager[va].DataMapping = GH_DataMapping.Flatten; + + } /// @@ -164,39 +181,45 @@ protected override void SolveInstance(IGH_DataAccess DA) #endregion updateInputs //bool caps = DA.Fetch("Cap"); - var maxColor = DA.Fetch(inputSelecterMax); - var minColor = DA.Fetch(inputSelectorMin); + Color? maxColor = DA.Fetch(i_inputSelecterMax); + Color? minColor = DA.Fetch(i_inputSelectorMin); var allResults = DA.FetchTree("Results"); var grids = DA.FetchList("Grids"); - var range = DA.Fetch("Range"); - var inStepSize = DA.Fetch("StepSize"); - var inSteps = DA.Fetch("Steps"); + //var gradientRange = DA.Fetch("GradientRange"); + //int maxCount = DA.Fetch("MaxCount"); + int maxCount = 200; + //var inStepSize = DA.Fetch("StepSize"); + //var inSteps = DA.Fetch("Steps"); + InputSelector inputSelector = DA.Fetch("_Section Type"); - if (allResults.Branches.Count != grids.Count) - throw new Exception("Grid count doesnt match results"); + double globalMin = double.MaxValue; + double globalMax = double.MinValue; - if (!caps) + for (int g = 0; g < grids.Count; g++) { - this.Params.Input[inputSelectorMin].NickName = "-"; - this.Params.Input[inputSelectorMin].Name = "-"; - this.Params.Input[inputSelecterMax].NickName = "-"; - this.Params.Input[inputSelecterMax].Name = "-"; + globalMin = Math.Min(globalMin, ((List)allResults.get_Branch(g)).Select(r => r.Value).Min()); + globalMax = Math.Max(globalMax, ((List)allResults.get_Branch(g)).Select(r => r.Value).Max()); } - else + + + if (inputSelector == null) { - this.Params.Input[inputSelectorMin].NickName = "MinColor"; - this.Params.Input[inputSelectorMin].Name = "MinColor"; - this.Params.Input[inputSelecterMax].NickName = "MaxColor"; - this.Params.Input[inputSelecterMax].Name = "MaxColor"; + inputSelector = new InputSelector(10, globalMin, globalMax); } - var domain = Misc.AutoDomain(range, allResults); + + + if (allResults.Branches.Count != grids.Count) + throw new Exception("Grid count doesnt match results"); + + + //var colorDomain = Misc.AutoDomain(gradientRange, allResults); //Rhino.RhinoApp.WriteLine($"{range} -> {domain[0]} to {domain[1]}"); GH_GradientControl gc; try { - gc = (GH_GradientControl)Params.Input[inputGradient].Sources[0].Attributes.GetTopLevel.DocObject; + gc = (GH_GradientControl)Params.Input[i_inputGradient].Sources[0].Attributes.GetTopLevel.DocObject; } catch (System.ArgumentOutOfRangeException) @@ -209,17 +232,24 @@ protected override void SolveInstance(IGH_DataAccess DA) GradientParser gp = new GradientParser(gc) { - Cap = caps, - AboveMax = maxColor, - BelowMin = minColor, - Min = domain[0], - Max = domain[1], - Reverse = Params.Input[inputGradient].Reverse + //Cap = caps, + AboveMax = maxColor == default(Color) ? null : maxColor, + BelowMin = minColor == default(Color) ? null : minColor, + //Min = domain[0], + //Max = domain[1], + Reverse = Params.Input[i_inputGradient].Reverse }; - //Rhino.RhinoApp.WriteLine($"Probing {domain[0]} to the value of {gp.GetColors(new List { domain[0] })[0]}"); - //Rhino.RhinoApp.WriteLine($"Probing {domain[1]} to the value of {gp.GetColors(new List { domain[1] })[0]}"); + + + + IDictionary colorDescriptions = new Dictionary(); + IDictionary colorPaths = new Dictionary(); + + + + #region coloredMesh var outMeshes = new List(); @@ -255,65 +285,31 @@ protected override void SolveInstance(IGH_DataAccess DA) } //Outputs - GH_Structure layeredMeshes = new GH_Structure(); - List tempMeshes = new List(); + GH_Structure oLayeredMeshes = new GH_Structure(); + List previewMeshes = new List(); List outPlanes = new List(); GH_Structure outCurves = new GH_Structure(); - const double SCALAR = 1; // don't change. - const float OFFSET = 0.0001f; - - double allMin = double.MaxValue; - double allMax = -double.MaxValue; - - for (int i = 0; i < allResults.Branches.Count; i++) - { - //System.Collections.IList results = allResults.get_Branch(i); - - for (int j = 0; j < allResults[i].Count; j++) - { - double result = allResults[i][j].Value; - if (result < allMin) - allMin = result; - if (result > allMax) - allMax = result; - } - + GH_Structure outValues = new GH_Structure(); + GH_Structure outColors = new GH_Structure(); - } + const double SCALAR = 1; // don't change. - stepSize = inStepSize; - double roundToNearest = 1; - if (inStepSize == 0) // auto - { - //double digits = Math.Round(Math.Log10((domain[1] - domain[0]))) + 1; - //double multiplier = Math.Pow(10, digits); - //stepSize = Math.Log10((domain[1] - domain[0])); - //if (allMax > 1000) - // stepSize = 100; - //else if (allMax > 100) - // stepSize = 10; - //else if (allMax > 10) - // stepSize = 1; - //else - // stepSize = 0.1; - stepSize = Misc.AutoStep(domain, out roundToNearest); // <-- TODO: We can set each slice in exactly the "round to nearest" number. + - } - else if (inStepSize < 0) // fragment + if (((GH_Structure)gc.Params.Input[1].VolatileData)[0][0].Value == 1) { - stepSize = 1 / Math.Abs(inStepSize); + this.AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, "The gradient connected has 1 as max. Is that on purpose? Check the inputs of your gradient component." + + $"\nI suggest you set your max somewhere around {globalMax:0.0}"); } - steps = Convert.ToInt32((domain[1] - domain[0]) / stepSize); - for (int g = 0; g < grids.Count; g++) { //GH_Structure curves = new GH_Structure(); Grid grid = grids[g]; - Mesh meshToCut = grids[g].SimMesh.DuplicateMesh(); + Mesh inputMesh = grids[g].SimMesh.DuplicateMesh(); //Mesh meshToCut = grids[g].SimMesh; List results = ((List)allResults.get_Branch(g)).Select(r => r.Value).ToList(); @@ -324,411 +320,353 @@ protected override void SolveInstance(IGH_DataAccess DA) // ADD CONVERSION TODO: } + inputMesh.Normals.ComputeNormals(); + Vector3f normal = inputMesh.FaceNormals[0]; + Plane basePlane = new Plane(inputMesh.Vertices[0], normal); - //Rhino.RhinoApp.WriteLine($"min = {allMin}, max = {allMax}, steps = {steps}, stepsize = {stepSize}"); + Transform ProjectToBase = Transform.PlanarProjection(basePlane); - if (steps <= 1 || steps > 100) - { - AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"too {(steps < 4 ? "few" : "many")} steps (should be between 1 to 100)"); - AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"min = {allMin}, max = {allMax}, steps = {steps}, stepsize = {stepSize}"); - continue; - } - - if (allMax == allMin) - { - AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"max==min"); - continue; - } - meshToCut.Normals.ComputeNormals(); + Plane cuttingPlane = new Plane(basePlane); - Plane cuttingPlane = new Plane(meshToCut.Vertices[0], meshToCut.FaceNormals[0]); + Mesh meshToCut = CreateMeshToBeCut(SCALAR, inputMesh, results, cuttingPlane); - //var planeOut = new Plane(plane); + previewMeshes.Add(new GH_Mesh(inputMesh)); - var planeBottom = new Plane(cuttingPlane); + MeshingParameters mp = new MeshingParameters(0); - //List belongsToWhichLayer = new List(); + List layeredMeshesThisGrid = new List(); - Vector3f normal = (Vector3f)(cuttingPlane.ZAxis); - //AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"normal x = {normal.X}, Y = {normal.Y}, Z = {normal.Z}"); - planeBottom.Transform(Transform.Translation(-cuttingPlane.ZAxis)); + double valueForSmallAreas = double.MinValue; - //Moving the bottom down - meshToCut.Translate(-normal * OFFSET); + double resultsMin = results.Min(); - //Moving the vertices up - for (int i = 0; i < results.Count; i++) + foreach (var item in inputSelector) { - meshToCut.Vertices[i] += (normal) * (float)SCALAR * (OFFSET + (float)results[i]); + if (resultsMin >= item) + { + valueForSmallAreas = item; + break; + } } - Mesh topMesh = meshToCut.DuplicateMesh(); - - tempMeshes.Add(new GH_Mesh(topMesh)); - - Mesh edgeMesh = new Mesh(); + //Color col = gp.GetColors(new List() { inputSelector.Min.Value })[0]; + Color col = gp.GetColors(new List() { gp.BelowMin.HasValue && inputSelector.Min.Value <= gp.Min ? resultsMin > gp.Min ? valueForSmallAreas : double.MinValue : inputSelector.Min.Value})[0]; - List ptOut = new List(); - Polyline[] edges = meshToCut.GetNakedEdges(); + Polyline[] outlinePolylines = inputMesh.GetNakedEdges(); - double totalLength = 0; + PolylineCurve[] curvesFromOutline = new PolylineCurve[outlinePolylines.Length]; - for (int i = 0; i < edges.Length; i++) + for (int i = 0; i < outlinePolylines.Length; i++) { - totalLength += edges[i].Length; + curvesFromOutline[i] = new PolylineCurve(outlinePolylines[i]); + curvesFromOutline[i].Transform(ProjectToBase); } - Polyline[] edgesProjected = new Polyline[edges.Length]; - Transform p = Transform.PlanarProjection(planeBottom); + Mesh meshFromCurves = GetMeshFromCurves(curvesFromOutline, mp, in col); - for (int i = 0; i < edges.Length; i++) - { - for (int j = 0; j < edges[i].SegmentCount; j++) - { - Mesh msh = new Mesh(); - Point3d[] pts = new Point3d[4]; - - int id = (j == edges[i].SegmentCount - 1) ? 0 : j + 1; + GH_Path startPath = new GH_Path(g, -1); + oLayeredMeshes.Append(new GH_Mesh(meshFromCurves), startPath); - pts[0] = new Point3d(edges[i].X[j], edges[i].Y[j], edges[i].Z[j]); - pts[1] = new Point3d(edges[i].X[id], edges[i].Y[id], edges[i].Z[id]); - pts[2] = new Point3d(pts[1]); - pts[3] = new Point3d(pts[0]); - pts[2].Transform(p); - pts[3].Transform(p); - msh.Vertices.AddVertices(pts); - var fc = new MeshFace(3, 2, 1, 0); - ptOut.AddRange(pts); - msh.Faces.AddFace(fc); + string lessThanKey = gp.BelowMin.HasValue && inputSelector.Min.Value < gp.Min ? $"<{gp.Min:0.0}" : $"<{inputSelector.Min.Value:0.0}"; + if (!colorDescriptions.ContainsKey(lessThanKey) && inputSelector.First() < gp.Min) + { + colorDescriptions.Add(lessThanKey, col); + colorPaths.Add(lessThanKey, -1); - edgeMesh.Append(msh); - } } - meshToCut.Append(edgeMesh); - meshToCut.Weld(Math.PI); - - tempMeshes.Add(new GH_Mesh(meshToCut)); - - //Transform t = Transform.Translation(new Vector3d(0, 0, inStepSize * SCALAR)); - - Vector3f v = normal * (float)(stepSize.RoundTo(roundToNearest) * SCALAR); - - Transform t = Transform.Translation(v); - - //AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Vector v = {v.X}, {v.Y}, {v.Z}, instep = "); - - Mesh meshPerArea = new Mesh(); - MeshingParameters mp = new MeshingParameters(0); - - - //double resultValue = inputMin; - //stepSize = (inputMax - inputMin) / (float)steps; - + ////outColors.Append(new GH_Colour(col), startPath); + ////outValues.Append(new GH_Number(double.MinValue), startPath); + //Mesh[] meshesFromCurves = GetMeshesFromCurves(curvesFromOutline, mp, in col); + //oLayeredMeshes.AppendRange(meshesFromCurves.Select(m => new GH_Mesh(m)), new GH_Path(g, -1)); + int cuttingCount = 0; + double previousValue = 0; - - - - - double currentValue = domain[0]; - int cuttingCount = -1; - - while (currentValue <= domain[1]) + foreach (double currentValue in inputSelector) { - - - - cuttingCount++; - - if (cuttingCount == 0) - currentValue = domain[0]; - - - if (cuttingCount == 1) - { - cuttingPlane.Translate(new Vector3d(0, 0, domain[0].RoundTo(roundToNearest))); - //currentValue = domain[0]; - } - - if (cuttingCount > 0) - currentValue += stepSize; - - - - - if (cuttingCount > 80) + if (cuttingCount > maxCount) { - AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "ERROR CUT THE CUTTINGCOUNT"); + AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Too many steps... I reached {maxCount} and then stopped"); break; } - //Rhino.RhinoApp.WriteLine($"CurrentValue = {currentValue}, cuttingCount = {cuttingCount}"); + if (gp.BelowMin.HasValue && currentValue < gp.Min) + continue; - //var resultValue = (double)cuttingCount / steps * (allMax - allMin) + allMin; + if (currentValue > results.Max()) + break; - //resultValue = (double)cuttingCount / steps * (domain[1] - domain[0]) + domain[0]; - //resultValue = (double)cuttingCount / steps * (domain[1] - domain[0]) + domain[0]; + // Create planes - //var resultValue = currentValue; // new + Vector3f moveUpVector = normal * (float)((currentValue - previousValue) * SCALAR); - //Rhino.RhinoApp.WriteLine($"Cutting {cuttingCount}, {resultValue}, {currentValue}, {allMin} - {allMax}"); - //if (resultValue < domain[0]) - // continue; - //if (resultValue > domain[1]) - // break; + Transform t = Transform.Translation(moveUpVector); + GH_Path path = new GH_Path(g, cuttingCount); + cuttingPlane.Transform(t); - Polyline[] pl = Rhino.Geometry.Intersect.Intersection.MeshPlane(meshToCut, cuttingPlane); outPlanes.Add(new GH_Plane(cuttingPlane)); - if (pl == null) - { - break; - } - Color col = gp.GetColors(new List() { cuttingCount == 0 ? double.MinValue : currentValue.RoundTo(roundToNearest) })[0]; - //Rhino.RhinoApp.WriteLine($"Probing value {currentValue} to {col}"); + // Create boundary intersected curves + + Curve[] intersectedCurves = GetIntersectedCurves(inputMesh, cuttingPlane); - //Mesh meshPerCut = new Mesh(); - GH_Path path = new GH_Path(g, cuttingCount); - if (pl.Length > 0) + if (intersectedCurves != null) { + outCurves.AppendRange(intersectedCurves.Select(c => new GH_Curve(c.DuplicateCurve())), path); - List curves = new List(); - for (int j = 0; j < pl.Length; j++) + foreach (var curve in intersectedCurves) { + curve.Transform(ProjectToBase); + } - Curve curve = new PolylineCurve(pl[j]); - if (cuttingCount <= 0) - { - curve.Translate(normal * (float)(domain[0] - stepSize)); - } + // Create meshes + col = gp.GetColors(new List() { currentValue })[0]; - curve.Translate(-normal * (float)(currentValue * 0.95 - stepSize)); // was 0.95 nice + - //curve.Translate(-normal * (float)allMin + normal * (float)(cuttingCount * Units.ConvertFromMeter(0.01))); + meshFromCurves = GetMeshFromCurves(intersectedCurves, mp, in col); + meshFromCurves.Transform(Transform.Translation(0, 0, (cuttingCount + 1) * Rhino.RhinoDoc.ActiveDoc.ModelAbsoluteTolerance * 12.0)); - curves.Add(curve); // to create brep later - - outCurves.Append(new GH_Curve(curve), path); // for output - } - Brep[] breps2 = Brep.CreatePlanarBreps(curves, Units.ConvertFromMeter(0.001)); - for (int j = 0; j < breps2.Length; j++) + if (meshFromCurves != null) { - Mesh[] mesh2 = Mesh.CreateFromBrep(breps2[j], mp); - for (int k = 0; k < mesh2.Length; k++) + //oLayeredMeshes.AppendRange(meshesFromCurves.Select(m => new GH_Mesh(m)), path); + oLayeredMeshes.Append(new GH_Mesh(meshFromCurves), path); + string key = currentValue >= gp.Max.Value ? $">{currentValue:0.0}" : $"{currentValue:0.0}"; + if (!colorDescriptions.ContainsKey(key)) { - mesh2[k].VertexColors.CreateMonotoneMesh(col); + colorDescriptions.Add(key, col); + colorPaths.Add(key, cuttingCount); + + } - //meshPerCut.Append(mesh2[k]); - layeredMeshes.Append(new GH_Mesh(mesh2[k]), path); - } + } + + if (currentValue >= gp.Max.Value) + break; } - //meshPerCut.VertexColors.CreateMonotoneMesh(col); - if (cuttingCount > 0) - cuttingPlane.Transform(t); - } - //layeredMeshes.Append(new GH_Mesh(meshPerArea), new GH_Path(g, ); - } - //for (int j = 0; j < pl.Length; j++) - //{ - // Curve curve = pl[j].ToNurbsCurve(); - // GH_Path path = new GH_Path(g, cuttingCount); - // outCurves.Append(new GH_Curve(curve), path); + previousValue = currentValue; - // Brep[] breps = Brep.CreatePlanarBreps(curve, Units.ConvertFromMeter(0.001)); + cuttingCount++; + } - // if (breps == null) - // continue; - // Brep brep = breps[0]; - // var area = AreaMassProperties.Compute(brep); - // if (area.Area > maxSize) - // { - // maxSize = area.Area; - // outerIndex = j; - // } - //} - - //boundaryEdge = pl[outerIndex]; + } + foreach (KeyValuePair valuePair in colorDescriptions) + { + GH_Path path = new GH_Path(colorPaths[valuePair.Key]); + - //for (int j = 0; j < pl.Length; j++) - //{ - // if (j != outerIndex) - // holes.Add(pl[j].ToNurbsCurve()); - //} + outColors.Append(new GH_Colour(valuePair.Value), path); + outValues.Append(new GH_String(valuePair.Key), path); + } - //Mesh mesh = null; - //if (boundaryEdge.IsClosed) - //{ - // mesh = Mesh.CreatePatch(boundaryEdge, Math.PI / 2.0, null, holes, null, null, false, 0); - //} - //else - //{ - // AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, $"Curve is not closed"); - //} + DA.SetDataTree(1, oLayeredMeshes); + DA.SetDataTree(2, outCurves); + DA.SetDataList("Planes", outPlanes); + DA.SetDataList("TempMeshes", previewMeshes); + DA.SetDataTree(6, outValues); + DA.SetDataTree(5, outColors); + #endregion layeredMesh + } - //outPlanes.Add(new GH_Plane(new Plane(cuttingPlane))); + private Mesh GetMeshFromCurves(Curve[] intersectedCurves, MeshingParameters mp, in Color col) + { + if (intersectedCurves == null || intersectedCurves.Length == 0) + return null; - //int curvesCount = pl.Length; - //int[] pointsRanges = new int[curvesCount]; - //Point3d[][] pts = new Point3d[curvesCount][]; + Brep[] brepsFromCurves = Brep.CreatePlanarBreps(intersectedCurves, Units.ConvertFromMeter(0.001)); - //for (int j = 0; j < pl.Length; j++) - //{ - // //Mesh mesh = GreenScenario.MeshUtil.CreateMeshWithHoles(pl); - // //Mesh mesh = Mesh.CreateFromTessellation(points, pl, Plane.WorldXY, false); - // //var mesh = Mesh.CreateFromClosedPolyline(pl[j]); + if (brepsFromCurves == null) + return null; + Mesh[] meshFromBreps = new Mesh[brepsFromCurves.Length]; - // if (mesh == null) - // continue; + for (int j = 0; j < brepsFromCurves.Length; j++) + { + Mesh outMesh = new Mesh(); + Mesh[] meshFromBrep = Mesh.CreateFromBrep(brepsFromCurves[j], mp); - // //outCurves.Append(new GH_Curve(pl[j].ToNurbsCurve())); + for (int k = 0; k < meshFromBrep.Length; k++) + { + meshFromBrep[k].VertexColors.CreateMonotoneMesh(col); - // //List colorList = new List(); + } - // ////for (int i = 0; i < mesh.Faces.Count; i++) - // ////{ - // //// colorList.Add(col); - // //// colorList.Add(col); - // //// colorList.Add(col); - // //// if (mesh.Faces[i].IsQuad) - // //// colorList.Add(col); - // ////} + for (int i = 0; i < meshFromBrep.Length; i++) + { + outMesh.Append(meshFromBrep[i]); + } - // //for (int i = 0; i < mesh.Vertices.Count; i++) - // //{ - // // colorList.Add(col); - // //} + meshFromBreps[j] = outMesh; + } + return meshFromBreps[0]; + } - // ////mesh.VertexColors.SetColors(colorList.ToArray()); - // //AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, $"Vertices = {mesh.Vertices.Count}, colors = {mesh.VertexColors.Count}"); + private static Curve[] GetIntersectedCurves(Mesh ínputMesh, in Plane cuttingPlane) + { + Polyline[] intersectedPolylines = Rhino.Geometry.Intersect.Intersection.MeshPlane(ínputMesh, cuttingPlane); - // // mesh.VertexColors.CreateMonotoneMesh(col); - // //mesh.Translate(-normal * inStepSize * (float)SCALAR * cuttingCount * 0.90f); + if (intersectedPolylines != null && intersectedPolylines.Length > 0) + { + Curve[] curves2 = new Curve[intersectedPolylines.Length]; - // // meshPerArea.Append(mesh); + for (int j = 0; j < intersectedPolylines.Length; j++) + { + curves2[j] = new PolylineCurve(intersectedPolylines[j]); + } + return curves2; + } - // // we don't have more heights to cut off. - // //if (brep == null) - // // continue; + return null; + } - // //for (int i = 0; i < brep.Length; i++) - // //{ - // // belongsToWhichLayer.Add(count); + /// + /// Returns the mesh extruded + /// + /// + /// + /// + /// + /// + /// + private static Mesh CreateMeshToBeCut(in double SCALAR, Mesh baseMesh, List results, Plane cuttingPlane) + { - // //} - // //pts.Add(polylinecrv.PointAtStart); + var planeBottomToProjectTo = new Plane(cuttingPlane); + var normal = (Vector3f)(cuttingPlane.ZAxis); - //} + planeBottomToProjectTo.Transform(Transform.Translation(-cuttingPlane.ZAxis + cuttingPlane.ZAxis * results.Min())); + ////Moving the bottom "one" down + ////baseMesh.Translate(-normal * OFFSET); - // By now curves are moved to different elevations. - //crvs = crvs; + //Moving the vertices up + for (int i = 0; i < results.Count; i++) + { + baseMesh.Vertices[i] += normal * (float)(SCALAR * results[i]); + } + //Mesh topMesh = meshToCut.DuplicateMesh(); - //Rhino.RhinoApp.WriteLine("adding a mesh"); + Mesh edgeMesh = new Mesh(); + Polyline[] edges = baseMesh.GetNakedEdges(); - //oNumbers = outNumbers; + Transform projectTransformation = Transform.PlanarProjection(planeBottomToProjectTo); + // Make the edges + for (int i = 0; i < edges.Length; i++) + { + for (int j = 0; j < edges[i].SegmentCount; j++) + { + Mesh msh = new Mesh(); + Point3d[] pts = new Point3d[4]; - //B = breps; - //meshOut = mesh; + int id = (j == edges[i].SegmentCount - 1) ? 0 : j + 1; + pts[0] = new Point3d(edges[i].X[j], edges[i].Y[j], edges[i].Z[j]); + pts[1] = new Point3d(edges[i].X[id], edges[i].Y[id], edges[i].Z[id]); + pts[2] = new Point3d(pts[1]); + pts[3] = new Point3d(pts[0]); + pts[2].Transform(projectTransformation); + pts[3].Transform(projectTransformation); - Message = $"Cap = {(this.caps ? "on" : "off")} | Steps = {steps} | Step = {stepSize:0.0}"; + msh.Vertices.AddVertices(pts); + var fc = new MeshFace(3, 2, 1, 0); - DA.SetDataTree(1, layeredMeshes); - DA.SetDataTree(2, outCurves); - DA.SetDataList("Planes", outPlanes); - DA.SetDataList("TempMeshes", tempMeshes); + msh.Faces.AddFace(fc); - #endregion layeredMesh + edgeMesh.Append(msh); + } + } + baseMesh.Append(edgeMesh); + baseMesh.Weld(Math.PI); // weld simplifies the mesh, although the render looks more jagged. + return baseMesh; + //return normal; } + //public override bool Read(GH_IO.Serialization.GH_IReader reader) + //{ + // //targetPanelComponentGuid = reader.GetGuid("targetPanelComponentGuid"); - public override bool Read(GH_IO.Serialization.GH_IReader reader) - { - //targetPanelComponentGuid = reader.GetGuid("targetPanelComponentGuid"); - - caps = reader.GetBoolean("caps"); + // caps = reader.GetBoolean("caps"); - return base.Read(reader); - } + // return base.Read(reader); + //} - public override bool Write(GH_IO.Serialization.GH_IWriter writer) - { - //writer.SetGuid("targetPanelComponentGuid", targetPanelComponentGuid); + //public override bool Write(GH_IO.Serialization.GH_IWriter writer) + //{ + // //writer.SetGuid("targetPanelComponentGuid", targetPanelComponentGuid); - writer.SetBoolean("caps", caps); + // writer.SetBoolean("caps", caps); - return base.Write(writer); - } + // return base.Write(writer); + //} //public void UpdateMessage() @@ -737,38 +675,38 @@ public override bool Write(GH_IO.Serialization.GH_IWriter writer) //} - private void OnMenu(object sender, EventArgs e) - { - //Rhino.RhinoApp.WriteLine($"e is {e}, id is {id}"); - //menu.Items[id].Visible = !menu.Items[id].Visible; - //menu.Items[id].Name = "on"; - // menuItemCaps.Name = caps ? "Disable cap" : "Enable cap"; - caps = !caps; - //Rhino.RhinoApp.WriteLine($"caps is now {(caps ? "on" : "off")}"); - this.ExpireSolution(true); - //UpdateMessage(); + //private void OnMenu(object sender, EventArgs e) + //{ + // //Rhino.RhinoApp.WriteLine($"e is {e}, id is {id}"); + // //menu.Items[id].Visible = !menu.Items[id].Visible; + // //menu.Items[id].Name = "on"; + // // menuItemCaps.Name = caps ? "Disable cap" : "Enable cap"; + // caps = !caps; + // //Rhino.RhinoApp.WriteLine($"caps is now {(caps ? "on" : "off")}"); + // this.ExpireSolution(true); + // //UpdateMessage(); - } + //} - public override void AppendAdditionalMenuItems(ToolStripDropDown menu) - { - base.AppendAdditionalMenuItems(menu); - Menu_AppendItem(menu, caps ? "Disable cap" : "Enable cap", OnMenu); + //public override void AppendAdditionalMenuItems(ToolStripDropDown menu) + //{ + // base.AppendAdditionalMenuItems(menu); + // Menu_AppendItem(menu, caps ? "Disable cap" : "Enable cap", OnMenu); - //Menu_AppendGenericMenuItem(menu, "First item"); - //Menu_AppendGenericMenuItem(menu, "Second item"); - //Menu_AppendGenericMenuItem(menu, "Third item"); - //Menu_AppendSeparator(menu); - //Menu_AppendGenericMenuItem(menu, "Fourth item"); - //Menu_AppendGenericMenuItem(menu, "Fifth item"); - //Menu_AppendGenericMenuItem(menu, "Sixth item"); - } + // //Menu_AppendGenericMenuItem(menu, "First item"); + // //Menu_AppendGenericMenuItem(menu, "Second item"); + // //Menu_AppendGenericMenuItem(menu, "Third item"); + // //Menu_AppendSeparator(menu); + // //Menu_AppendGenericMenuItem(menu, "Fourth item"); + // //Menu_AppendGenericMenuItem(menu, "Fifth item"); + // //Menu_AppendGenericMenuItem(menu, "Sixth item"); + //} diff --git a/Grasshopper_Doodles_Public/GH_SectionType_ List.cs b/Grasshopper_Doodles_Public/GH_SectionType_ List.cs new file mode 100644 index 0000000..80b8424 --- /dev/null +++ b/Grasshopper_Doodles_Public/GH_SectionType_ List.cs @@ -0,0 +1,73 @@ +using Grasshopper.Kernel; +using Rhino.Geometry; +using System; +using System.Collections.Generic; + +namespace Grasshopper_Doodles_Public +{ + public class GH_SectionType_List : GH_Component + { + /// + /// Initializes a new instance of the GH_SectionType_Range class. + /// + public GH_SectionType_List() + : base("GH_SectionType_List", "Section_List", + "Description", + Constants.GH_TAB, Constants.GH_GENERIC_SUBTAB) + { + } + + /// + /// Registers all the input parameters for this component. + /// + protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) + { + pManager.AddNumberParameter("steps", "steps", "manually input steps ", GH_ParamAccess.list); + + + } + + /// + /// Registers all the output parameters for this component. + /// + protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) + { + pManager.AddGenericParameter("Input Selector", "Input Selector", "Input Selector", GH_ParamAccess.item); + } + + /// + /// This is the method that actually does the work. + /// + /// The DA object is used to retrieve from inputs and store in outputs. + protected override void SolveInstance(IGH_DataAccess DA) + { + List manuallySteps = DA.FetchList("steps"); + + + InputSelector inputSelector = new InputSelector(manuallySteps); + + DA.SetData(0, inputSelector); + } + + /// + /// Provides an Icon for the component. + /// + protected override System.Drawing.Bitmap Icon + { + get + { + //You can add image files to your project resources and access them like this: + // return Resources.IconForThisComponent; + return null; + } + } + + /// + /// Gets the unique ID for this component. Do not change this ID after release. + /// + public override Guid ComponentGuid + { + get { return new Guid("53a71289-6178-48f2-bb2d-68362245dd08"); } + } + } +} \ No newline at end of file diff --git a/Grasshopper_Doodles_Public/GH_SectionType_Inverval.cs b/Grasshopper_Doodles_Public/GH_SectionType_Inverval.cs new file mode 100644 index 0000000..ffb0953 --- /dev/null +++ b/Grasshopper_Doodles_Public/GH_SectionType_Inverval.cs @@ -0,0 +1,79 @@ +using Grasshopper.Kernel; +using Rhino.Geometry; +using System; +using System.Collections.Generic; + +namespace Grasshopper_Doodles_Public +{ + public class GH_SectionType_Interval : GH_Component + { + /// + /// Initializes a new instance of the GH_SectionType_Range class. + /// + public GH_SectionType_Interval() + : base("GH_SectionType_Interval", "Section_Interval", + "Description", + Constants.GH_TAB, Constants.GH_GENERIC_SUBTAB) + { + } + + /// + /// Registers all the input parameters for this component. + /// + protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) + { + pManager.AddNumberParameter("From", "From", "From", GH_ParamAccess.item); + pManager.AddNumberParameter("To", "To", "To", GH_ParamAccess.item); + pManager.AddNumberParameter("StepSize", "StepSize", "StepSize", GH_ParamAccess.item); + + for (int i = 0; i < pManager.ParamCount - 1; i++) + { + pManager[i].Optional = true; + } + } + + /// + /// Registers all the output parameters for this component. + /// + protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) + { + pManager.AddGenericParameter("Input Selector", "Input Selector", "Input Selector", GH_ParamAccess.item); + } + + /// + /// This is the method that actually does the work. + /// + /// The DA object is used to retrieve from inputs and store in outputs. + protected override void SolveInstance(IGH_DataAccess DA) + { + double? from = DA.Fetch("From"); + double? to = DA.Fetch("To"); + double stepSize = DA.Fetch("StepSize"); + + InputSelector inputSelector = new InputSelector(stepSize, from, to); + + DA.SetData(0, inputSelector); + } + + /// + /// Provides an Icon for the component. + /// + protected override System.Drawing.Bitmap Icon + { + get + { + //You can add image files to your project resources and access them like this: + // return Resources.IconForThisComponent; + return null; + } + } + + /// + /// Gets the unique ID for this component. Do not change this ID after release. + /// + public override Guid ComponentGuid + { + get { return new Guid("53a71289-6178-48f2-be2d-68362245dd08"); } + } + } +} \ No newline at end of file diff --git a/Grasshopper_Doodles_Public/GH_SectionType_Range.cs b/Grasshopper_Doodles_Public/GH_SectionType_Range.cs new file mode 100644 index 0000000..6ce6e3d --- /dev/null +++ b/Grasshopper_Doodles_Public/GH_SectionType_Range.cs @@ -0,0 +1,79 @@ +using Grasshopper.Kernel; +using Rhino.Geometry; +using System; +using System.Collections.Generic; + +namespace Grasshopper_Doodles_Public +{ + public class GH_SectionType_Range : GH_Component + { + /// + /// Initializes a new instance of the GH_SectionType_Range class. + /// + public GH_SectionType_Range() + : base("GH_SectionType_Range", "Section_Range", + "Description", + Constants.GH_TAB, Constants.GH_GENERIC_SUBTAB) + { + } + + /// + /// Registers all the input parameters for this component. + /// + protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) + { + pManager.AddNumberParameter("From", "From", "From", GH_ParamAccess.item); + pManager.AddNumberParameter("To", "To", "To", GH_ParamAccess.item); + pManager.AddIntegerParameter("Steps", "Steps", "Steps", GH_ParamAccess.item); + + for (int i = 0; i < pManager.ParamCount - 1; i++) + { + pManager[i].Optional = true; + } + } + + /// + /// Registers all the output parameters for this component. + /// + protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) + { + pManager.AddGenericParameter("Input Selector", "Input Selector", "Input Selector", GH_ParamAccess.item); + } + + /// + /// This is the method that actually does the work. + /// + /// The DA object is used to retrieve from inputs and store in outputs. + protected override void SolveInstance(IGH_DataAccess DA) + { + double? from = DA.Fetch("From"); + double? to = DA.Fetch("To"); + int steps = DA.Fetch("Steps"); + + InputSelector inputSelector = new InputSelector(steps, from, to); + + DA.SetData(0, inputSelector); + } + + /// + /// Provides an Icon for the component. + /// + protected override System.Drawing.Bitmap Icon + { + get + { + //You can add image files to your project resources and access them like this: + // return Resources.IconForThisComponent; + return null; + } + } + + /// + /// Gets the unique ID for this component. Do not change this ID after release. + /// + public override Guid ComponentGuid + { + get { return new Guid("53a7e289-6178-48f2-be6d-68362245dd08"); } + } + } +} \ No newline at end of file diff --git a/Grasshopper_Doodles_Public/GradientParser.cs b/Grasshopper_Doodles_Public/GradientParser.cs index 8ddf264..344da29 100644 --- a/Grasshopper_Doodles_Public/GradientParser.cs +++ b/Grasshopper_Doodles_Public/GradientParser.cs @@ -1,5 +1,6 @@ using Grasshopper.GUI.Gradient; using Grasshopper.Kernel; +using Grasshopper.Kernel.Data; using Grasshopper.Kernel.Special; using Grasshopper.Kernel.Types; using System; @@ -18,9 +19,9 @@ class GradientParser readonly Grasshopper.GUI.Gradient.GH_Gradient Gradient = new Grasshopper.GUI.Gradient.GH_Gradient(); public double? Max { get; set; } = null; public double? Min { get; set; } = null; - public Color AboveMax { get; set; } - public Color BelowMin { get; set; } - public bool Cap { get; set; } + public Color? AboveMax { get; set; } + public Color? BelowMin { get; set; } + //public bool Cap { get; set; } public bool Reverse { get; set; } = false; public GradientParser(GH_GradientControl gradientControl = null) @@ -59,6 +60,36 @@ public GradientParser(GH_GradientControl gradientControl = null) return; } + + GH_Structure gradientFirstInput = (GH_Structure)gradientControl.Params.Input[0].VolatileData; + + Min = gradientFirstInput[0][0].Value; + + GH_Structure gradientSecondInput = (GH_Structure)gradientControl.Params.Input[1].VolatileData; + + Max = gradientSecondInput[0][0].Value; + + + + + //GH_PersistentParam minCast = (GH_PersistentParam)gradientControl.Params.Input[0].VolatileData.AllData(false).First(); + + //foreach (var item in minCast.VolatileData.AllData(false)) + //{ + // bool casted = item.CastTo(out GH_Number number); + // if (casted) Min = number.Value; + //} + + //GH_PersistentParam maxCast = (GH_PersistentParam)gradientControl.Params.Input[1].VolatileData.AllData(false).First(); + + //foreach (var item in maxCast.VolatileData.AllData(false)) + //{ + // bool casted = item.CastTo(out GH_Number number); + // if (casted) Max = number.Value; + //} + + + bool isLinear = Gradient.Linear; bool isLocked = Gradient.Locked; int gripCount = Gradient.GripCount; @@ -114,65 +145,41 @@ public Color[] GetColors(IList data) if (!Min.HasValue || !Max.HasValue) throw new Exception("Min or Max wasnt set for the GradientParser. Please do that before using me"); - //int k = 1; - if (Reverse) + + + + for (int i = 0; i < data.Count; i++) { - for (int i = 0; i < data.Count; i++) + double lookupValue = (data[i] - Min.Value) / (Max.Value - Min.Value); + if (Reverse) + lookupValue = 1 - lookupValue; + + + colors[i] = Gradient.ColourAt(lookupValue); + + + if (data[i] < Min) { - if (data[i] >= Min.Value && data[i] <= Max.Value) - colors[i] = Gradient.ColourAt(1 - (data[i] - Min.Value) / (Max.Value - Min.Value)); - else if (data[i] < Min) - { - //Rhino.RhinoApp.WriteLine($"data {data[i]} is below {Min}"); - if (Cap) - colors[i] = BelowMin; - else - colors[i] = Gradient.ColourAt(0); - } - - else - { - //Rhino.RhinoApp.WriteLine($"data {data[i]} is above {Max}"); - if (Cap) - colors[i] = AboveMax; - else - colors[i] = Gradient.ColourAt(1); - - } + + colors[i] = BelowMin ?? Gradient.ColourAt(Reverse? 1 : 0); } - } - else - { - for (int i = 0; i < data.Count; i++) + + if (data[i] > Max) { - if (!Cap || (Cap && (data[i] >= Min.Value && data[i] <= Max.Value))) - colors[i] = Gradient.ColourAt((data[i] - Min.Value) / (Max.Value - Min.Value)); - else if (data[i] < Min) - { - //Rhino.RhinoApp.WriteLine($"data {data[i]} is below {Min}"); - if (Cap) - colors[i] = BelowMin; - else - colors[i] = Gradient.ColourAt(0); - } - - else - { - //Rhino.RhinoApp.WriteLine($"data {data[i]} is above {Max}"); - if (Cap) - colors[i] = AboveMax; - else - colors[i] = Gradient.ColourAt(1); - - } + + + colors[i] = AboveMax ?? Gradient.ColourAt(Reverse ? 0 : 1); + + } } + return colors; } - } +} } diff --git a/Grasshopper_Doodles_Public/Grasshopper_Doodles_Public.csproj b/Grasshopper_Doodles_Public/Grasshopper_Doodles_Public.csproj index 286c316..1ebc639 100644 --- a/Grasshopper_Doodles_Public/Grasshopper_Doodles_Public.csproj +++ b/Grasshopper_Doodles_Public/Grasshopper_Doodles_Public.csproj @@ -63,11 +63,15 @@ + + + + diff --git a/Grasshopper_Doodles_Public/Grid.cs b/Grasshopper_Doodles_Public/Grid.cs index e838f59..8a6a192 100644 --- a/Grasshopper_Doodles_Public/Grid.cs +++ b/Grasshopper_Doodles_Public/Grid.cs @@ -29,6 +29,8 @@ public class Grid internal List Resultss { get; set; } public double GridDist { get; set; } + public string Name { get; set; } = ""; + public bool UseCenters { get; } public string GetNames() @@ -38,7 +40,7 @@ public string GetNames() public override string ToString() { - return $"GraceHopper.Grid ({SimPoints.Count} points)"; + return $"GraceHopper.Grid \"{Name}\" ({SimPoints.Count} points)"; } public List GetResults(string name) @@ -84,14 +86,14 @@ public Grid() } - public Grid(Brep srf, double gridDist, bool useCenters = false) + public Grid(Brep srf, double gridDist, bool useCenters = false, bool goLarge = false) { // https://discourse.mcneel.com/t/mesh-fill-holes-best-strategy/71850/14 Strategies to find inner/outer holes for clipper offset TODO. var bb = srf.GetBoundingBox(false); //Rhino.RhinoApp.WriteLine($"sqrt {Math.Sqrt(bb.Area)} and dist {gridDist}.. divided = {Math.Sqrt(bb.Area) / gridDist}"); - if (bb.Area / (gridDist * gridDist) > 60000) - throw new Exception("too many points?"); + if (bb.Area / (gridDist * gridDist) > 60000 && goLarge) + throw new Exception("too many points? You can set GoLarge to true"); SimMesh = GeneratePoints(srf, gridDist, out List pts, out planes, useCenters); @@ -121,15 +123,15 @@ public Grid(Mesh mesh, bool useCenters = false) UpdateAreas(); } - public Grid(Curve curve, double gridDist, bool useCenters = false) + public Grid(Curve curve, double gridDist, bool useCenters = false, bool goLarge = false) { var srfs = Brep.CreatePlanarBreps(curve, Units.ConvertFromMeter(0.001)); // mesh is faster, but we do breps to automatically sort edge vs hole var bb = srfs[0].GetBoundingBox(false); //Rhino.RhinoApp.WriteLine($"sqrt {Math.Sqrt(bb.Area)} and dist {gridDist}.. divided = {Math.Sqrt(bb.Area) / gridDist}"); - if (Math.Sqrt(bb.Area) / gridDist > 300) - throw new Exception("too many points?"); + if (bb.Area / (gridDist * gridDist) > 60000 && goLarge) + throw new Exception("too many points? You can set GoLarge to true"); //var surface = NurbsSurface.CreateExtrusion(polyline.ToNurbsCurve(), new Rhino.Geometry.Vector3d(99, 0, 0)); diff --git a/Grasshopper_Doodles_Public/InputSelector.cs b/Grasshopper_Doodles_Public/InputSelector.cs new file mode 100644 index 0000000..9e76755 --- /dev/null +++ b/Grasshopper_Doodles_Public/InputSelector.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Grasshopper_Doodles_Public +{ + class InputSelector : IEnumerable + { + public double? StepSize { get; set; } + public int? Steps { get; set; } + public double? Min { get; set; } + public double? Max { get; set; } + public List ManuallySteps { get; set; } + + + public override string ToString() + { + + StringBuilder sb = new StringBuilder($"[InputSelector, {Min} to {Max}]:"); + + + int i = 0; + foreach (var item in this) + { + if (i > 3) + { + sb.Append($"\n... up to {Max}"); + break; + } + sb.Append($"\n· {item:0.00}"); + i++; + + } + + return sb.ToString(); + + + } + + public InputSelector(int steps, double? min = null, double? max = null) + { + Steps = steps; + + if (steps < 2) + throw new Exception("Too few steps. Need at least 3"); + + + if (min.HasValue && max.HasValue && min > max) + { + throw new Exception("min>max in the input selector"); + } + + Min = min; + Max = max; + } + + public InputSelector(List manuallySteps) + { + manuallySteps.Sort(); + ManuallySteps = manuallySteps; + + Min = manuallySteps.Min(); + Max = manuallySteps.Max(); + } + + public InputSelector(double stepSize, double? min = null, double? max = null) + { + StepSize = stepSize; + + if (min.HasValue && max.HasValue && min > max) + { + throw new Exception("min>max in the input selector"); + } + + Min = min; + Max = max; + + + + } + + + /// + /// overrides the Min and Max value based on inputnumbers.min and max + /// + /// + public void SetMinMax(IEnumerable inputNumbers) + { + double min = double.MaxValue; + double max = double.MinValue; + foreach (double value in inputNumbers) + { + if (value < min) + min = value; + + if (value > max) + max = value; + } + Min = min; + Max = max; + } + + public IEnumerator GetEnumerator() + { + + if ((Min == null || Max == null) && ManuallySteps == null) + { + throw new Exception("Need to set min and max first. Parse your numbers into the SetMinMax method"); + } + + + + double span = Max.Value - Min.Value; + + if (ManuallySteps != null && ManuallySteps.Count > 0) + { + foreach (var item in ManuallySteps) + { + yield return item; + } + + } + + + else if (Steps.HasValue && StepSize.HasValue) + { + throw new Exception("Both StepSize and Steps have values. I'm unsure what to use"); + } + + + else if (Steps.HasValue) + { + + for (int i = 0; i < Steps.Value; i++) + { + yield return Min.Value + (double)i / (Steps.Value - 1.0) * span ; + } + + } + + + else if (StepSize.HasValue) + { + double step = Min.Value; + while (step <= Max) + { + yield return step; + step += StepSize.Value; + } + } + + + else + { + throw new Exception("StepSize and Steps were not set"); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } +} From a2d9d318217be9bd3683ee30cf956685fca806f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20S=C3=B8nderskov=20Schaltz?= Date: Thu, 19 Aug 2021 18:37:14 +0200 Subject: [PATCH 2/2] Create Grids_LINK Example.gh --- Examples/Grids_LINK Example.gh | Bin 0 -> 27900 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Examples/Grids_LINK Example.gh diff --git a/Examples/Grids_LINK Example.gh b/Examples/Grids_LINK Example.gh new file mode 100644 index 0000000000000000000000000000000000000000..c30e1e5bacad30089169a8956c2e4172a26ff8a9 GIT binary patch literal 27900 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 Wm4acSY5BeF3PsuT;M+iB|Nj9|l?jCa literal 0 HcmV?d00001