diff --git a/SandWorm.sln b/SandWorm.sln
index 436e6ea..bf93a64 100644
--- a/SandWorm.sln
+++ b/SandWorm.sln
@@ -1,12 +1,10 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.28307.489
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30621.155
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SandWorm", "SandWorm\SandWorm.csproj", "{23B848FB-52C2-4E58-B86F-F69B076045DE}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SandwormBenchmarks", "SandwormBenchmarks\SandwormBenchmarks.csproj", "{8637E765-912C-4964-9BC5-6BB972AF86D6}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -23,14 +21,6 @@ Global
{23B848FB-52C2-4E58-B86F-F69B076045DE}.Release|Any CPU.Build.0 = Release|Any CPU
{23B848FB-52C2-4E58-B86F-F69B076045DE}.Release|x64.ActiveCfg = Release|x64
{23B848FB-52C2-4E58-B86F-F69B076045DE}.Release|x64.Build.0 = Release|x64
- {8637E765-912C-4964-9BC5-6BB972AF86D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8637E765-912C-4964-9BC5-6BB972AF86D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8637E765-912C-4964-9BC5-6BB972AF86D6}.Debug|x64.ActiveCfg = Debug|Any CPU
- {8637E765-912C-4964-9BC5-6BB972AF86D6}.Debug|x64.Build.0 = Debug|Any CPU
- {8637E765-912C-4964-9BC5-6BB972AF86D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8637E765-912C-4964-9BC5-6BB972AF86D6}.Release|Any CPU.Build.0 = Release|Any CPU
- {8637E765-912C-4964-9BC5-6BB972AF86D6}.Release|x64.ActiveCfg = Release|Any CPU
- {8637E765-912C-4964-9BC5-6BB972AF86D6}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SandWorm/Analysis.cs b/SandWorm/Analysis.cs
index 2fc2ad8..09b3a45 100644
--- a/SandWorm/Analysis.cs
+++ b/SandWorm/Analysis.cs
@@ -8,60 +8,6 @@ namespace SandWorm
{
public static class Analysis
{
- /// Abstractions for managing analysis state.
- public static class AnalysisManager
- {
- /// Stories copies of each analysis option and interfaces their use with components.
- public static List options;
-
- static AnalysisManager() // Note that the order of items here determines their menu order
- {
- options = new List
- {
- new Analytics.WaterLevel(), new Analytics.Contours(),
- new Analytics.None(),
- new Analytics.Elevation(), new Analytics.Slope(), new Analytics.Aspect()
- };
- // Default to showing elevation analysis
- options[3].isEnabled = true;
- }
-
- public static List GetEnabledAnalyses()
- {
- return options.FindAll(x => x.isEnabled);
- }
-
- public static MeshColorAnalysis GetEnabledMeshColoring()
- {
- foreach (var enabledOption in GetEnabledAnalyses())
- if (enabledOption.GetType().IsSubclassOf(typeof(MeshColorAnalysis)))
- return enabledOption as MeshColorAnalysis;
- return null; // Shouldn't happen; a mesh coloring option (even no color) is always set
- }
-
- public static List GetEnabledMeshAnalytics()
- {
- var enabledGeometryAnalysis = new List();
- foreach (var enabledOption in GetEnabledAnalyses())
- {
- // Testing inheritance with generics is not going to work; so just check if the option is not a color one
- if (enabledOption.GetType().IsSubclassOf(typeof(MeshGeometryAnalysis)))
- enabledGeometryAnalysis.Add(enabledOption as MeshGeometryAnalysis);
- }
- return enabledGeometryAnalysis;
- }
-
- public static void SetEnabledOptions(ToolStripMenuItem selectedMenuItem)
- {
- var selectedOption = options.Find(x => x.MenuItem == selectedMenuItem);
- if (selectedOption.IsExclusive)
- foreach (var exclusiveOption in options.FindAll(x => x.IsExclusive))
- exclusiveOption.isEnabled =
- selectedOption == exclusiveOption; // Toggle selected item; untoggle other exclusive items
- else
- selectedOption.isEnabled = !selectedOption.isEnabled; // Simple toggle for independent items
- }
- }
public class VisualisationRangeWithColor
{
@@ -118,8 +64,10 @@ public abstract class MeshColorAnalysis : MeshAnalysis
public MeshColorAnalysis(string menuName) : base(menuName, true)
{
} // Note: is mutually exclusive
-
- public abstract void ComputeLookupTableForAnalysis(double sensorElevation);
+
+ // Provide two methods for overloading; one for custom-gradient-using options and one for the others
+ public virtual void ComputeLookupTableForAnalysis(double sensorElevation, double gradientRange) { return; }
+ public virtual void ComputeLookupTableForAnalysis(double sensorElevation, double gradientRange, int paletteOptions = 0) { return; }
public void ComputeLinearRanges(params VisualisationRangeWithColor[] lookUpRanges)
{
diff --git a/SandWorm/Analytics/Aspect.cs b/SandWorm/Analytics/Aspect.cs
index d2fc135..c828c54 100644
--- a/SandWorm/Analytics/Aspect.cs
+++ b/SandWorm/Analytics/Aspect.cs
@@ -20,11 +20,11 @@ private Color GetColorForAspect(short aspectValue)
return lookupTable[aspectValue];
}
- public Color[] GetColorCloudForAnalysis(double[] pixelArray, int width, int height)
+ public Color[] GetColorCloudForAnalysis(double[] pixelArray, int width, int height, double gradientRange)
{
if (lookupTable == null)
{
- ComputeLookupTableForAnalysis(0.0);
+ ComputeLookupTableForAnalysis(0.0, gradientRange);
}
var vertexColors = new Color[pixelArray.Length];
@@ -165,7 +165,7 @@ public Color[] GetColorCloudForAnalysis(double[] pixelArray, int width, int heig
return vertexColors;
}
- public override void ComputeLookupTableForAnalysis(double sensorElevation)
+ public override void ComputeLookupTableForAnalysis(double sensorElevation, double gradientRange)
{
var north = new ColorHSL(0.7, 1, 0.90); // North = Blue
var east = new ColorHSL(0.9, 1, 0.5); // East = Pink
diff --git a/SandWorm/Analytics/ColorPalettes.cs b/SandWorm/Analytics/ColorPalettes.cs
new file mode 100644
index 0000000..971ba93
--- /dev/null
+++ b/SandWorm/Analytics/ColorPalettes.cs
@@ -0,0 +1,124 @@
+using System.Collections.Generic;
+using System.Drawing;
+
+namespace SandWorm
+{
+ public static class ColorPalettes
+ {
+ public static List GenerateColorPalettes(Structs.ColorPalettes palette, List customColors)
+ {
+ List paletteSwatches = new List();
+
+ switch (palette)
+ {
+ case Structs.ColorPalettes.Custom:
+ if (customColors.Count == 0) // No inputs provided; use placeholder
+ {
+ paletteSwatches.Add(Color.FromArgb(122, 122, 122));
+ paletteSwatches.Add(Color.FromArgb(122, 122, 122));
+ }
+
+ for (int i = 0; i < customColors.Count; i++)
+ paletteSwatches.Add(customColors[i]);
+ break;
+
+ case Structs.ColorPalettes.Chile:
+ paletteSwatches.Add(Color.FromArgb(38, 115, 0));
+ paletteSwatches.Add(Color.FromArgb(124, 191, 48));
+ paletteSwatches.Add(Color.FromArgb(255, 247, 52));
+ paletteSwatches.Add(Color.FromArgb(196, 65, 0));
+ paletteSwatches.Add(Color.FromArgb(230, 188, 167));
+ break;
+
+ case Structs.ColorPalettes.CutFill:
+ paletteSwatches.Add(Color.FromArgb(235, 100, 75));
+ paletteSwatches.Add(Color.FromArgb(40, 40, 40));
+ paletteSwatches.Add(Color.FromArgb(164, 206, 101));
+ break;
+
+ case Structs.ColorPalettes.Desert:
+ paletteSwatches.Add(Color.FromArgb(55, 101, 84));
+ paletteSwatches.Add(Color.FromArgb(73, 117, 100));
+ paletteSwatches.Add(Color.FromArgb(172, 196, 160));
+ paletteSwatches.Add(Color.FromArgb(148, 131, 85));
+ paletteSwatches.Add(Color.FromArgb(217, 209, 190));
+ break;
+
+ case Structs.ColorPalettes.Europe:
+ paletteSwatches.Add(Color.FromArgb(36, 121, 36));
+ paletteSwatches.Add(Color.FromArgb(89, 148, 54));
+ paletteSwatches.Add(Color.FromArgb(181, 195, 80));
+ paletteSwatches.Add(Color.FromArgb(208, 191, 94));
+ paletteSwatches.Add(Color.FromArgb(115, 24, 19));
+ break;
+
+ case Structs.ColorPalettes.Greyscale:
+ paletteSwatches.Add(Color.FromArgb(40, 40, 40));
+ paletteSwatches.Add(Color.FromArgb(80, 80, 80));
+ paletteSwatches.Add(Color.FromArgb(120, 120, 120));
+ paletteSwatches.Add(Color.FromArgb(160, 160, 160));
+ paletteSwatches.Add(Color.FromArgb(200, 200, 200));
+ break;
+
+ case Structs.ColorPalettes.Dune:
+ paletteSwatches.Add(Color.FromArgb(80, 80, 80));
+ paletteSwatches.Add(Color.FromArgb(122, 91, 76));
+ paletteSwatches.Add(Color.FromArgb(191, 118, 40));
+ paletteSwatches.Add(Color.FromArgb(240, 173, 50));
+ paletteSwatches.Add(Color.FromArgb(255, 210, 128));
+ break;
+
+ case Structs.ColorPalettes.Ocean:
+ paletteSwatches.Add(Color.FromArgb(47, 34, 58));
+ paletteSwatches.Add(Color.FromArgb(62, 90, 146));
+ paletteSwatches.Add(Color.FromArgb(80, 162, 162));
+ paletteSwatches.Add(Color.FromArgb(152, 218, 164));
+ paletteSwatches.Add(Color.FromArgb(250, 250, 200));
+ break;
+
+ case Structs.ColorPalettes.Turbo:
+ paletteSwatches.Add(Color.FromArgb(48, 18, 59));
+ paletteSwatches.Add(Color.FromArgb(65, 69, 171));
+ paletteSwatches.Add(Color.FromArgb(70,117,237));
+ paletteSwatches.Add(Color.FromArgb(57, 162, 252));
+ paletteSwatches.Add(Color.FromArgb(27, 207, 212));
+ paletteSwatches.Add(Color.FromArgb(36, 236, 166));
+ paletteSwatches.Add(Color.FromArgb(97, 252, 108));
+ paletteSwatches.Add(Color.FromArgb(164, 252, 59));
+ paletteSwatches.Add(Color.FromArgb(209, 232, 52));
+ paletteSwatches.Add(Color.FromArgb(243, 198, 58));
+ paletteSwatches.Add(Color.FromArgb(254, 155, 45));
+ paletteSwatches.Add(Color.FromArgb(243, 99, 21));
+ paletteSwatches.Add(Color.FromArgb(217, 56, 6));
+ paletteSwatches.Add(Color.FromArgb(177, 25, 1));
+ paletteSwatches.Add(Color.FromArgb(122, 4, 2));
+ break;
+
+ case Structs.ColorPalettes.Viridis:
+ paletteSwatches.Add(Color.FromArgb(52, 0, 66));
+ paletteSwatches.Add(Color.FromArgb(55, 8, 85));
+ paletteSwatches.Add(Color.FromArgb(55, 23, 100));
+ paletteSwatches.Add(Color.FromArgb(53, 37, 110));
+ paletteSwatches.Add(Color.FromArgb(48, 52, 117));
+ paletteSwatches.Add(Color.FromArgb(44, 65, 121));
+ paletteSwatches.Add(Color.FromArgb(39, 80, 123));
+ paletteSwatches.Add(Color.FromArgb(36, 93, 123));
+ paletteSwatches.Add(Color.FromArgb(33, 106, 123));
+ paletteSwatches.Add(Color.FromArgb(31, 120, 122));
+ paletteSwatches.Add(Color.FromArgb(30, 134, 120));
+ paletteSwatches.Add(Color.FromArgb(32, 148, 115));
+ paletteSwatches.Add(Color.FromArgb(38, 162, 108));
+ paletteSwatches.Add(Color.FromArgb(52, 178, 98));
+ paletteSwatches.Add(Color.FromArgb(73, 190, 84));
+ paletteSwatches.Add(Color.FromArgb(101, 202, 68));
+ paletteSwatches.Add(Color.FromArgb(132, 212, 50));
+ paletteSwatches.Add(Color.FromArgb(171, 219, 32));
+ paletteSwatches.Add(Color.FromArgb(212, 225, 21));
+ paletteSwatches.Add(Color.FromArgb(252, 229, 30));
+ break;
+ }
+
+ return paletteSwatches;
+ }
+ }
+}
diff --git a/SandWorm/Analytics/Contours.cs b/SandWorm/Analytics/Contours.cs
deleted file mode 100644
index a7f1a29..0000000
--- a/SandWorm/Analytics/Contours.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System.Collections.Generic;
-using Rhino.Geometry;
-
-namespace SandWorm.Analytics
-{
- public class Contours : Analysis.MeshGeometryAnalysis
- {
- public Contours() : base("Show Contour Lines")
- {
- }
-
- public override void GetGeometryForAnalysis(ref List outputGeometry, double contourInterval, Mesh mesh)
- {
- var bounds = mesh.GetBoundingBox(false);
- var originStart = new Point3d(0, 0, bounds.Min.Z);
- var originEnd = new Point3d(0, 0, bounds.Max.Z);
- var contours = Mesh.CreateContourCurves(mesh, originStart, originEnd, contourInterval);
- outputGeometry.AddRange(contours);
- }
- }
-}
\ No newline at end of file
diff --git a/SandWorm/Analytics/ContoursFromPoints.cs b/SandWorm/Analytics/ContoursFromPoints.cs
new file mode 100644
index 0000000..783b0c3
--- /dev/null
+++ b/SandWorm/Analytics/ContoursFromPoints.cs
@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+using Rhino.Geometry;
+
+
+namespace SandWorm.Analytics
+{
+ public static class ContoursFromPoints
+ {
+ public static void GetGeometryForAnalysis(ref List contourLines, Point3d[] points, int threshold,
+ int trimmedWidth, int trimmedHeight, int ContourRoughness)
+ {
+
+ int xd = trimmedWidth;
+ int yd = trimmedHeight;
+
+ var ydd = (int)(yd / ContourRoughness);
+ var xdd = (int)(xd / ContourRoughness);
+
+ ConcurrentBag _contourLines = new ConcurrentBag();
+
+ Parallel.For(1, ydd, y => // Iterate over y dimension
+ {
+ for (int x = 1; x < xdd; x++) // Iterate over x dimension
+ {
+ List intersectionPoints = new List();
+
+ int i = y * ContourRoughness * xd + (x * ContourRoughness);
+ int j = y * ContourRoughness * xd - (ContourRoughness * xd) + (x * ContourRoughness);
+
+ // lower left corner -> j - multiplier
+ // lower right corner -> j
+ // upper right corner-> i
+ // upper left corner -> i - multiplier
+
+ intersectionPoints.AddRange(FindIntersections(points[j - ContourRoughness], points[j], threshold));
+ intersectionPoints.AddRange(FindIntersections(points[j], points[i], threshold));
+ intersectionPoints.AddRange(FindIntersections(points[i], points[i - ContourRoughness], threshold));
+ intersectionPoints.AddRange(FindIntersections(points[i - ContourRoughness], points[j - ContourRoughness], threshold));
+
+ if (intersectionPoints.Count > 0)
+ {
+ Stack vertexStack = new Stack();
+
+ for (int a = 0; a < intersectionPoints.Count; a++)
+ {
+ if (vertexStack.Count == 0 || vertexStack.Peek().W == intersectionPoints[a].W) // Add points to the stack if they have the same direction
+ vertexStack.Push(intersectionPoints[a]);
+ else
+ {
+ Point4d _start = vertexStack.Pop();
+ Point4d _end = intersectionPoints[a];
+
+ if (_start.Z != _end.Z)
+ {
+ if (a < intersectionPoints.Count - 1)
+ {
+ Point4d _next = intersectionPoints[a + 1];
+
+ if (_start.Z == _next.Z && _start.W != _next.W)
+ _end = _next;
+ else if (vertexStack.Count > 0)
+ _start = vertexStack.Pop();
+ else
+ {
+ vertexStack.Push(_start);
+ vertexStack.Push(intersectionPoints[a]);
+ continue;
+ }
+ }
+ }
+ if( _start.Z == _end.Z)
+ _contourLines.Add(new Line(_start.X, _start.Y, _start.Z, _end.X, _end.Y, _end.Z));
+ }
+ }
+ }
+ }
+ }); // Parallel for
+
+ contourLines = new List(_contourLines);
+ }
+
+
+ private static List FindIntersections(Point3d startVertex, Point3d endVertex, int threshold)
+ {
+ List intersections = new List();
+ Point4d _p = new Point4d();
+ Point3d _startVertex = new Point3d(startVertex.X, startVertex.Y, startVertex.Z / SandWormComponent.unitsMultiplier);
+ Point3d _endVertex = new Point3d(endVertex.X, endVertex.Y, endVertex.Z / SandWormComponent.unitsMultiplier);
+ double deltaZ = Math.Abs(_endVertex.Z - _startVertex.Z);
+
+ // Discard points if they have (0,0) coordinates (coming from the WFOV)
+ if ((_startVertex.X == 0 && _startVertex.Y == 0) || (_endVertex.X == 0 && _endVertex.Y == 0))
+ return intersections;
+
+ // Discard points if they don't cross an isocurve
+ if ((int)_startVertex.Z == (int)_endVertex.Z || deltaZ == 0)
+ return intersections;
+
+ double ratio = 0.0;
+
+ for (int a = 1; a <= Math.Ceiling(deltaZ); a++)
+ {
+ if (_startVertex.Z < _endVertex.Z)
+ {
+ _p.Z = Math.Floor(_startVertex.Z) + a;
+ ratio = (_p.Z - _startVertex.Z) / deltaZ;
+ }
+ else
+ {
+ _p.Z = Math.Ceiling(_startVertex.Z) - a;
+ ratio = 1 - ((_p.Z - _endVertex.Z) / deltaZ);
+ _p.W = 1; // Use point weight, to mark that this is an inwards facing point
+ }
+
+ if (_p.Z % threshold == 0) // Only create intersection points if they fall within the user-defined threshold
+ {
+ _p.X = InterpolateCoordinates(_startVertex.X, _endVertex.X, ratio);
+ _p.Y = InterpolateCoordinates(_startVertex.Y, _endVertex.Y, ratio);
+ _p.Z *= SandWormComponent.unitsMultiplier;
+
+ intersections.Add(_p);
+ }
+ }
+ return intersections;
+ }
+
+
+ private static double InterpolateCoordinates(double start, double end, double ratio)
+ {
+ return start + ratio * (end - start);
+ }
+
+ }
+}
diff --git a/SandWorm/Analytics/CutFill.cs b/SandWorm/Analytics/CutFill.cs
new file mode 100644
index 0000000..8ef9f59
--- /dev/null
+++ b/SandWorm/Analytics/CutFill.cs
@@ -0,0 +1,112 @@
+using System.Collections.Generic;
+using System.Drawing;
+using Rhino.Display;
+using Rhino.Geometry;
+
+
+namespace SandWorm.Analytics
+{
+ public class CutFill : Analysis.MeshColorAnalysis
+ {
+ private Structs.ColorPalettes _colorPalette = Structs.ColorPalettes.Europe;
+ List paletteSwatches; // Color values for the given palette
+
+ public CutFill() : base("Visualise Cut & Fill")
+ {
+ }
+
+ private Color GetColorForCutFill(int cutFillValue, double gradientRange)
+ {
+ if (cutFillValue < 0)
+ return lookupTable[0];
+ else if (cutFillValue > 2 * gradientRange) // Since gradientRange defines a span in both negative and positive directions, we multiply it by 2 to get the whole range
+ return lookupTable[lookupTable.Length - 1];
+ else
+ return lookupTable[cutFillValue];
+
+ }
+ public Color[] GetColorCloudForAnalysis(Point3d[] pointCoordinates, double?[] baseMeshElevationPoints, double gradientRange,
+ Structs.ColorPalettes colorPalette, List customColors, ref List stats)
+ {
+ double _cut = 0.0;
+ double _fill = 0.0;
+ double _area = 0.0;
+ double _previousArea = 0.0;
+ double _maxArea = 500; // Arbitrary value to check against
+
+ _colorPalette = colorPalette;
+ if (lookupTable == null)
+ {
+ paletteSwatches = ColorPalettes.GenerateColorPalettes(_colorPalette, customColors);
+ ComputeLookupTableForAnalysis(0.0, gradientRange * 3, paletteSwatches.Count);
+ }
+
+ Color[] vertexColors = new Color[pointCoordinates.Length];
+
+ for (int i = 0; i < baseMeshElevationPoints.Length; i++)
+ {
+ if (baseMeshElevationPoints[i] == null)
+ vertexColors[i] = Color.FromArgb(50, 50, 50); // All pixels which are outside of the provided mesh are marked as dark grey
+ else
+ {
+ double _difference = pointCoordinates[i].Z - (double)baseMeshElevationPoints[i];
+ vertexColors[i] = GetColorForCutFill((int)(_difference + gradientRange), gradientRange); // Add gradientRange to accommodate for negative values from cut operations
+
+ if (i < pointCoordinates.Length - 1) // Assume dX == dY
+ _area = System.Math.Pow(pointCoordinates[i].X - pointCoordinates[i + 1].X, 2) / SandWormComponent.unitsMultiplier / SandWormComponent.unitsMultiplier;
+
+ if (_area > _maxArea) // Make sure that area is correct when a pixel jumps from one side of the table to the other
+ _area = _previousArea;
+ else
+ _previousArea = _area;
+
+ if (_difference > 0)
+ _fill += _difference * _area / SandWormComponent.unitsMultiplier;
+ else
+ _cut += _difference * _area / SandWormComponent.unitsMultiplier;
+ }
+ }
+
+ stats.Add($"Cut Volume, in cubic centimeters:");
+ stats.Add(System.Math.Round(_cut * 0.001).ToString());
+ stats.Add($"Fill Volume, in cubic centimeters:");
+ stats.Add(System.Math.Round(_fill * 0.001).ToString());
+ stats.Add($"Cut/Fill Balance, in cubic centimeters:");
+ stats.Add(System.Math.Round((_cut + _fill) * 0.001).ToString());
+ return vertexColors;
+ }
+
+
+ public override void ComputeLookupTableForAnalysis(double sensorElevation, double gradientRange, int swatchCount)
+ {
+ var elevationRanges = new Analysis.VisualisationRangeWithColor[swatchCount - 1];
+ for (int i = 0; i < swatchCount - 1; i++)
+ {
+ var elevationRange = new Analysis.VisualisationRangeWithColor
+ {
+ ValueSpan = (int)(gradientRange / swatchCount),
+ ColorStart = new ColorHSL(paletteSwatches[i]),
+ ColorEnd = new ColorHSL(paletteSwatches[i + 1])
+ };
+ elevationRanges[i] = elevationRange;
+ }
+
+ ComputeLinearRanges(elevationRanges);
+ }
+
+ public static double?[] MeshToPointArray(Mesh baseMesh, Point3d[] pointCoordinates)
+ {
+ double?[] pointElevationArray = new double?[pointCoordinates.Length];
+
+ for (int i = 0; i < pointCoordinates.Length; i++)
+ {
+ // This needs to be done for each point individually. Providing an array of points would return only the points which were hit, disregarding all remaining ones
+ var projectedPoints = Rhino.Geometry.Intersect.Intersection.ProjectPointsToMeshes(new[] { baseMesh }, new[] { pointCoordinates[i] }, new Vector3d(0, 0, 1), 0.001);
+ if (projectedPoints != null && projectedPoints.Length != 0)
+ pointElevationArray[i] = projectedPoints[0].Z;
+ }
+
+ return pointElevationArray;
+ }
+ }
+}
diff --git a/SandWorm/Analytics/Elevation.cs b/SandWorm/Analytics/Elevation.cs
index 7c318d4..d96750a 100644
--- a/SandWorm/Analytics/Elevation.cs
+++ b/SandWorm/Analytics/Elevation.cs
@@ -1,25 +1,30 @@
-using System;
+using System.Drawing;
using System.Collections.Generic;
-using System.Drawing;
using Rhino.Display;
-using Rhino.Geometry;
+using System;
namespace SandWorm.Analytics
{
public class Elevation : Analysis.MeshColorAnalysis
{
private int _lastSensorElevation; // Keep track of prior values to recalculate only as needed
+ private double _lastGradientRange;
+ private Structs.ColorPalettes _colorPalette = Structs.ColorPalettes.Europe;
+ List paletteSwatches; // Color values for the given palette
public Elevation() : base("Visualise Elevation")
{
}
- public Color[] GetColorCloudForAnalysis(double[] pixelArray, double sensorElevation)
+ public Color[] GetColorCloudForAnalysis(double[] pixelArray, double sensorElevation, double gradientRange,
+ Structs.ColorPalettes colorPalette, List customColors)
{
+ _colorPalette = colorPalette;
var sensorElevationRounded = (int)sensorElevation; // Convert once as it is done often
- if (lookupTable == null || sensorElevationRounded != _lastSensorElevation)
+ if (lookupTable == null || sensorElevationRounded != _lastSensorElevation || gradientRange != _lastGradientRange)
{
- ComputeLookupTableForAnalysis(sensorElevation);
+ paletteSwatches = ColorPalettes.GenerateColorPalettes(_colorPalette, customColors);
+ ComputeLookupTableForAnalysis(sensorElevation, gradientRange, paletteSwatches.Count);
}
// Lookup elevation value in color table
@@ -37,34 +42,24 @@ public Color[] GetColorCloudForAnalysis(double[] pixelArray, double sensorElevat
return vertexColors;
}
- public override void ComputeLookupTableForAnalysis(double sensorElevation)
+ // Given the sensor's height from the table, map between vertical distance intervals and color palette values
+ public override void ComputeLookupTableForAnalysis(double sensorElevation, double gradientRange, int swatchCount)
{
- var sElevationRange = new Analysis.VisualisationRangeWithColor
+ var elevationRanges = new Analysis.VisualisationRangeWithColor[swatchCount - 1];
+ for (int i = 0; i < swatchCount - 1; i++)
{
- ValueSpan = 20,
- ColorStart = new ColorHSL(0.40, 0.35, 0.3), // Dark Green
- ColorEnd = new ColorHSL(0.30, 0.85, 0.4) // Green
- };
- var mElevationRange = new Analysis.VisualisationRangeWithColor
- {
- ValueSpan = 20,
- ColorStart = new ColorHSL(0.30, 0.85, 0.4), // Green
- ColorEnd = new ColorHSL(0.20, 0.85, 0.5) // Yellow
- };
- var lElevationRange = new Analysis.VisualisationRangeWithColor
- {
- ValueSpan = 20,
- ColorStart = new ColorHSL(0.20, 0.85, 0.5), // Yellow
- ColorEnd = new ColorHSL(0.10, 0.85, 0.6) // Orange
- };
- var xlElevationRange = new Analysis.VisualisationRangeWithColor
- {
- ValueSpan = 20,
- ColorStart = new ColorHSL(0.10, 1, 0.6), // Orange
- ColorEnd = new ColorHSL(0.00, 1, 0.7) // Red
- };
- ComputeLinearRanges(sElevationRange, mElevationRange, lElevationRange, xlElevationRange);
+ var elevationRange = new Analysis.VisualisationRangeWithColor
+ {
+ ValueSpan = Math.Max((int)(gradientRange / swatchCount), 1), // Prevent 0 value
+ ColorStart = new ColorHSL(paletteSwatches[i]),
+ ColorEnd = new ColorHSL(paletteSwatches[i+1])
+ };
+ elevationRanges[i] = elevationRange;
+ }
+
+ ComputeLinearRanges(elevationRanges);
_lastSensorElevation = (int)sensorElevation;
+ _lastGradientRange = gradientRange;
}
}
}
\ No newline at end of file
diff --git a/SandWorm/Analytics/FlowLines.cs b/SandWorm/Analytics/FlowLines.cs
new file mode 100644
index 0000000..3af0b49
--- /dev/null
+++ b/SandWorm/Analytics/FlowLines.cs
@@ -0,0 +1,206 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+using Rhino.Geometry;
+
+namespace SandWorm
+{
+ public class FlowLine
+ {
+ private int zDistance = 1;
+ public int Endvertex { get; set; }
+ public Polyline Polyline { get; set; }
+ public int Inactive { get; set; }
+
+ public FlowLine(int startVertex, ref Point3d[] points)
+ {
+ Endvertex = startVertex;
+ Polyline = new Polyline();
+ Inactive = 0;
+ Point3d _pt = new Point3d(points[Endvertex].X, points[Endvertex].Y, points[Endvertex].Z + (zDistance * SandWormComponent.unitsMultiplier)); // Raise the polyline slightly above terrain for better visibility
+ Polyline.Add(_pt);
+ }
+
+ public void Grow(Point3d[] points, int xStride)
+ {
+ int _previousEndvertex = Endvertex;
+ findNextPoint(ref points, xStride);
+ if (Endvertex != _previousEndvertex)
+ {
+ Point3d _pt = new Point3d(points[Endvertex].X, points[Endvertex].Y, points[Endvertex].Z + zDistance * SandWormComponent.unitsMultiplier);
+ Polyline.Add(_pt);
+ Inactive = 0;
+ }
+ else
+ Inactive++;
+ }
+
+ public void Shrink()
+ {
+ Polyline.RemoveAt(0);
+ }
+
+ private void findNextPoint(ref Point3d[] pointArray, int xStride)
+ {
+ double maxDistance = 50 * SandWormComponent.unitsMultiplier; // Necessary for checks around the mesh borders
+
+ int maxSlopeIndex = Endvertex;
+ double maxSlope = 0.0;
+ double _slope = 0.0;
+ double deltaX = 0.0;
+ double deltaY = 0.0;
+
+ int i = Endvertex;
+
+ int _sw = i - xStride - 1;//SW pixel
+ int _s = i - xStride; //S pixel
+ int _se = i - xStride + 1; //SE pixel
+ int _w = i - 1; //W pixel
+ int _e = i + 1; //E pixel
+ int _nw = i + xStride - 1; //NW pixel
+ int _n = i + xStride; //N pixel
+ int _ne = i + xStride + 1; //NE pixel
+
+ if (_sw >= 0)
+ {
+ deltaX = pointArray[i].X - pointArray[_sw].X;
+ deltaY = pointArray[i].Y - pointArray[_sw].Y;
+
+ if (Math.Abs(deltaX) < maxDistance)
+ {
+ _slope = (pointArray[i].Z - pointArray[_sw].Z) / Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+ CheckForMaxSlope(_slope, ref maxSlope, _sw, ref maxSlopeIndex);
+ }
+ }
+
+ if (_s >= 0)
+ {
+ deltaY = pointArray[i].Y - pointArray[_s].Y;
+
+ _slope = (pointArray[i].Z - pointArray[_s].Z) / deltaY;
+ CheckForMaxSlope(_slope, ref maxSlope, _s, ref maxSlopeIndex);
+ }
+
+ if (_se >= 0)
+ {
+ deltaX = pointArray[i].X - pointArray[_se].X;
+ deltaY = pointArray[i].Y - pointArray[_se].Y;
+
+ if (Math.Abs(deltaX) < maxDistance)
+ {
+ _slope = (pointArray[i].Z - pointArray[_se].Z) / Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+ CheckForMaxSlope(_slope, ref maxSlope, _se, ref maxSlopeIndex);
+ }
+ }
+
+ if (_w >= 0)
+ {
+ deltaX = pointArray[i].X - pointArray[_w].X;
+
+ if (Math.Abs(deltaX) < maxDistance)
+ {
+ _slope = (pointArray[i].Z - pointArray[_w].Z) / deltaX;
+ CheckForMaxSlope(_slope, ref maxSlope, _w, ref maxSlopeIndex);
+ }
+ }
+
+ if (_e <= pointArray.Length - 1)
+ {
+ deltaX = pointArray[i].X - pointArray[_e].X;
+
+ if (Math.Abs(deltaX) < maxDistance)
+ {
+ _slope = (pointArray[i].Z - pointArray[_e].Z) / deltaX;
+ CheckForMaxSlope(_slope, ref maxSlope, _e, ref maxSlopeIndex);
+ }
+ }
+
+ if (_nw <= pointArray.Length - 1)
+ {
+ deltaX = pointArray[i].X - pointArray[_nw].X;
+ deltaY = pointArray[i].Y - pointArray[_nw].Y;
+
+ if (Math.Abs(deltaX) < maxDistance)
+ {
+ _slope = (pointArray[i].Z - pointArray[_nw].Z) / Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+ CheckForMaxSlope(_slope, ref maxSlope, _nw, ref maxSlopeIndex);
+ }
+ }
+
+ if (_n <= pointArray.Length - 1)
+ {
+ deltaY = pointArray[i].Y - pointArray[_n].Y;
+
+ _slope = (pointArray[i].Z - pointArray[_n].Z) / deltaY;
+ CheckForMaxSlope(_slope, ref maxSlope, _n, ref maxSlopeIndex);
+ }
+
+ if (_ne <= pointArray.Length - 1)
+ {
+ deltaX = pointArray[i].X - pointArray[_ne].X;
+ deltaY = pointArray[i].Y - pointArray[_ne].Y;
+
+ if (Math.Abs(deltaX) < maxDistance)
+ {
+ _slope = (pointArray[i].Z - pointArray[_ne].Z) / Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+ CheckForMaxSlope(_slope, ref maxSlope, _ne, ref maxSlopeIndex);
+ }
+ }
+
+ if (maxSlope > 0)
+ Endvertex = maxSlopeIndex;
+ }
+
+ private void CheckForMaxSlope(double currentSlope, ref double maxSlope, int currentIndex, ref int maxSlopeIndex)
+ {
+ if (currentSlope > maxSlope)
+ {
+ maxSlope = currentSlope;
+ maxSlopeIndex = currentIndex;
+ }
+ }
+
+ public static void DistributeFlowLines(Point3d[] pointArray, ref List flowLines, int xStride, int yStride, int spacing)
+ {
+ for (int y = 0; y < yStride; y += spacing) // Iterate over y dimension
+ for (int x = spacing; x < xStride; x += spacing) // Iterate over x dimension
+ {
+ int i = y * xStride + x;
+ flowLines.Add(new FlowLine(i, ref pointArray));
+ }
+ }
+
+ public static void DistributeRandomRaindrops(ref Point3d[] pointArray, ref ConcurrentDictionary flowLines, int spacing)
+ {
+ Random random = new Random();
+ int flowLinesCount = pointArray.Length / (spacing * 10); // Arbitrary division by 10 to reduce the amount of flowlines
+ int pointsCount = pointArray.Length - 1;
+
+ for (int i = 0; i < flowLinesCount; i++)
+ {
+ int _index = random.Next(pointsCount);
+ flowLines.TryAdd(_index, new FlowLine(_index, ref pointArray));
+ }
+ }
+
+ public static void GrowAndRemoveFlowlines(Point3d[] pointArray, ConcurrentDictionary flowLines, int xStride, double maxLength)
+ {
+ Parallel.ForEach(flowLines, kvp =>
+ {
+ if (kvp.Value.Polyline.Count > maxLength)
+ kvp.Value.Shrink();
+
+ if (kvp.Value.Inactive < 3) // Only grow if a polyline wasn't idle for more than 3 ticks
+ kvp.Value.Grow(pointArray, xStride);
+ else
+ {
+ if (kvp.Value.Polyline.Count > 3)
+ kvp.Value.Shrink();
+ else
+ flowLines.TryRemove(kvp.Key, out _); // Remove single line segments of stuck polylines
+ }
+ });
+ }
+ }
+}
diff --git a/SandWorm/Analytics/MeshFlow.cs b/SandWorm/Analytics/MeshFlow.cs
new file mode 100644
index 0000000..af35b40
--- /dev/null
+++ b/SandWorm/Analytics/MeshFlow.cs
@@ -0,0 +1,188 @@
+using System;
+using System.Threading.Tasks;
+using Rhino.Geometry;
+
+using ILGPU;
+using ILGPU.Runtime;
+using ILGPU.Runtime.Cuda;
+
+namespace SandWorm.Analytics
+{
+ class MeshFlow
+ {
+ public static double[] waterHead;
+ public static Point3d[] waterElevationPoints;
+ public static double[] runoffCoefficients;
+ private static double rain = 1;
+ public static int[] flowDirections;
+ private static double[] waterAmounts;
+
+ private const double flowVelocity = 2; // Set max amount of water cells can exchange with each other in one iteration
+
+ // GPU variables
+ public static Context context;
+ public static Accelerator accelerator;
+ public static MemoryBuffer1D _d_flowDirections;
+ public static MemoryBuffer1D _d_waterAmounts;
+ public static MemoryBuffer1D _d_elevationsArray;
+ public static MemoryBuffer1D _d_waterHead;
+ private static Action, ArrayView, int, int, ArrayView, ArrayView, double> loadedKernel;
+
+ public static void CalculateWaterHeadArray(Point3d[] pointArray, double[] elevationsArray, int xStride, int yStride, bool simulateFlood)
+ {
+ #region Initialize
+
+ if (waterHead == null)
+ {
+ waterHead = new double[elevationsArray.Length];
+ waterAmounts = new double[elevationsArray.Length];
+ flowDirections = new int[elevationsArray.Length];
+
+ waterElevationPoints = new Point3d[elevationsArray.Length];
+ Parallel.For(0, elevationsArray.Length, i =>
+ {
+ waterElevationPoints[i].X = pointArray[i].X;
+ waterElevationPoints[i].Y = pointArray[i].Y;
+ });
+
+ runoffCoefficients = new double[elevationsArray.Length];
+ for (int i = 0; i < runoffCoefficients.Length; i++) // Populate array with arbitrary runoff values. Ideally, this should be provided by users through UI
+ runoffCoefficients[i] = 0.8;
+ }
+
+ if (context == null || context.IsDisposed)
+ {
+ context = Context.Create(builder => builder.AllAccelerators());
+
+ if (context.GetCudaDevices().Count > 0) // prefer NVIDIA
+ accelerator = context.GetCudaDevice(0).CreateAccelerator(context);
+ else // let ILGPU decide
+ accelerator = context.GetPreferredDevice(false).CreateAccelerator(context);
+
+ // allocate memory buffers on the GPU
+ _d_elevationsArray = accelerator.Allocate1D(elevationsArray);
+ _d_waterHead = accelerator.Allocate1D(waterHead);
+
+ _d_flowDirections = accelerator.Allocate1D(flowDirections);
+ _d_waterAmounts = accelerator.Allocate1D(waterAmounts);
+
+ // precompile the kernel
+ loadedKernel = accelerator.LoadAutoGroupedStreamKernel, ArrayView, int, int, ArrayView, ArrayView, double>(FlowDirectionKernel);
+ }
+ #endregion
+
+ if (simulateFlood) // Distribute precipitation equally
+ Parallel.For(0, waterHead.Length, i =>
+ {
+ waterHead[i] += rain * SandWormComponent.unitsMultiplier;
+ });
+
+ DrainBorders(xStride, yStride);
+
+ // copy data from CPU memory space to the GPU
+ _d_elevationsArray.CopyFromCPU(elevationsArray);
+ _d_waterHead.CopyFromCPU(waterHead);
+
+ // tell the accelerator to start computing the kernel
+ loadedKernel(elevationsArray.Length, _d_elevationsArray.View, _d_waterHead.View, xStride, yStride, _d_flowDirections.View, _d_waterAmounts.View, SandWormComponent.unitsMultiplier);
+ accelerator.Synchronize();
+
+ // copy output data from the GPU back to the CPU
+ flowDirections = _d_flowDirections.GetAsArray1D();
+ waterAmounts = _d_waterAmounts.GetAsArray1D();
+
+ Parallel.For(0, yStride - 1, rows =>
+ {
+ for (int columns = 1; columns < xStride - 1; columns++)
+ {
+ int i = rows * xStride + columns;
+ DistributeWater(flowDirections, i);
+ }
+ });
+
+ Parallel.For(0, elevationsArray.Length, i =>
+ {
+ if (waterHead[i] > 0)
+ waterElevationPoints[i].Z = pointArray[i].Z + waterHead[i];
+ else
+ waterElevationPoints[i].Z = pointArray[i].Z - (1 * SandWormComponent.unitsMultiplier); // Hide water mesh under terrain
+ });
+ }
+
+ private static void FlowDirectionKernel(Index1D i, ArrayView _elevationsArray, ArrayView _waterHead, int _xStride, int _yStride, ArrayView _flowDirections, ArrayView _waterAmounts, double _unitsMultiplier)
+ {
+ if (_waterHead[i] == 0)
+ _flowDirections[i] = i;
+ else
+ {
+ int h = i - _xStride;
+ int j = i + _xStride;
+
+ int[] indices = new int[8] { h - 1, h, h + 1, i + 1, j + 1, j, j - 1, i - 1 }; // SW, S, SE, E, NE, N, NW, W
+ double[] deltas = new double[8] { 0.7, 1, 0.7, 1, 0.7, 1, 0.7, 1 }; // deltaXY = 0.7, deltaX & deltaY = 1
+
+ double waterLevel = (_elevationsArray[i] * _unitsMultiplier) - _waterHead[i];
+ double maxSlope = 0;
+ double maxDeltaZ = 0;
+ int maxIndex = i;
+ int maxCount = _xStride * _yStride;
+
+ for (int o = 0; o < indices.Length; o++)
+ {
+ if (indices[o] >= 0 && indices[o] < maxCount) // Make sure index is not out of bounds
+ {
+ double _deltaZ = waterLevel - (_elevationsArray[indices[o]] * _unitsMultiplier) + _waterHead[indices[o]]; // Working on inverted elevation values
+ double _slope = _deltaZ * deltas[o];
+ if (_slope < maxSlope) // Again, inverted elevation values
+ {
+ maxSlope = _slope;
+ maxDeltaZ = _deltaZ;
+ maxIndex = indices[o];
+ }
+ }
+ }
+
+ double _waterAmountHalved = maxDeltaZ * -0.5; // Divide by -2 to split the water equally. Negative number is due to inverted elevation table coming from the sensor
+ double waterAmount = _waterAmountHalved < _waterHead[i] ? _waterAmountHalved : _waterHead[i]; // Clamp to the amount of water a cell actually contains
+
+ _waterAmounts[i] = waterAmount;
+ _flowDirections[i] = maxIndex;
+ }
+ }
+
+ private static void DistributeWater(int[] flowDirections, int currentIndex)
+ {
+ int destination = flowDirections[currentIndex];
+
+ if (waterHead[currentIndex] == 0 || destination == currentIndex)
+ return;
+
+ // Clamp water flow to max value defined by flow velocity
+ double waterFlow = waterAmounts[currentIndex] < flowVelocity * SandWormComponent.unitsMultiplier ? waterAmounts[currentIndex] : flowVelocity * SandWormComponent.unitsMultiplier;
+
+ waterHead[currentIndex] -= waterFlow;
+
+ // If the cell isn't wet reduce outflow to neighbors by infiltration & evaporation
+ if (waterHead[currentIndex] > 0)
+ waterHead[destination] += waterFlow * 0.96; // There are always some water losses. This is a somewhat arbitrary factor
+ else
+ waterHead[destination] += waterFlow * runoffCoefficients[currentIndex];
+ }
+
+ private static void DrainBorders(int xStride, int yStride)
+ {
+ // Borders are a water sink
+ for (int i = 0, e = xStride - 1; i < e; i++) // Bottom border
+ waterHead[i] = 0;
+
+ for (int i = (yStride - 1) * xStride, e = yStride * xStride; i < e; i++) // Top border
+ waterHead[i] = 0;
+
+ for (int i = xStride, e = (yStride - 1) * xStride; i < e; i += xStride)
+ {
+ waterHead[i] = 0; // Left border
+ waterHead[i - 1] = 0; // Right border
+ }
+ }
+ }
+}
diff --git a/SandWorm/Analytics/None.cs b/SandWorm/Analytics/None.cs
index 765972a..d1efa0f 100644
--- a/SandWorm/Analytics/None.cs
+++ b/SandWorm/Analytics/None.cs
@@ -14,10 +14,5 @@ public Color[] GetColorCloudForAnalysis()
var vertexColors = new Color[0];
return vertexColors; // Send back an empty array so mesh is transparent/uncolored
}
-
- public override void ComputeLookupTableForAnalysis(double sensorElevation)
- {
- return; // No lookup table necessary
- }
}
}
\ No newline at end of file
diff --git a/SandWorm/Analytics/RGB.cs b/SandWorm/Analytics/RGB.cs
new file mode 100644
index 0000000..d408f2f
--- /dev/null
+++ b/SandWorm/Analytics/RGB.cs
@@ -0,0 +1,23 @@
+using System.Drawing;
+using Microsoft.Azure.Kinect.Sensor;
+
+
+namespace SandWorm.Analytics
+{
+ public class RGB : Analysis.MeshColorAnalysis
+ {
+ public RGB() : base("RGB")
+ {
+ }
+ public Color[] GetColorCloudForAnalysis(Color[] pixelColors)
+ {
+ Color[] vertexColors = new Color[pixelColors.Length];
+ for (int i = 0; i < pixelColors.Length; i++)
+ {
+ vertexColors[i] = Color.FromArgb(pixelColors[i].R, pixelColors[i].G, pixelColors[i].B);
+ }
+
+ return vertexColors;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/Analytics/Slope.cs b/SandWorm/Analytics/Slope.cs
index 5769f20..040151b 100644
--- a/SandWorm/Analytics/Slope.cs
+++ b/SandWorm/Analytics/Slope.cs
@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Drawing;
+using System.Numerics;
using System.Threading.Tasks;
using Rhino.Display;
-using Rhino.Geometry;
-namespace SandWorm.Analytics
+namespace SandWorm
{
public class Slope : Analysis.MeshColorAnalysis
{
@@ -23,99 +23,131 @@ private Color GetColorForSlope(ushort slopeValue)
return lookupTable[slopeValue];
}
- public Color[] GetColorCloudForAnalysis(double[] pixelArray, int width, int height, double deltaX, double deltaY)
+ public Color[] GetColorCloudForAnalysis(double[] pixelArray, int width, int height, double gradientRange, Vector2[] xyLookupTable)
{
if (lookupTable == null)
- {
- ComputeLookupTableForAnalysis(0.0);
- }
- var vertexColors = new Color[pixelArray.Length];
+ ComputeLookupTableForAnalysis(0.0, gradientRange);
- // Calculate slope values
- double deltaXY = Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+ double deltaX;
+ double deltaY;
+ double deltaXY;
double slope = 0.0;
+ var vertexColors = new Color[pixelArray.Length];
+
// first pixel NW
- slope += Math.Abs(pixelArray[1] - pixelArray[0]) / deltaX; // E Pixel
- slope += Math.Abs(pixelArray[width] - pixelArray[0]) / deltaY; // S Pixel
- slope += Math.Abs(pixelArray[width + 1] - pixelArray[0]) / deltaXY; // SE Pixel
+ deltaX = (xyLookupTable[1].X - xyLookupTable[0].X);
+ deltaY = (xyLookupTable[width].Y - xyLookupTable[0].Y);
+ deltaXY = Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+
+ slope += Math.Abs(pixelArray[1] - pixelArray[0]) * SandWormComponent.unitsMultiplier / deltaX; // E Pixel
+ slope += Math.Abs(pixelArray[width] - pixelArray[0]) * SandWormComponent.unitsMultiplier / deltaY; // S Pixel
+ slope += Math.Abs(pixelArray[width + 1] - pixelArray[0]) * SandWormComponent.unitsMultiplier / deltaXY; // SE Pixel
vertexColors[0] = GetColorForSlope((ushort)(slope * 33.33)); // Divide by 3 multiply by 100 => 33.33
// last pixel NE
+ deltaX = (xyLookupTable[width - 2].X - xyLookupTable[width - 1].X);
+ deltaY = (xyLookupTable[2 * width - 1].Y - xyLookupTable[width - 1].Y);
+ deltaXY = Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+
slope = 0.0;
- slope += Math.Abs(pixelArray[width - 2] - pixelArray[width - 1]) / deltaX; // W Pixel
- slope += Math.Abs(pixelArray[2 * width - 1] - pixelArray[width - 1]) / deltaY; // S Pixel
- slope += Math.Abs(pixelArray[2 * width - 2] - pixelArray[width - 1]) / deltaXY; // SW Pixel
+ slope += Math.Abs(pixelArray[width - 2] - pixelArray[width - 1]) * SandWormComponent.unitsMultiplier / deltaX; // W Pixel
+ slope += Math.Abs(pixelArray[2 * width - 1] - pixelArray[width - 1]) * SandWormComponent.unitsMultiplier / deltaY; // S Pixel
+ slope += Math.Abs(pixelArray[2 * width - 2] - pixelArray[width - 1]) * SandWormComponent.unitsMultiplier / deltaXY; // SW Pixel
vertexColors[width - 1] = GetColorForSlope((ushort)(slope * 33.33)); // Divide by 3 multiply by 100 => 33.33
// first pixel SW
+ deltaX = (xyLookupTable[(height - 1) * width + 1].X - xyLookupTable[(height - 1) * width].X);
+ deltaY = (xyLookupTable[(height - 2) * width].Y - xyLookupTable[(height - 1) * width].Y);
+ deltaXY = Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+
slope = 0.0;
- slope += Math.Abs(pixelArray[(height - 1) * width + 1] - pixelArray[(height - 1) * width]) / deltaX; // E Pixel
- slope += Math.Abs(pixelArray[(height - 2) * width] - pixelArray[(height - 1) * width]) / deltaY; // N Pixel
- slope += Math.Abs(pixelArray[(height - 2) * width + 1] - pixelArray[(height - 1) * width]) / deltaXY; //NE Pixel
+ slope += Math.Abs(pixelArray[(height - 1) * width + 1] - pixelArray[(height - 1) * width]) * SandWormComponent.unitsMultiplier / deltaX; // E Pixel
+ slope += Math.Abs(pixelArray[(height - 2) * width] - pixelArray[(height - 1) * width]) * SandWormComponent.unitsMultiplier / deltaY; // N Pixel
+ slope += Math.Abs(pixelArray[(height - 2) * width + 1] - pixelArray[(height - 1) * width]) * SandWormComponent.unitsMultiplier / deltaXY; //NE Pixel
vertexColors[(height - 1) * width] = GetColorForSlope((ushort)(slope * 33.33)); // Divide by 3 multiply by 100 => 33.33
// last pixel SE
+ deltaX = (xyLookupTable[height * width - 2].X - xyLookupTable[height * width - 1].X);
+ deltaY = (xyLookupTable[(height - 1) * width - 1].Y - xyLookupTable[height * width - 1].Y);
+ deltaXY = Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+
slope = 0.0;
- slope += Math.Abs(pixelArray[height * width - 2] - pixelArray[height * width - 1]) / deltaX; // W Pixel
- slope += Math.Abs(pixelArray[(height - 1) * width - 1] - pixelArray[height * width - 1]) / deltaY; // N Pixel
- slope += Math.Abs(pixelArray[(height - 1) * width - 2] - pixelArray[height * width - 1]) / deltaXY; //NW Pixel
+ slope += Math.Abs(pixelArray[height * width - 2] - pixelArray[height * width - 1]) * SandWormComponent.unitsMultiplier / deltaX; // W Pixel
+ slope += Math.Abs(pixelArray[(height - 1) * width - 1] - pixelArray[height * width - 1]) * SandWormComponent.unitsMultiplier / deltaY; // N Pixel
+ slope += Math.Abs(pixelArray[(height - 1) * width - 2] - pixelArray[height * width - 1]) * SandWormComponent.unitsMultiplier / deltaXY; //NW Pixel
vertexColors[height * width - 1] = GetColorForSlope((ushort)(slope * 33.33)); // Divide by 3 multiply by 100 => 33.33
// first row
- for (int x = 1; x < width - 1; x++)
+ for (int i = 1; i < width - 1; i++)
{
+ deltaX = (xyLookupTable[i - 1].X - xyLookupTable[i].X);
+ deltaY = (xyLookupTable[i + width].Y - xyLookupTable[i].Y);
+ deltaXY = Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+
slope = 0.0;
- slope += Math.Abs(pixelArray[x - 1] - pixelArray[x]) / deltaX; // W Pixel
- slope += Math.Abs(pixelArray[x + 1] - pixelArray[x]) / deltaX; // E Pixel
- slope += Math.Abs(pixelArray[x + width - 1] - pixelArray[x]) / deltaXY; // SW Pixel
- slope += Math.Abs(pixelArray[x + width] - pixelArray[x]) / deltaY; // S Pixel
- slope += Math.Abs(pixelArray[x + width + 1] - pixelArray[x]) / deltaXY; // SE Pixel
+ slope += Math.Abs(pixelArray[i - 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaX; // W Pixel
+ slope += Math.Abs(pixelArray[i + 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaX; // E Pixel
+ slope += Math.Abs(pixelArray[i + width - 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaXY; // SW Pixel
+ slope += Math.Abs(pixelArray[i + width] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaY; // S Pixel
+ slope += Math.Abs(pixelArray[i + width + 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaXY; // SE Pixel
- vertexColors[x] = GetColorForSlope((ushort)(slope * 20.0)); // Divide by 5 multiply by 100 => 20.0
+ vertexColors[i] = GetColorForSlope((ushort)(slope * 20.0)); // Divide by 5 multiply by 100 => 20.0
}
// last row
- for (int x = (height - 1) * width + 1; x < height * width - 1; x++)
+ for (int i = (height - 1) * width + 1; i < height * width - 1; i++)
{
+ deltaX = (xyLookupTable[i - 1].X - xyLookupTable[i].X);
+ deltaY = (xyLookupTable[i - width].Y - xyLookupTable[i].Y);
+ deltaXY = Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+
slope = 0.0;
- slope += Math.Abs(pixelArray[x - 1] - pixelArray[x]) / deltaX; // W Pixel
- slope += Math.Abs(pixelArray[x + 1] - pixelArray[x]) / deltaX; // E Pixel
- slope += Math.Abs(pixelArray[x - width - 1] - pixelArray[x]) / deltaXY; // NW Pixel
- slope += Math.Abs(pixelArray[x - width] - pixelArray[x]) / deltaY; // N Pixel
- slope += Math.Abs(pixelArray[x - width + 1] - pixelArray[x]) / deltaXY; // NE Pixel
+ slope += Math.Abs(pixelArray[i - 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaX; // W Pixel
+ slope += Math.Abs(pixelArray[i + 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaX; // E Pixel
+ slope += Math.Abs(pixelArray[i - width - 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaXY; // NW Pixel
+ slope += Math.Abs(pixelArray[i - width] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaY; // N Pixel
+ slope += Math.Abs(pixelArray[i - width + 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaXY; // NE Pixel
- vertexColors[x] = GetColorForSlope((ushort)(slope * 20.0)); // Divide by 5 multiply by 100 => 20.0
+ vertexColors[i] = GetColorForSlope((ushort)(slope * 20.0)); // Divide by 5 multiply by 100 => 20.0
}
// first column
- for (int x = width; x < (height - 1) * width; x += width)
+ for (int i = width; i < (height - 1) * width; i += width)
{
+ deltaX = (xyLookupTable[i - width].X - xyLookupTable[i].X);
+ deltaY = (xyLookupTable[i + 1].Y - xyLookupTable[i].Y);
+ deltaXY = Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+
slope = 0.0;
- slope += Math.Abs(pixelArray[x - width] - pixelArray[x]) / deltaY; // N Pixel
- slope += Math.Abs(pixelArray[x + width] - pixelArray[x]) / deltaY; // S Pixel
- slope += Math.Abs(pixelArray[x - width + 1] - pixelArray[x]) / deltaXY; // NE Pixel
- slope += Math.Abs(pixelArray[x + 1] - pixelArray[x]) / deltaX; // E Pixel
- slope += Math.Abs(pixelArray[x + width + 1] - pixelArray[x]) / deltaXY; // SE Pixel
+ slope += Math.Abs(pixelArray[i - width] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaY; // N Pixel
+ slope += Math.Abs(pixelArray[i + width] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaY; // S Pixel
+ slope += Math.Abs(pixelArray[i - width + 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaXY; // NE Pixel
+ slope += Math.Abs(pixelArray[i + 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaX; // E Pixel
+ slope += Math.Abs(pixelArray[i + width + 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaXY; // SE Pixel
- vertexColors[x] = GetColorForSlope((ushort)(slope * 20.0)); // Divide by 5 multiply by 100 => 20.0
+ vertexColors[i] = GetColorForSlope((ushort)(slope * 20.0)); // Divide by 5 multiply by 100 => 20.0
}
// last column
- for (int x = 2 * width - 1; x < height * width - 1; x += width)
+ for (int i = 2 * width - 1; i < height * width - 1; i += width)
{
+ deltaX = (xyLookupTable[i - width].X - xyLookupTable[i].X);
+ deltaY = (xyLookupTable[i - 1].Y - xyLookupTable[i].Y);
+ deltaXY = Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+
slope = 0.0;
- slope += Math.Abs(pixelArray[x - width] - pixelArray[x]) / deltaY; // N Pixel
- slope += Math.Abs(pixelArray[x + width] - pixelArray[x]) / deltaY; // S Pixel
- slope += Math.Abs(pixelArray[x - width - 1] - pixelArray[x]) / deltaXY; // NW Pixel
- slope += Math.Abs(pixelArray[x - 1] - pixelArray[x]) / deltaX; // W Pixel
- slope += Math.Abs(pixelArray[x + width - 1] - pixelArray[x]) / deltaXY; // SW Pixel
+ slope += Math.Abs(pixelArray[i - width] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaY; // N Pixel
+ slope += Math.Abs(pixelArray[i + width] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaY; // S Pixel
+ slope += Math.Abs(pixelArray[i - width - 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaXY; // NW Pixel
+ slope += Math.Abs(pixelArray[i - 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaX; // W Pixel
+ slope += Math.Abs(pixelArray[i + width - 1] - pixelArray[i]) * SandWormComponent.unitsMultiplier / deltaXY; // SW Pixel
- vertexColors[x] = GetColorForSlope((ushort)(slope * 20.0)); // Divide by 5 multiply by 100 => 20.0
+ vertexColors[i] = GetColorForSlope((ushort)(slope * 20.0)); // Divide by 5 multiply by 100 => 20.0
}
// rest of the array
@@ -123,39 +155,45 @@ public Color[] GetColorCloudForAnalysis(double[] pixelArray, int width, int heig
{
for (int columns = 1; columns < width - 1; columns++) // Iterate over x dimension
{
- int h = (rows - 1) * width + columns;
int i = rows * width + columns;
- int j = (rows + 1) * width + columns;
+ int h = i - width;
+ int j = i + width;
double parallelSlope = 0.0; // Declare a local variable in the parallel loop for performance reasons
- parallelSlope += Math.Abs((pixelArray[h - 1] - pixelArray[i])) / deltaXY; // NW pixel
- parallelSlope += Math.Abs((pixelArray[h] - pixelArray[i])) / deltaY; //N pixel
- parallelSlope += Math.Abs((pixelArray[h + 1] - pixelArray[i])) / deltaXY; //NE pixel
- parallelSlope += Math.Abs((pixelArray[i - 1] - pixelArray[i])) / deltaX; //W pixel
- parallelSlope += Math.Abs((pixelArray[i + 1] - pixelArray[i])) / deltaX; //E pixel
- parallelSlope += Math.Abs((pixelArray[j - 1] - pixelArray[i])) / deltaXY; //SW pixel
- parallelSlope += Math.Abs((pixelArray[j] - pixelArray[i])) / deltaY; //S pixel
- parallelSlope += Math.Abs((pixelArray[j + 1] - pixelArray[i])) / deltaXY; //SE pixel
+
+ double parallelDeltaX = (xyLookupTable[i + 1].X - xyLookupTable[i].X);
+ double parallelDeltaY = (xyLookupTable[h].Y - xyLookupTable[i].Y);
+ double parallelDeltaXY = Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
+
+ parallelSlope += Math.Abs((pixelArray[h - 1] - pixelArray[i])) * SandWormComponent.unitsMultiplier / parallelDeltaXY; //NW pixel
+ parallelSlope += Math.Abs((pixelArray[h] - pixelArray[i])) * SandWormComponent.unitsMultiplier / parallelDeltaY; //N pixel
+ parallelSlope += Math.Abs((pixelArray[h + 1] - pixelArray[i])) * SandWormComponent.unitsMultiplier / parallelDeltaXY; //NE pixel
+ parallelSlope += Math.Abs((pixelArray[i - 1] - pixelArray[i])) * SandWormComponent.unitsMultiplier / parallelDeltaX; //W pixel
+ parallelSlope += Math.Abs((pixelArray[i + 1] - pixelArray[i])) * SandWormComponent.unitsMultiplier / parallelDeltaX; //E pixel
+ parallelSlope += Math.Abs((pixelArray[j - 1] - pixelArray[i])) * SandWormComponent.unitsMultiplier / parallelDeltaXY; //SW pixel
+ parallelSlope += Math.Abs((pixelArray[j] - pixelArray[i])) * SandWormComponent.unitsMultiplier / parallelDeltaY; //S pixel
+ parallelSlope += Math.Abs((pixelArray[j + 1] - pixelArray[i])) * SandWormComponent.unitsMultiplier / parallelDeltaXY; //SE pixel
vertexColors[i] = GetColorForSlope((ushort)(parallelSlope * 12.5)); // Divide by 8 multiply by 100 => 12.5
}
});
+
return vertexColors;
}
- public override void ComputeLookupTableForAnalysis(double sensorElevation)
+ public override void ComputeLookupTableForAnalysis(double sensorElevation, double gradientRange)
{
var slightSlopeRange = new Analysis.VisualisationRangeWithColor
{
- ValueSpan = 30,
+ ValueSpan = (int)gradientRange,
ColorStart = new ColorHSL(0.30, 1.0, 0.5), // Green
ColorEnd = new ColorHSL(0.15, 1.0, 0.5) // Yellow
};
var moderateSlopeRange = new Analysis.VisualisationRangeWithColor
{
- ValueSpan = 30,
- ColorStart = new ColorHSL(0.15, 1.0, 0.5), // Green
+ ValueSpan = (int)gradientRange,
+ ColorStart = new ColorHSL(0.15, 1.0, 0.5), // Yellow
ColorEnd = new ColorHSL(0.0, 1.0, 0.5) // Red
};
var extremeSlopeRange = new Analysis.VisualisationRangeWithColor
diff --git a/SandWorm/Analytics/WaterLevel.cs b/SandWorm/Analytics/WaterLevel.cs
index 97673b4..3dd6eac 100644
--- a/SandWorm/Analytics/WaterLevel.cs
+++ b/SandWorm/Analytics/WaterLevel.cs
@@ -3,21 +3,29 @@
namespace SandWorm.Analytics
{
- public class WaterLevel : Analysis.MeshGeometryAnalysis
+ public static class WaterLevel
{
- public WaterLevel() : base("Show Water Level")
+ public static void GetGeometryForAnalysis(ref List outputGeometry, double waterLevel, Mesh terrainMesh)
{
- }
+ List waterCorners = new List();
+ BoundingBox box = terrainMesh.GetBoundingBox(false);
+ Point3d[] corners = box.GetCorners();
- public override void GetGeometryForAnalysis(ref List outputGeometry, double waterLevel, Mesh mesh)
- {
- var bounds = mesh.GetBoundingBox(false);
- var waterPlane = new Plane(new Point3d(bounds.Max.X, bounds.Max.Y, waterLevel), new Vector3d(0, 0, 1));
- var waterSrf = new PlaneSurface(waterPlane,
- new Interval(bounds.Min.X, bounds.Max.X),
- new Interval(bounds.Min.Y, bounds.Max.Y)
- );
- outputGeometry.Add(waterSrf);
+ waterCorners.Add(corners[0]);
+ waterCorners.Add(corners[1]);
+ waterCorners.Add(corners[2]);
+ waterCorners.Add(corners[3]);
+ waterCorners.Add(corners[0]); // Close polyline
+
+ for (int i = 0; i < waterCorners.Count; i++)
+ waterCorners[i] = new Point3d(waterCorners[i].X, waterCorners[i].Y, waterLevel * SandWormComponent.unitsMultiplier);
+
+ Polyline waterBoundary = new Polyline(waterCorners);
+ Mesh waterPlane = Mesh.CreateFromClosedPolyline(waterBoundary);
+
+ //Setting mesh transparency doesn't work. It's a known bug https://mcneel.myjetbrains.com/youtrack/issue/RH-49604
+ //waterPlane.VertexColors.CreateMonotoneMesh(System.Drawing.Color.FromArgb(100, 100, 100, 255));
+ outputGeometry.Add(waterPlane);
}
}
}
\ No newline at end of file
diff --git a/SandWorm/Components/BaseComponent.cs b/SandWorm/Components/BaseComponent.cs
deleted file mode 100644
index 23881eb..0000000
--- a/SandWorm/Components/BaseComponent.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Collections.Generic;
-using System.Diagnostics;
-using Grasshopper.Kernel;
-
-namespace SandWorm.Components
-{
- // Provides common functions across the various Sandworm components
- public abstract class BaseComponent : GH_Component
- {
- public static List output; // Debugging
- protected Stopwatch timer;
-
- // Pass the constructor parameters up to the main GH_Component abstract class
- protected BaseComponent(string name, string nickname, string description, string subCategory)
- : base(name, nickname, description, "Sandworm", subCategory)
- {
- }
-
- // Components must implement a solve instance using SandwormSolveInstance()
- protected abstract void SandwormSolveInstance(IGH_DataAccess DA);
-
- protected override void SolveInstance(IGH_DataAccess DA)
- {
- SandwormSolveInstance(DA);
- }
-
- protected void SetupLogging()
- {
- timer = Stopwatch.StartNew(); // Setup timer used for debugging
- output = new List(); // For the debugging log lines
- }
- }
-}
\ No newline at end of file
diff --git a/SandWorm/Components/BaseKinectComponent.cs b/SandWorm/Components/BaseKinectComponent.cs
deleted file mode 100644
index f636d51..0000000
--- a/SandWorm/Components/BaseKinectComponent.cs
+++ /dev/null
@@ -1,196 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Grasshopper.Kernel;
-using Microsoft.Kinect;
-using Rhino;
-using Rhino.Geometry;
-
-namespace SandWorm.Components
-{
- // Provides common functions across the components that read from the Kinect stream
- public abstract class BaseKinectComponent : BaseComponent
- {
- // Common Input Parameters
- public int averageFrames = 1;
- public int blurRadius = 1;
- public int keepFrames = 1;
- // Sandworm Options
- public SetupOptions options; // List of options coming from the SetupComponent
- public int topRows = 0;
- public int rightColumns = 0;
- public int bottomRows = 0;
- public int leftColumns = 0;
- public double sensorElevation = 1000; // Arbitrary default value (must be >0)
- public double[] elevationArray;
- public int tickRate = 33; // In ms
- // Derived
- protected Core.PixelSize depthPixelSize;
- public static double unitsMultiplier;
- protected KinectSensor kinectSensor;
- protected Point3f[] allPoints;
- protected int trimmedHeight;
- protected int trimmedWidth;
- protected readonly LinkedList renderBuffer = new LinkedList();
- public int[] runningSum = Enumerable.Range(1, 217088).Select(i => new int()).ToArray();
-
- public BaseKinectComponent(string name, string nickname, string description, string subCategory)
- : base(name, nickname, description, subCategory)
- {
- }
-
- protected void GetSandwormOptions(IGH_DataAccess DA, int optionsIndex, int framesIndex, int blurIndex)
- {
- // Loads standard options provided by the setup component
- options = new SetupOptions();
- DA.GetData(optionsIndex, ref options);
-
- if (options.SensorElevation != 0) sensorElevation = options.SensorElevation;
- if (options.LeftColumns != 0) leftColumns = options.LeftColumns;
- if (options.RightColumns != 0) rightColumns = options.RightColumns;
- if (options.TopRows != 0) topRows = options.TopRows;
- if (options.BottomRows != 0) bottomRows = options.BottomRows;
- if (options.TickRate != 0) tickRate = options.TickRate;
- if (options.KeepFrames != 0) keepFrames = options.KeepFrames;
- if (options.ElevationArray != null && options.ElevationArray.Length != 0) elevationArray = options.ElevationArray;
- else elevationArray = new double[0];
-
- // Pick the correct multiplier based on the drawing units. Shouldn't be a class variable; gets 'stuck'.
- unitsMultiplier = Core.ConvertDrawingUnits(RhinoDoc.ActiveDoc.ModelUnitSystem);
- sensorElevation /= unitsMultiplier; // Standardise to mm to match sensor units
-
- // Technically not provided by setup; but common to all Kinect-accessing components
- if (framesIndex > 0)
- {
- DA.GetData(framesIndex, ref averageFrames);
- // Make sure there is at least one frame in the render buffer
- averageFrames = averageFrames < 1 ? 1 : averageFrames;
- }
- if (blurIndex > 0)
- DA.GetData(blurIndex, ref blurRadius);
-
- depthPixelSize = Core.GetDepthPixelSpacing(sensorElevation);
- }
-
- protected void SetupKinect()
- {
- if (kinectSensor == null)
- {
- KinectController.AddRef();
- kinectSensor = KinectController.sensor;
- }
-
- if (KinectController.depthFrameData == null)
- {
- ShowComponentError("No depth frame data provided by the Kinect.");
- return;
- }
-
- // Initialize all arrays
- trimmedWidth = KinectController.depthWidth - leftColumns - rightColumns;
- trimmedHeight = KinectController.depthHeight - topRows - bottomRows;
- }
-
- protected void SetupRenderBuffer(int[] depthFrameDataInt, Mesh quadMesh)
- {
- // Trim the depth array and cast ushort values to int
- Core.CopyAsIntArray(KinectController.depthFrameData, depthFrameDataInt,
- leftColumns, rightColumns, topRows, bottomRows,
- KinectController.depthHeight, KinectController.depthWidth);
-
- // Reset everything when resizing Kinect's field of view or changing the amounts of frame to average across
- if (renderBuffer.Count > averageFrames || (quadMesh != null && quadMesh.Faces.Count != (trimmedWidth - 2) * (trimmedHeight - 2)))
- {
- renderBuffer.Clear();
- Array.Clear(runningSum, 0, runningSum.Length);
- renderBuffer.AddLast(depthFrameDataInt);
- }
- else
- {
- renderBuffer.AddLast(depthFrameDataInt);
- }
- }
-
- protected void AverageAndBlurPixels(int[] depthFrameDataInt, ref double[] averagedDepthFrameData)
- {
- // Average across multiple frames
- for (var pixel = 0; pixel < depthFrameDataInt.Length; pixel++)
- {
- if (depthFrameDataInt[pixel] > 200) // We have a valid pixel.
- {
- runningSum[pixel] += depthFrameDataInt[pixel];
- }
- else
- {
- if (pixel > 0) // Pixel is invalid and we have a neighbor to steal information from
- {
- runningSum[pixel] += depthFrameDataInt[pixel - 1];
- // Replace the zero value from the depth array with the one from the neighboring pixel
- renderBuffer.Last.Value[pixel] = depthFrameDataInt[pixel - 1];
- }
- else // Pixel is invalid and it is the first one in the list. (No neighbor on the left hand side, so we set it to the lowest point on the table)
- {
- runningSum[pixel] += (int)sensorElevation;
- renderBuffer.Last.Value[pixel] = (int)sensorElevation;
- }
- }
-
- averagedDepthFrameData[pixel] = runningSum[pixel] / renderBuffer.Count; // Calculate average values
- if (elevationArray.Length > 0) averagedDepthFrameData[pixel] -= elevationArray[pixel]; // Correct for Kinect's inacurracies using input from the calibration component
-
- if (renderBuffer.Count >= averageFrames)
- runningSum[pixel] -= renderBuffer.First.Value[pixel]; // Subtract the oldest value from the sum
- }
-
- Core.LogTiming(ref output, timer, "Frames averaging"); // Debug Info
-
- if (blurRadius > 1) // Apply gaussian blur
- {
- var gaussianBlurProcessor = new GaussianBlurProcessor(blurRadius, trimmedWidth, trimmedHeight);
- gaussianBlurProcessor.Apply(averagedDepthFrameData);
- Core.LogTiming(ref output, timer, "Gaussian blurring"); // Debug Info
- }
- }
-
- protected void GeneratePointCloud(double[] averagedDepthFrameData)
- {
- double depthPoint;
- // Setup variables for per-pixel loop
- allPoints = new Point3f[trimmedWidth * trimmedHeight];
- var tempPoint = new Point3f();
- var arrayIndex = 0;
- for (var rows = 0; rows < trimmedHeight; rows++)
- for (var columns = 0; columns < trimmedWidth; columns++)
- {
- depthPoint = averagedDepthFrameData[arrayIndex];
- tempPoint.X = (float)(columns * -unitsMultiplier * depthPixelSize.x);
- tempPoint.Y = (float)(rows * -unitsMultiplier * depthPixelSize.y);
- tempPoint.Z = (float)((depthPoint - sensorElevation) * -unitsMultiplier);
- allPoints[arrayIndex] = tempPoint; // Add new point to point cloud itself
- arrayIndex++;
- }
-
- // Keep only the desired amount of frames in the buffer
- while (renderBuffer.Count >= averageFrames) renderBuffer.RemoveFirst();
-
- Core.LogTiming(ref output, timer, "Point cloud generation"); // Debug Info
- }
-
- protected void ShowComponentError(string errorMessage)
- {
- AddRuntimeMessage(GH_RuntimeMessageLevel.Error, errorMessage);
- ScheduleSolve(); // Ensure a future solve is scheduled despite an early return to SolveInstance()
- }
-
- protected void ScheduleDelegate(GH_Document doc)
- {
- ExpireSolution(false);
- }
-
- protected void ScheduleSolve()
- {
- if (tickRate > 0) // Allow users to force manual recalculation
- OnPingDocument().ScheduleSolution(tickRate, ScheduleDelegate);
- }
- }
-}
\ No newline at end of file
diff --git a/SandWorm/Components/BaseMarkerComponent.cs b/SandWorm/Components/BaseMarkerComponent.cs
deleted file mode 100644
index 4099abd..0000000
--- a/SandWorm/Components/BaseMarkerComponent.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Linq;
-using Grasshopper.Kernel;
-using Microsoft.Kinect;
-using Rhino;
-using Rhino.Geometry;
-using OpenCvSharp;
-
-namespace SandWorm.Components
-{
- // Provides common functions across the components that classify markers within the Kinect stream
- public abstract class BaseMarkerComponent : BaseKinectComponent
- {
- protected List markerColors;
- protected double colorFuzz;
- protected Color[] allPixels;
-
- public BaseMarkerComponent(string name, string nickname, string description)
- : base(name, nickname, description, "Markers")
- {
- }
-
- protected Mat GenerateColorImage()
- {
- if (KinectController.colorFrameData == null)
- {
- ShowComponentError("No color frame data provided by the Kinect.");
- return null;
- }
-
- // Convert the kinect pixel byte array to a opencv mat file for processing
- var height = KinectController.colorHeight;
- var width = KinectController.colorWidth;
- var mat = new Mat(height, width, MatType.CV_8UC4, KinectController.colorFrameData);
- Core.LogTiming(ref output, timer, "Color image generation"); // Debug Info
- return mat;
- }
-
- protected override void RegisterInputParams(GH_InputParamManager pManager)
- {
- pManager.AddColourParameter("MarkerColor", "Color", "The color, or colors, of the markers this component should track", GH_ParamAccess.list);
- pManager.AddNumberParameter("ColorFuzz", "Fuzz", "The amount of leeway to use when matching the color. A higher value will identify more similar colors",
- GH_ParamAccess.item);
- pManager.AddGenericParameter("SandwormOptions", "SWO", "Setup & Calibration options", GH_ParamAccess.item);
- pManager[0].Optional = false;
- pManager[1].Optional = true;
- pManager[2].Optional = true;
- }
- }
-}
\ No newline at end of file
diff --git a/SandWorm/Components/MarkerAreaComponent.cs b/SandWorm/Components/MarkerAreaComponent.cs
deleted file mode 100644
index 9b3cb0a..0000000
--- a/SandWorm/Components/MarkerAreaComponent.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Linq;
-using Grasshopper.Kernel;
-using Grasshopper.Kernel.Types;
-using Microsoft.Kinect;
-using Rhino.Geometry;
-using SandWorm.Components;
-using SandWorm.Properties;
-
-namespace SandWorm
-{
- public class MarkerAreaComponent : BaseMarkerComponent
- {
- private List markerAreas;
- public MarkerAreaComponent() : base("Sandworm Area Markers", "SW PMarks",
- "Track color markers from the Kinect's' camera and output as areas")
- {
- }
-
- protected override Bitmap Icon => Resources.icons_marker_areas;
-
- public override Guid ComponentGuid => new Guid("41b279b6-643e-4d22-bc45-b47efa264ffb");
-
- protected override void RegisterOutputParams(GH_OutputParamManager pManager)
- {
- pManager.AddCurveParameter("Marker Areas", "A", "The areas of different color; as a Grasshopper curve", GH_ParamAccess.list);
- pManager.AddTextParameter("Output", "O", "Output", GH_ParamAccess.list); //debugging
- }
-
- protected override void SandwormSolveInstance(IGH_DataAccess DA)
- {
- SetupLogging();
- markerColors = new List();
- DA.GetDataList(0, markerColors);
- DA.GetData(1, ref colorFuzz);
- GetSandwormOptions(DA, 2, 0, 0);
- SetupKinect();
- Core.LogTiming(ref output, timer, "Initial setup"); // Debug Info
-
- var binaryImage = GenerateColorImage();
- Core.LogTiming(ref output, timer, "Image generation"); // Debug Info
- if (binaryImage != null)
- {
-
- // Translate identified areas back into Grasshopper geometry
- markerAreas = new List();
-
- // TODO: translation
-
- DA.SetDataList(0, markerAreas);
- }
- else
- {
- // TODO: add warning?
- }
- binaryImage.Dispose();
-
- Core.LogTiming(ref output, timer, "Image processing"); // Debug Info
- DA.SetDataList(1, output); // For logging/debugging
- ScheduleSolve();
- }
- }
-}
\ No newline at end of file
diff --git a/SandWorm/Components/MarkerPointComponent.cs b/SandWorm/Components/MarkerPointComponent.cs
deleted file mode 100644
index f466c37..0000000
--- a/SandWorm/Components/MarkerPointComponent.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Linq;
-using Grasshopper.Kernel;
-using Grasshopper.Kernel.Types;
-using Microsoft.Kinect;
-using Rhino.Geometry;
-using SandWorm.Components;
-using OpenCvSharp;
-using SandWorm.Properties;
-using Point3d = Rhino.Geometry.Point3d;
-
-namespace SandWorm
-{
- public class MarkerPointComponent : BaseMarkerComponent
- {
- private List markerPoints;
- public MarkerPointComponent() : base("Sandworm Point Markers", "SW Markers",
- "Track color markers from the Kinect's' camera and output as points")
- {
- }
-
- protected override Bitmap Icon => Resources.icons_marker_points;
-
- public override Guid ComponentGuid => new Guid("e4752964-5214-47b8-a7db-954166ba5eee");
-
- protected override void RegisterOutputParams(GH_OutputParamManager pManager)
- {
- pManager.AddPointParameter("Marker Locations", "L", "The centers of different color areas; as a Grasshopper point", GH_ParamAccess.list);
- pManager.AddTextParameter("Output", "O", "Output", GH_ParamAccess.list); //debugging
- }
-
- protected override void SandwormSolveInstance(IGH_DataAccess DA)
- {
- SetupLogging();
- markerColors = new List();
- DA.GetDataList(0, markerColors);
- DA.GetData(1, ref colorFuzz);
- GetSandwormOptions(DA, 2, 0, 0);
- SetupKinect();
- Core.LogTiming(ref output, timer, "Initial setup"); // Debug Info
-
- var binaryImage = GenerateColorImage();
- Core.LogTiming(ref output, timer, "Image generation"); // Debug Info
- if (binaryImage != null)
- {
- // Search image for the color and identify/classify
- var keyPoints = new List();
- var detectorParameters = new SimpleBlobDetector.Params
- {
- FilterByArea = true,
- FilterByColor = true, // If it doesn't work; pre-filter the image
- MinDistBetweenBlobs = 1,
- MinArea = 10,
- MaxArea = 20
- };
- Core.LogTiming(ref output, timer, "Detector setup"); // Debug Info
-
- foreach (Color markerColor in markerColors)
- {
- var blobDetector = SimpleBlobDetector.Create(detectorParameters);
- keyPoints.AddRange(blobDetector.Detect(binaryImage));
- blobDetector.Dispose();
- }
- Core.LogTiming(ref output, timer, "Image blob detection"); // Debug Info
-
- // Translate identified points back into Grasshopper geometry
- markerPoints = new List();
- foreach (KeyPoint keyPoint in keyPoints)
- {
- var x = keyPoint.Pt.X;
- var y = keyPoint.Pt.Y;
- markerPoints.Add(new Point3d(x, y, 0));
- }
- DA.SetDataList(0, markerPoints);
- Core.LogTiming(ref output, timer, "Blob output"); // Debug Info
- }
- else
- {
- // TODO: add warning?
- }
- binaryImage.Dispose();
-
- DA.SetDataList(1, output); // For logging/debugging
- ScheduleSolve();
- }
- }
-}
\ No newline at end of file
diff --git a/SandWorm/Components/MeshComponent.cs b/SandWorm/Components/MeshComponent.cs
deleted file mode 100644
index 5d3559a..0000000
--- a/SandWorm/Components/MeshComponent.cs
+++ /dev/null
@@ -1,158 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Linq;
-using System.Windows.Forms;
-using Grasshopper.Kernel;
-using Rhino.Geometry;
-using SandWorm.Analytics;
-using SandWorm.Components;
-using SandWorm.Properties;
-
-namespace SandWorm
-{
- public class MeshComponent : BaseKinectComponent
- {
- private Color[] _vertexColors;
- private Mesh _quadMesh = new Mesh();
- // Input Parameters
- private double _waterLevel = 50;
- private double _contourInterval = 10;
- // Outputs
- private List _outputGeometry;
- private List outputMesh;
-
- public MeshComponent() : base("Sandworm Mesh", "SW Mesh",
- "Visualise Kinect depth data as a mesh", "Visualisation")
- {
- }
-
- protected override Bitmap Icon => Resources.icons_mesh;
-
- public override Guid ComponentGuid => new Guid("f923f24d-86a0-4b7a-9373-23c6b7d2e162");
-
- protected override void RegisterInputParams(GH_InputParamManager pManager)
- {
- pManager.AddNumberParameter("WaterLevel", "WL", "WaterLevel", GH_ParamAccess.item, _waterLevel);
- pManager.AddNumberParameter("ContourInterval", "CI", "The interval (if this analysis is enabled)",
- GH_ParamAccess.item, _contourInterval);
- pManager.AddIntegerParameter("AverageFrames", "AF",
- "Amount of depth frames to average across. This number has to be greater than zero.",
- GH_ParamAccess.item, averageFrames);
- pManager.AddIntegerParameter("BlurRadius", "BR", "Radius for Gaussian blur.", GH_ParamAccess.item,
- blurRadius);
- pManager.AddGenericParameter("SandwormOptions", "SWO", "Setup & Calibration options", GH_ParamAccess.item);
- pManager[0].Optional = true;
- pManager[1].Optional = true;
- pManager[2].Optional = true;
- pManager[3].Optional = true;
- pManager[4].Optional = true;
- }
-
- protected override void RegisterOutputParams(GH_OutputParamManager pManager)
- {
- pManager.AddMeshParameter("Mesh", "M", "Resulting mesh", GH_ParamAccess.list);
- pManager.AddGeometryParameter("Analysis", "A", "Additional mesh analysis", GH_ParamAccess.list);
- pManager.AddTextParameter("Output", "O", "Output", GH_ParamAccess.list); //debugging
- }
-
- protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu)
- {
- base.AppendAdditionalComponentMenuItems(menu);
- foreach (var option in Analysis.AnalysisManager.options) // Add analysis items to menu
- {
- Menu_AppendItem(menu, option.Name, SetMeshVisualisation, true, option.isEnabled);
- // Create reference to the menu item in the analysis class
- option.MenuItem = (ToolStripMenuItem) menu.Items[menu.Items.Count - 1];
- if (!option.IsExclusive)
- Menu_AppendSeparator(menu);
- }
- }
-
- private void SetMeshVisualisation(object sender, EventArgs e)
- {
- Analysis.AnalysisManager.SetEnabledOptions((ToolStripMenuItem) sender);
- _quadMesh.VertexColors.Clear(); // Must flush mesh colors to properly updated display
- ExpireSolution(true);
- }
-
- protected override void SandwormSolveInstance(IGH_DataAccess DA)
- {
- SetupLogging();
- DA.GetData(0, ref _waterLevel);
- DA.GetData(1, ref _contourInterval);
- GetSandwormOptions(DA, 4, 2, 3);
-
- SetupKinect();
- var depthFrameDataInt = new int[trimmedWidth * trimmedHeight];
- var averagedDepthFrameData = new double[trimmedWidth * trimmedHeight];
-
- // Initialize outputs
- if (keepFrames <= 1 || outputMesh == null)
- outputMesh = new List(); // Don't replace prior frames (by clearing array) if using keepFrames
-
- SetupRenderBuffer(depthFrameDataInt, _quadMesh);
- Core.LogTiming(ref output, timer, "Initial setup"); // Debug Info
-
- AverageAndBlurPixels(depthFrameDataInt, ref averagedDepthFrameData);
-
- GeneratePointCloud(averagedDepthFrameData);
-
- // Produce 1st type of analysis that acts on the pixel array and assigns vertex colors
- switch (Analysis.AnalysisManager.GetEnabledMeshColoring())
- {
- case None analysis:
- _vertexColors = analysis.GetColorCloudForAnalysis();
- break;
- case Elevation analysis:
- _vertexColors = analysis.GetColorCloudForAnalysis(averagedDepthFrameData, sensorElevation);
- break;
- case Slope analysis:
- _vertexColors = analysis.GetColorCloudForAnalysis(averagedDepthFrameData,
- trimmedWidth, trimmedHeight, depthPixelSize.x, depthPixelSize.y);
- break;
- case Aspect analysis:
- _vertexColors = analysis.GetColorCloudForAnalysis(averagedDepthFrameData,
- trimmedWidth, trimmedHeight);
- break;
- }
- Core.LogTiming(ref output, timer, "Point cloud analysis"); // Debug Info
-
- // Generate the mesh itself
- _quadMesh = Core.CreateQuadMesh(_quadMesh, allPoints, _vertexColors, trimmedWidth, trimmedHeight);
- if (keepFrames > 1)
- outputMesh.Insert(0, _quadMesh.DuplicateMesh()); // Clone and prepend if keeping frames
- else
- outputMesh.Add(_quadMesh);
-
- Core.LogTiming(ref output, timer, "Meshing"); // Debug Info
-
- // Produce 2nd type of analysis that acts on the mesh and creates new geometry
- _outputGeometry = new List();
- foreach (var enabledAnalysis in Analysis.AnalysisManager.GetEnabledMeshAnalytics())
- switch (enabledAnalysis)
- {
- case Contours analysis:
- analysis.GetGeometryForAnalysis(ref _outputGeometry, _contourInterval, _quadMesh);
- break;
- case WaterLevel analysis:
- analysis.GetGeometryForAnalysis(ref _outputGeometry, _waterLevel, _quadMesh);
- break;
- }
- Core.LogTiming(ref output, timer, "Mesh analysis"); // Debug Info
-
- // Trim the outputMesh List to length specified in keepFrames
- if (keepFrames > 1 && keepFrames < outputMesh.Count)
- {
- var framesToRemove = outputMesh.Count - keepFrames;
- outputMesh.RemoveRange(keepFrames, framesToRemove > 0 ? framesToRemove : 0);
- }
-
- DA.SetDataList(0, outputMesh);
- DA.SetDataList(1, _outputGeometry);
- DA.SetDataList(2, output); // For logging/debugging
-
- ScheduleSolve();
- }
- }
-}
\ No newline at end of file
diff --git a/SandWorm/Components/PointCloudComponent.cs b/SandWorm/Components/PointCloudComponent.cs
deleted file mode 100644
index c28237e..0000000
--- a/SandWorm/Components/PointCloudComponent.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Linq;
-using Grasshopper.Kernel;
-using Grasshopper.Kernel.Types;
-using Microsoft.Kinect;
-using Rhino.Geometry;
-using SandWorm.Components;
-using SandWorm.Properties;
-
-namespace SandWorm
-{
- public class PointCloudComponent : BaseKinectComponent
- {
- private bool _showPixelColor = true;
- private bool _writeOutPoints = false;
- private double _pixelSize = 1.0;
- // Outputs
- private PointCloud _pointCloud;
- private List _outputPoints;
-
- public PointCloudComponent() : base("Sandworm Point Cloud", "SW PCloud",
- "Visualise Kinect depth data as a point cloud", "Visualisation")
- {
- }
-
- protected override Bitmap Icon => Resources.icons_points;
-
- public override Guid ComponentGuid => new Guid("be133406-49aa-4b8d-a622-23f77161b03f");
-
- protected override void RegisterInputParams(GH_InputParamManager pManager)
- {
- pManager.AddBooleanParameter("ShowColor", "PC", "Whether pixels are colored or not.", GH_ParamAccess.item,
- _showPixelColor);
- pManager.AddBooleanParameter("PointsList", "PL", "Write out a list of points (will be slow).",
- GH_ParamAccess.item, _writeOutPoints);
- pManager.AddNumberParameter("PointSize", "PS", "The size of the points within the point cloud, as displayed by this component.",
- GH_ParamAccess.item, _pixelSize);
- pManager.AddIntegerParameter("AverageFrames", "AF",
- "Amount of depth frames to average across. This number has to be greater than zero.",
- GH_ParamAccess.item, averageFrames);
- pManager.AddIntegerParameter("BlurRadius", "BR", "Radius for Gaussian blur.", GH_ParamAccess.item,
- blurRadius);
- pManager.AddGenericParameter("SandwormOptions", "SWO", "Setup & Calibration options", GH_ParamAccess.item);
- pManager[0].Optional = true;
- pManager[1].Optional = true;
- pManager[2].Optional = true;
- pManager[3].Optional = true;
- pManager[4].Optional = true;
- pManager[5].Optional = true;
- }
-
- protected override void RegisterOutputParams(GH_OutputParamManager pManager)
- {
- pManager.AddPointParameter("Point Cloud", "PC", "Point Cloud", GH_ParamAccess.list);
- pManager.AddPointParameter("Points", "P", "Points", GH_ParamAccess.list);
- pManager.AddTextParameter("Output", "O", "Output", GH_ParamAccess.list); //debugging
- }
-
- protected override void SandwormSolveInstance(IGH_DataAccess DA)
- {
- SetupLogging();
- DA.GetData(0, ref _showPixelColor);
- DA.GetData(1, ref _writeOutPoints);
- DA.GetData(2, ref _pixelSize);
- GetSandwormOptions(DA, 5, 3, 4);
- SetupKinect();
-
- var depthFrameDataInt = new int[trimmedWidth * trimmedHeight];
- var averagedDepthFrameData = new double[trimmedWidth * trimmedHeight];
-
- // Initialize outputs
- if (keepFrames <= 1 || _outputPoints == null)
- {
- // Don't replace prior frames (by clearing array) if using keepFrames
- _outputPoints = new List();
- }
-
- SetupRenderBuffer(depthFrameDataInt, null);
- Core.LogTiming(ref output, timer, "Initial setup"); // Debug Info
-
- AverageAndBlurPixels(depthFrameDataInt, ref averagedDepthFrameData);
-
- GeneratePointCloud(averagedDepthFrameData);
-
- // Assign colors and pixels to the point cloud
- _pointCloud = new PointCloud(); // TODO intelligently manipulate the point cloud
- // TODO: actually color pixels by their RGB values and/or using analytic methods
- if (allPoints.Length > 0)
- {
- for (int i = 0; i < allPoints.Length; i++) // allPoints.Length
- {
- if (_showPixelColor)
- {
- _pointCloud.Add(allPoints[i], Color.Aqua);
- }
- else
- {
- _pointCloud.Add(allPoints[i]);
- }
- }
- Core.LogTiming(ref output, timer, "Point cloud construction"); // Debug Info
- }
-
- //DA.SetDataList(0, ); // TODO: pointcloud output
- if (_writeOutPoints)
- {
- // Cast to GH_Point for performance reasons
- DA.SetDataList(0, allPoints.Select(x => new GH_Point(x)));
- }
- DA.SetDataList(1, output); // For logging/debugging
- ScheduleSolve();
- }
-
- //Draw all points in this method.
- public override void DrawViewportWires(IGH_PreviewArgs args)
- {
- if (_pointCloud == null)
- return;
- else
- args.Display.DrawPointCloud(_pointCloud, (float)_pixelSize);
- }
- }
-}
\ No newline at end of file
diff --git a/SandWorm/Components/SandWormComponent.cs b/SandWorm/Components/SandWormComponent.cs
new file mode 100644
index 0000000..6561d5e
--- /dev/null
+++ b/SandWorm/Components/SandWormComponent.cs
@@ -0,0 +1,428 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.Drawing;
+using System.Linq;
+using System.Numerics;
+
+using Rhino;
+using Rhino.Geometry;
+
+using Grasshopper.Kernel;
+
+using SandWorm.Analytics;
+using static SandWorm.Core;
+using static SandWorm.Structs;
+using static SandWorm.SandWormComponentUI;
+using Microsoft.Azure.Kinect.Sensor;
+
+namespace SandWorm
+{
+ public class SandWormComponent : GH_ExtendableComponent
+ {
+ #region Class Variables
+
+ // Units & dimensions
+ public static double unitsMultiplier = 1;
+
+ private int activeHeight = 0;
+ private int activeWidth = 0;
+ private int trimmedHeight;
+ private int trimmedWidth;
+
+ private double _left;
+ private double _right;
+
+ // Data arrays
+ private BGRA[] rgbArray;
+ private Point3d[] allPoints;
+ private Color[] _vertexColors;
+ private Mesh quadMesh;
+ private PointCloud pointCloud;
+ private List colorPalettes;
+ private List labels;
+ private Mesh waterMesh;
+
+ private readonly LinkedList renderBuffer = new LinkedList();
+ private int[] runningSum;
+ private Vector2[] trimmedXYLookupTable = null;
+ private Vector3?[] trimmedBooleanMatrix;
+ private Color[] trimmedRGBArray;
+
+ // Cut & Fill analysis
+ private double?[] baseMeshElevationPoints;
+ private Mesh baseMesh;
+ private Rhino.Display.DisplayMaterial meshMaterial;
+ private Color meshColor = Color.FromArgb(128, 128, 128);
+
+ // Water flow analysis
+ private Color blueWaterSurface = Color.FromArgb(75, 190, 255);
+ private Color blueFlowLines = Color.FromArgb(75, 170, 255);
+ private Rhino.Display.DisplayMaterial material;
+ private ConcurrentDictionary flowLines;
+ Color[] waterColors = null;
+
+ // Outputs
+ private List _outputWaterSurface;
+ private List _outputContours;
+
+ // Debugging
+ public static List stats;
+ protected Stopwatch timer;
+
+ public bool reset;
+
+ #endregion
+
+ public SandWormComponent()
+ : base("Sandworm Mesh", "SW Mesh",
+ "Visualise Kinect depth data as a mesh", "SandWorm", "Visualisation")
+ {
+ }
+
+ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
+ {
+ pManager.AddBooleanParameter("Reset", "reset", "Re-initialise the camera stream and its processing. Use if your output is strange or behaving unexpectedly. ", GH_ParamAccess.item, reset);
+ pManager.AddColourParameter("Color list", "color list", "Provide a list of custom colors to define a gradient for the elevation analysis.", GH_ParamAccess.list);
+ pManager.AddGenericParameter("Mesh", "mesh", "Provide a base mesh for the Cut & Fill analysis mode.", GH_ParamAccess.item);
+
+ pManager[1].Optional = true;
+ pManager[2].Optional = true;
+ }
+
+ protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
+ {
+ pManager.AddGeometryParameter("Terrain", "terrain", "", GH_ParamAccess.list);
+ pManager.AddGeometryParameter("Water surface", "water surface", "", GH_ParamAccess.list);
+ pManager.AddCurveParameter("Contours", "contours", "", GH_ParamAccess.list);
+ pManager.AddGenericParameter("Stats", "stats", "", GH_ParamAccess.item);
+ }
+
+ protected override void Setup(GH_ExtendableComponentAttributes attr) // Initialize the UI
+ {
+ MainComponentUI(attr);
+ }
+
+ protected override void OnComponentLoaded()
+ {
+ base.OnComponentLoaded();
+ }
+
+ protected override void SolveInstance(IGH_DataAccess DA)
+ {
+ DA.GetData(0, ref reset);
+ if (_calibrate.Active)
+ {
+ _sensorElevation.Value = Calibrate((KinectTypes)_sensorType.Value);
+ ResetDataArrays();
+ }
+
+ if (reset || _reset)
+ {
+
+ if ((KinectTypes)_sensorType.Value == KinectTypes.KinectForWindows)
+ KinectForWindows.RemoveRef();
+ else
+ KinectAzureController.RemoveRef();
+
+ ResetDataArrays();
+ unitsMultiplier = GeneralHelpers.ConvertDrawingUnits(RhinoDoc.ActiveDoc.ModelUnitSystem);
+ }
+
+ if (_resize)
+ {
+ ResetDataArrays();
+ }
+
+ // Flip left and right columns for Kinect for Windows
+ GeneralHelpers.SwapLeftRight((KinectTypes)_sensorType.Value, _leftColumns.Value, _rightColumns.Value, ref _left, ref _right);
+ GeneralHelpers.SetupLogging(ref timer, ref stats);
+
+
+
+ // Trim
+ GetTrimmedDimensions((KinectTypes)_sensorType.Value, ref trimmedWidth, ref trimmedHeight, runningSum,
+ _bottomRows.Value, _topRows.Value, _left, _right);
+
+
+ // Initialize
+ int[] depthFrameDataInt = new int[trimmedWidth * trimmedHeight];
+ double[] averagedDepthFrameData = new double[trimmedWidth * trimmedHeight];
+ allPoints = new Point3d[trimmedWidth * trimmedHeight];
+ _outputWaterSurface = new List();
+ _outputContours = new List();
+
+ if(material == null)
+ {
+ material = new Rhino.Display.DisplayMaterial(blueWaterSurface, blueWaterSurface, blueWaterSurface, blueWaterSurface, 0.7, 0.5);
+ meshMaterial = new Rhino.Display.DisplayMaterial(meshColor, meshColor, meshColor, meshColor, 0.7, 0.5);
+ }
+
+
+ if (runningSum == null)
+ runningSum = Enumerable.Range(1, depthFrameDataInt.Length).Select(i => new int()).ToArray();
+
+ SetupRenderBuffer(depthFrameDataInt, (KinectTypes)_sensorType.Value, ref rgbArray, _analysisType.Value,
+ _left, _right, _bottomRows.Value, _topRows.Value, _sensorElevation.Value, ref activeHeight,
+ ref activeWidth, _averagedFrames.Value, runningSum, renderBuffer);
+
+
+ if (trimmedXYLookupTable == null) // Only recalculate on reset
+ {
+ trimmedXYLookupTable = new Vector2[trimmedWidth * trimmedHeight];
+ trimmedBooleanMatrix = new Vector3?[trimmedWidth * trimmedHeight];
+
+ switch ((KinectTypes)_sensorType.Value)
+ {
+ case KinectTypes.KinectAzureNear:
+ case KinectTypes.KinectAzureWide:
+ Core.TrimXYLookupTable(KinectAzureController.idealXYCoordinates, trimmedXYLookupTable,
+ _leftColumns.Value, _rightColumns.Value, _bottomRows.Value, _topRows.Value, activeHeight, activeWidth);
+
+ Core.SetupCorrectiveLookupTables(KinectAzureController.idealXYCoordinates, KinectAzureController.verticalTiltCorrectionMatrix,
+ KinectAzureController.undistortMatrix, trimmedBooleanMatrix,
+ _leftColumns.Value, _rightColumns.Value, _bottomRows.Value, _topRows.Value, activeHeight, activeWidth);
+ break;
+
+ case KinectTypes.KinectForWindows:
+ Core.TrimXYLookupTable(KinectForWindows.idealXYCoordinates, trimmedXYLookupTable,
+ _left, _right, _bottomRows.Value, _topRows.Value, activeHeight, activeWidth);
+ break;
+ }
+ }
+#if DEBUG
+ GeneralHelpers.LogTiming(ref stats, timer, "Initial setup"); // Debug Info
+#endif
+ AverageAndBlurPixels(depthFrameDataInt, ref averagedDepthFrameData, runningSum, renderBuffer,
+ _sensorElevation.Value, _averagedFrames.Value, _blurRadius.Value, trimmedWidth, trimmedHeight);
+
+ GeneratePointCloud(averagedDepthFrameData, trimmedXYLookupTable,
+ KinectAzureController.verticalTiltCorrectionMatrix, (KinectTypes)_sensorType.Value,
+ allPoints, renderBuffer, trimmedWidth, trimmedHeight, _sensorElevation.Value, _averagedFrames.Value);
+
+ #region RGB from camera
+ if ((AnalysisTypes)_analysisType.Value == AnalysisTypes.Camera)
+ {
+ trimmedRGBArray = new Color[trimmedHeight * trimmedWidth];
+ TrimColorArray(rgbArray, ref trimmedRGBArray, (KinectTypes)_sensorType.Value,
+ _left, _right, _bottomRows.Value, _topRows.Value, activeHeight, activeWidth);
+ }
+ #endregion
+
+ #region Cut Fill
+ // Calculate elevation points from mesh provided for Cut & Fill analysis. Only do this on reset.
+ DA.GetData(2, ref baseMesh);
+ if ((AnalysisTypes)_analysisType.Value == AnalysisTypes.CutFill && baseMeshElevationPoints == null)
+ baseMeshElevationPoints = CutFill.MeshToPointArray(baseMesh, allPoints);
+ #endregion
+
+ colorPalettes = new List();
+ DA.GetDataList(1, colorPalettes);
+
+ GenerateMeshColors(ref _vertexColors, (AnalysisTypes)_analysisType.Value, averagedDepthFrameData,
+ trimmedXYLookupTable, trimmedRGBArray, _colorGradientRange.Value,
+ (Structs.ColorPalettes)_colorPalette.Value, colorPalettes, baseMeshElevationPoints,
+ allPoints, ref stats, _sensorElevation.Value, trimmedWidth, trimmedHeight);
+#if DEBUG
+ GeneralHelpers.LogTiming(ref stats, timer, "Point cloud analysis");
+#endif
+
+ #region Contour lines
+ if (_contourIntervalRange.Value > 0)
+ {
+ ContoursFromPoints.GetGeometryForAnalysis(ref _outputContours, allPoints,
+ (int)_contourIntervalRange.Value, trimmedWidth,
+ trimmedHeight, (int)_contourRoughness.Value);
+ if (Params.Output[2].Recipients.Count > 0)
+ {
+ Grasshopper.Kernel.Types.GH_Line[] ghLines = GeneralHelpers.ConvertLineToGHLine(_outputContours);
+ DA.SetDataList(2, ghLines);
+ }
+ }
+#if DEBUG
+ GeneralHelpers.LogTiming(ref stats, timer, "Contour lines");
+#endif
+ #endregion
+
+ #region Labels
+ if (_labelSpacing.Value > 0)
+ {
+ labels = new List();
+ GeneralHelpers.CreateLabels(allPoints, ref labels, (AnalysisTypes)_analysisType.Value,
+ baseMeshElevationPoints, trimmedWidth, trimmedHeight, (int)_labelSpacing.Value);
+ }
+#if DEBUG
+ GeneralHelpers.LogTiming(ref stats, timer, "Labels");
+#endif
+ #endregion
+
+ #region Flow lines
+ if (_flowLinesLength.Value > 0)
+ {
+ if (flowLines == null)
+ flowLines = new ConcurrentDictionary();
+
+ FlowLine.DistributeRandomRaindrops(ref allPoints, ref flowLines, (int)_raindropSpacing.Value);
+ FlowLine.GrowAndRemoveFlowlines(allPoints, flowLines, trimmedWidth, _flowLinesLength.Value);
+ } else
+ flowLines = null;
+#if DEBUG
+ GeneralHelpers.LogTiming(ref stats, timer, "Flow lines");
+#endif
+ #endregion
+
+
+ if ((OutputTypes)_outputType.Value == OutputTypes.Mesh)
+ {
+ pointCloud = null;
+ quadMesh = CreateQuadMesh(ref quadMesh, allPoints, ref _vertexColors, ref trimmedBooleanMatrix, (KinectTypes)_sensorType.Value, trimmedWidth, trimmedHeight);
+ DA.SetDataList(0, new List { quadMesh });
+
+//#if DEBUG
+ GeneralHelpers.LogTiming(ref stats, timer, "Meshing"); // Debug Info
+//#endif
+ if (_simulateFloodEvent.Active)
+ {
+ MeshFlow.CalculateWaterHeadArray(allPoints, averagedDepthFrameData, trimmedWidth, trimmedHeight, _makeItRain.Active);
+ _makeItRain.Active = false;
+ GeneralHelpers.LogTiming(ref stats, timer, "Mesh Flow");
+ waterMesh = CreateQuadMesh(ref waterMesh, MeshFlow.waterElevationPoints, ref waterColors, ref trimmedBooleanMatrix, (KinectTypes)_sensorType.Value, trimmedWidth, trimmedHeight);
+
+ }
+ }
+ else if ((OutputTypes)_outputType.Value == OutputTypes.PointCloud)
+ {
+ pointCloud = new PointCloud();
+
+ if (_vertexColors.Length > 0)
+ pointCloud.AddRange(allPoints, _vertexColors);
+ else
+ pointCloud.AddRange(allPoints);
+#if DEBUG
+ GeneralHelpers.LogTiming(ref stats, timer, "Point cloud display"); // Debug Info
+#endif
+ }
+
+ if (_waterLevel.Value > 0)
+ {
+ WaterLevel.GetGeometryForAnalysis(ref _outputWaterSurface, _waterLevel.Value, quadMesh);
+ if (Params.Output[1].Recipients.Count > 0)
+ DA.SetDataList(1, _outputWaterSurface);
+ }
+
+ DA.SetDataList(3, stats);
+ ScheduleSolve();
+ }
+ public override void DrawViewportMeshes(IGH_PreviewArgs args)
+ {
+ if ((OutputTypes)_outputType.Value == OutputTypes.Mesh && quadMesh != null)
+ {
+ if ((AnalysisTypes)_analysisType.Value != AnalysisTypes.None)
+ args.Display.DrawMeshFalseColors(quadMesh);
+ else
+ args.Display.DrawMeshShaded(quadMesh, meshMaterial);
+ }
+
+
+ if (_waterLevel.Value > 0 && _outputWaterSurface.Count > 0 && Params.Output[1].Recipients.Count == 0)
+ args.Display.DrawMeshShaded((Mesh)_outputWaterSurface[0], material);
+
+ if (_simulateFloodEvent.Active && waterMesh != null)
+ args.Display.DrawMeshShaded(waterMesh, material);
+ }
+ public override void DrawViewportWires(IGH_PreviewArgs args)
+ {
+ if (pointCloud != null)
+ args.Display.DrawPointCloud(pointCloud, 3);
+
+ var grayscaled = (int)(_labelBrightness.Value * 12.75); // 0-20 into 0-255
+ var labelAndContourColor = Color.FromArgb(grayscaled, grayscaled, grayscaled);
+
+ if (_outputContours != null && _outputContours.Count != 0 && Params.Output[2].Recipients.Count == 0)
+ args.Display.DrawLines(_outputContours, labelAndContourColor, 1);
+
+ if (_labelSpacing.Value > 0 && ((AnalysisTypes)_analysisType.Value == AnalysisTypes.CutFill || (AnalysisTypes)_analysisType.Value == AnalysisTypes.Elevation))
+ {
+ if (labels != null)
+ foreach (var text in labels)
+ args.Display.Draw3dText(text, labelAndContourColor);
+ }
+
+ if (_flowLinesLength.Value > 0)
+ {
+ foreach (var kvp in flowLines)
+ args.Display.DrawPolyline(kvp.Value.Polyline, blueFlowLines);
+ }
+ }
+ public override BoundingBox ClippingBox
+ {
+ get
+ {
+ if (quadMesh != null)
+ return quadMesh.GetBoundingBox(false);
+ else
+ return new BoundingBox();
+ }
+ }
+ protected override Bitmap Icon => Properties.Resources.Icons_Main;
+ public override Guid ComponentGuid => new Guid("{53fefb98-1cec-4134-b707-0c366072af2c}");
+ public override void AddedToDocument(GH_Document document)
+ {
+ if (Params.Input[0].SourceCount == 0)
+ {
+ List componentList = new List();
+ PointF pivot;
+ pivot = Attributes.Pivot;
+
+ var reset = new Grasshopper.Kernel.Special.GH_ButtonObject();
+ reset.CreateAttributes();
+ reset.NickName = "reset";
+ reset.Attributes.Pivot = new PointF(pivot.X - 200, pivot.Y - 38);
+ reset.Attributes.ExpireLayout();
+ reset.Attributes.PerformLayout();
+ componentList.Add(reset);
+
+ Params.Input[0].AddSource(reset);
+
+
+ foreach (var component in componentList)
+ document.AddObject(component, false);
+
+
+ document.UndoUtil.RecordAddObjectEvent("Add buttons", componentList);
+ }
+
+ }
+ protected void ScheduleSolve()
+ {
+ OnPingDocument().ScheduleSolution(GeneralHelpers.ConvertFPStoMilliseconds(_refreshRate.Value), ScheduleDelegate);
+ }
+ protected void ScheduleDelegate(GH_Document doc)
+ {
+ ExpireSolution(false);
+ }
+ private void ResetDataArrays()
+ {
+ quadMesh = null;
+ trimmedXYLookupTable = null;
+ runningSum = null;
+ renderBuffer.Clear();
+ baseMeshElevationPoints = null;
+ flowLines = null;
+ waterMesh = null;
+ MeshFlow.waterHead = null;
+
+ if (MeshFlow.accelerator != null)
+ {
+ MeshFlow.accelerator.Dispose();
+ MeshFlow.context.Dispose();
+ }
+
+
+ _calibrate.Active = false; // Reset the button
+ _resize = false;
+ _reset = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/Components/SetupComponent.cs b/SandWorm/Components/SetupComponent.cs
deleted file mode 100644
index 64f528b..0000000
--- a/SandWorm/Components/SetupComponent.cs
+++ /dev/null
@@ -1,195 +0,0 @@
-using System;
-using System.Drawing;
-using System.Linq;
-using Grasshopper.Kernel;
-using Grasshopper.Kernel.Special;
-using Rhino;
-using SandWorm.Components;
-using SandWorm.Properties;
-
-namespace SandWorm
-{
- public class SetupComponent : BaseKinectComponent
- {
- public double averagedSensorElevation;
- public bool calibrateSandworm;
- public new double[] elevationArray = Enumerable.Range(1, 217088).Select(i => new double()).ToArray();
-
- public int frameCount; // Number of frames to average the calibration across
-
- public SetupComponent() : base("Setup Component", "SWSetup",
- "This component takes care of all the setup & calibration of your sandbox.", "Utility")
- {
- }
-
- protected override Bitmap Icon => Resources.icons_setup;
-
- public override Guid ComponentGuid => new Guid("9ee53381-c269-4fff-9d45-8a2dbefc243c");
-
- protected override void RegisterInputParams(GH_InputParamManager pManager)
- {
- pManager.AddBooleanParameter("CalibrateSandworm", "CS",
- "Set to true to initiate the calibration process.", GH_ParamAccess.item, calibrateSandworm);
- pManager.AddNumberParameter("SensorHeight", "SH",
- "The height (in document units) of the sensor above your model.", GH_ParamAccess.item, sensorElevation);
- pManager.AddIntegerParameter("LeftColumns", "LC",
- "Number of columns to trim from the left.", GH_ParamAccess.item, 0);
- pManager.AddIntegerParameter("RightColumns", "RC",
- "Number of columns to trim from the right.", GH_ParamAccess.item, 0);
- pManager.AddIntegerParameter("TopRows", "TR",
- "Number of rows to trim from the top.", GH_ParamAccess.item, 0);
- pManager.AddIntegerParameter("BottomRows", "BR",
- "Number of rows to trim from the bottom.", GH_ParamAccess.item, 0);
- pManager.AddIntegerParameter("TickRate", "TR",
- "The time interval, in milliseconds, to update geometry from the Kinect. Set as 0 to disable automatic updates.",
- GH_ParamAccess.item, tickRate);
- pManager.AddIntegerParameter("KeepFrames", "KF",
- "Output a running list of frame updates rather than just the current frame. Set to 1 or 0 to disable.",
- GH_ParamAccess.item, keepFrames);
- pManager[0].Optional = true;
- pManager[1].Optional = true;
- pManager[2].Optional = true;
- pManager[3].Optional = true;
- pManager[4].Optional = true;
- pManager[5].Optional = true;
- pManager[6].Optional = true;
- pManager[7].Optional = true;
- }
-
- protected override void RegisterOutputParams(GH_OutputParamManager pManager)
- {
- pManager.AddGenericParameter("Options", "O", "SandWorm Options", GH_ParamAccess.item);
- pManager.AddTextParameter("Info", "I", "Component info", GH_ParamAccess.list); //debugging
- }
-
- private void ManipulateSlider(GH_Document doc)
- {
- var input = Params.Input[1].Sources[0]; // Get the first thing connected to the second input of this component
-
- if (input is GH_NumberSlider slider)
- {
- slider.Slider.RaiseEvents = false;
- slider.Slider.DecimalPlaces = 4;
- slider.SetSliderValue((decimal) averagedSensorElevation);
- slider.ExpireSolution(false);
- slider.Slider.RaiseEvents = true;
- AddRuntimeMessage(GH_RuntimeMessageLevel.Remark,
- "Calibration measurement finished; sensor elevation measured and set as " + averagedSensorElevation);
- }
- }
-
- protected override void SandwormSolveInstance(IGH_DataAccess DA)
- {
- SetupLogging();
-
- DA.GetData(0, ref calibrateSandworm);
- DA.GetData(1, ref sensorElevation);
- DA.GetData(2, ref leftColumns);
- DA.GetData(3, ref rightColumns);
- DA.GetData(4, ref topRows);
- DA.GetData(5, ref bottomRows);
- DA.GetData(6, ref tickRate);
- DA.GetData(7, ref keepFrames);
-
- // Initialize all arrays
- var trimmedWidth = KinectController.depthWidth - leftColumns - rightColumns;
- var trimmedHeight = KinectController.depthHeight - topRows - bottomRows;
-
- var depthFrameDataInt = new int[trimmedWidth * trimmedHeight];
- var averagedDepthFrameData = new double[trimmedWidth * trimmedHeight];
- if (elevationArray.Length != trimmedWidth * trimmedHeight
- ) // Only create a new elevation array when user resizes the mesh
- elevationArray = new double[trimmedWidth * trimmedHeight];
-
- averagedSensorElevation = sensorElevation;
- var unitsMultiplier = Core.ConvertDrawingUnits(RhinoDoc.ActiveDoc.ModelUnitSystem);
-
- if (calibrateSandworm) frameCount = 60; // Start calibration
-
- if (frameCount > 1) // Iterate a pre-set number of times
- {
- output.Add("Reading frame: " + frameCount); // Debug Info
-
- // Trim the depth array and cast ushort values to int
- Core.CopyAsIntArray(KinectController.depthFrameData, depthFrameDataInt, leftColumns, rightColumns,
- topRows, bottomRows, KinectController.depthHeight, KinectController.depthWidth);
-
- renderBuffer.AddLast(depthFrameDataInt);
- for (var pixel = 0; pixel < depthFrameDataInt.Length; pixel++)
- {
- if (depthFrameDataInt[pixel] > 200) // We have a valid pixel.
- {
- runningSum[pixel] += depthFrameDataInt[pixel];
- }
- else
- {
- if (pixel > 0) // Pixel is invalid and we have a neighbor to steal information from
- {
- runningSum[pixel] += depthFrameDataInt[pixel - 1];
- // Replace the zero value from the depth array with the one from the neighboring pixel
- renderBuffer.Last.Value[pixel] = depthFrameDataInt[pixel - 1];
- }
- else // Pixel is invalid and it is the first one in the list. (No neighbor on the left hand side, so we set it to the lowest point on the table)
- {
- runningSum[pixel] += (int) sensorElevation;
- renderBuffer.Last.Value[pixel] = (int) sensorElevation;
- }
- }
- averagedDepthFrameData[pixel] = runningSum[pixel] / renderBuffer.Count; // Calculate average values
- }
-
- frameCount--;
- if (frameCount == 1) // All frames have been collected, we can save the results
- {
- // Measure sensor elevation by averaging over a grid of 20x20 pixels in the center of the table
- var counter = 0;
- averagedSensorElevation = 0;
-
- for (var y = trimmedHeight / 2 - 10; y < trimmedHeight / 2 + 10; y++) // Iterate over y dimension
- for (var x = trimmedWidth / 2 - 10; x < trimmedWidth / 2 + 10; x++) // Iterate over x dimension
- {
- var i = y * trimmedWidth + x;
- averagedSensorElevation += averagedDepthFrameData[i];
- counter++;
- }
-
- averagedSensorElevation /= counter;
-
- // Counter for Kinect inaccuracies and potential hardware misalignment by storing differences between the averaged sensor elevation and individual pixels.
- for (var i = 0; i < averagedDepthFrameData.Length; i++)
- elevationArray[i] = averagedDepthFrameData[i] - averagedSensorElevation;
-
- averagedSensorElevation *= unitsMultiplier;
-
- renderBuffer.Clear();
- Array.Clear(runningSum, 0, runningSum.Length);
- }
-
- if (frameCount > 1)
- ScheduleSolve(); // Schedule another solution to get more data from Kinect
- else
- OnPingDocument().ScheduleSolution(5, ManipulateSlider);
- }
-
- output.Add("Parameter-Provided Sensor Elevation: " + sensorElevation); // Debug Info
- output.Add("Measured-Average Sensor Elevation: " + averagedSensorElevation); // Debug Info
- output.Add("Output Sensor Elevation: " + averagedSensorElevation); // Debug Info
-
- var outputOptions = new SetupOptions
- {
- SensorElevation = averagedSensorElevation,
- LeftColumns = leftColumns,
- RightColumns = rightColumns,
- TopRows = topRows,
- BottomRows = bottomRows,
- TickRate = tickRate,
- KeepFrames = keepFrames,
- ElevationArray = elevationArray
- };
-
- Core.LogTiming(ref output, timer, "Setup completion"); // Debug Info
- DA.SetData(0, outputOptions);
- DA.SetDataList(1, output); // For logging/debugging
- }
- }
-}
\ No newline at end of file
diff --git a/SandWorm/ComponentsUI/SandWormComponentUI.cs b/SandWorm/ComponentsUI/SandWormComponentUI.cs
new file mode 100644
index 0000000..4976225
--- /dev/null
+++ b/SandWorm/ComponentsUI/SandWormComponentUI.cs
@@ -0,0 +1,289 @@
+using System;
+
+namespace SandWorm
+{
+ class SandWormComponentUI
+ {
+ #region Class variables
+
+ public static bool _reset = false;
+ public static bool _resize = false;
+
+ public static MenuDropDown _sensorType;
+ public static MenuDropDown _refreshRate;
+ public static MenuSlider _sensorElevation;
+ public static MenuButton _calibrate;
+ public static MenuSlider _leftColumns;
+ public static MenuSlider _rightColumns;
+ public static MenuSlider _topRows;
+ public static MenuSlider _bottomRows;
+
+ public static MenuDropDown _outputType;
+ public static MenuDropDown _analysisType;
+ public static MenuDropDown _colorPalette;
+ public static MenuSlider _colorGradientRange;
+ public static MenuSlider _contourIntervalRange;
+ public static MenuSlider _contourRoughness;
+ public static MenuSlider _waterLevel;
+ public static MenuSlider _raindropSpacing;
+ public static MenuSlider _flowLinesLength;
+ public static MenuCheckBox _simulateFloodEvent;
+ public static MenuButton _makeItRain;
+ public static MenuSlider _labelSpacing;
+ public static MenuSlider _labelBrightness;
+
+ public static MenuSlider _averagedFrames;
+ public static MenuSlider _blurRadius;
+
+ private static int _previousAnalysisMode;
+ private static double _previousColorGradientRange;
+ #endregion
+ public static void MainComponentUI(GH_ExtendableComponentAttributes attr)
+ {
+ #region Sensor Type
+ MenuPanel optionsMenuPanel = new MenuPanel(0, "panel_options")
+ {
+ Name = "Options for the SandWorm component.", // <- mouse over header for the entire "options" fold-out.
+ Header = "Define custom parameters here." // <- mouse over description
+ };
+
+ GH_ExtendableMenu optionsMenu = new GH_ExtendableMenu(0, "menu_options")
+ {
+ Name = "Sensor", // <- Foldable header
+ Header = "Setup the Kinect Sensor." // <- Foldable mouseOver
+ };
+
+ MenuStaticText sensorTypeHeader = new MenuStaticText("Sensor type", "Choose which Kinect Version you have.");
+ _sensorType = new MenuDropDown(0, "Sensor type", "Choose Kinect Version");
+ _sensorType.AddItem("Kinect Azure Narrow", "Kinect Azure Narrow");
+ _sensorType.AddItem("Kinect Azure Wide", "Kinect Azure Wide");
+ _sensorType.AddItem("Kinect for Windows", "Kinect for Windows");
+
+ _sensorType.ValueChanged += _sensorType__ValueChanged;
+
+ MenuStaticText refreshRateHeader = new MenuStaticText("Refresh rate", "Choose the refresh rate of the model.");
+ _refreshRate = new MenuDropDown(11111, "Refresh rate", "Choose Refresh Rate");
+ _refreshRate.AddItem("Max", "Max");
+ _refreshRate.AddItem("15 FPS", "15 FPS");
+ _refreshRate.AddItem("5 FPS", "5 FPS");
+ _refreshRate.AddItem("1 FPS", "1 FPS");
+ _refreshRate.AddItem("0.2 FPS", "0.2 FPS");
+
+ // Initiate the automatic calibration process to determine elevation.
+ _calibrate = new MenuButton(111121,
+ "Calibrate Elevation",
+ "Automatically estimates the Elevation distance using the depth camera.");
+
+ MenuStaticText sensorElevationHeader = new MenuStaticText("Sensor elevation", "Distance between the sensor and the table. \nInput should be in millimeters.\nTo automatically estimate this value, check the 'Calibrate' checkbox and reset.");
+ _sensorElevation = new MenuSlider(sensorElevationHeader, 1, 500, 2000, 1000, 0);
+
+ MenuStaticText leftColumnsHeader = new MenuStaticText("Left columns", "Number of pixels to trim from the left.");
+ _leftColumns = new MenuSlider(leftColumnsHeader, 2, 0, 200, 50, 0);
+ _leftColumns.ValueChanged += _slider__ValueChanged;
+
+ MenuStaticText rightColumnsHeader = new MenuStaticText("Right columns", "Number of pixels to trim from the right.");
+ _rightColumns = new MenuSlider(rightColumnsHeader, 3, 0, 200, 50, 0);
+ _rightColumns.ValueChanged += _slider__ValueChanged;
+
+ MenuStaticText topRowsHeader = new MenuStaticText("Top rows", "Number of pixels to trim from the top.");
+ _topRows = new MenuSlider(topRowsHeader, 4, 0, 200, 50, 0);
+ _topRows.ValueChanged += _slider__ValueChanged;
+
+ MenuStaticText bottomRowsHeader = new MenuStaticText("Bottom rows", "Number of pixels to trim from the bottom.");
+ _bottomRows = new MenuSlider(bottomRowsHeader, 5, 0, 200, 50, 0);
+ _bottomRows.ValueChanged += _slider__ValueChanged;
+
+ optionsMenu.AddControl(optionsMenuPanel);
+ attr.AddMenu(optionsMenu);
+
+ optionsMenuPanel.AddControl(sensorTypeHeader);
+ optionsMenuPanel.AddControl(_sensorType);
+ optionsMenuPanel.AddControl(refreshRateHeader);
+ optionsMenuPanel.AddControl(_refreshRate);
+ optionsMenuPanel.AddControl(_calibrate);
+ optionsMenuPanel.AddControl(sensorElevationHeader);
+ optionsMenuPanel.AddControl(_sensorElevation);
+ optionsMenuPanel.AddControl(leftColumnsHeader);
+ optionsMenuPanel.AddControl(_leftColumns);
+ optionsMenuPanel.AddControl(rightColumnsHeader);
+ optionsMenuPanel.AddControl(_rightColumns);
+ optionsMenuPanel.AddControl(topRowsHeader);
+ optionsMenuPanel.AddControl(_topRows);
+ optionsMenuPanel.AddControl(bottomRowsHeader);
+ optionsMenuPanel.AddControl(_bottomRows);
+
+ #endregion
+
+ #region Analysis
+ MenuPanel analysisPanel = new MenuPanel(20, "panel_analysis")
+ {
+ Name = "Analysis",
+ Header = "Define custom analysis parameters."
+ };
+ GH_ExtendableMenu analysisMenu = new GH_ExtendableMenu(21, "menu_analysis")
+ {
+ Name = "Analysis",
+ Header = "Define custom analysis parameters."
+ };
+
+ MenuStaticText outputTypeHeader = new MenuStaticText("Output type", "Choose which type of geometry to output.");
+ _outputType = new MenuDropDown(22, "Ouput type", "Choose type of geometry to output.");
+ _outputType.AddItem("Mesh", "Mesh");
+ _outputType.AddItem("Point Cloud", "Point Cloud");
+
+ MenuStaticText analysisTypeHeader = new MenuStaticText("Analysis type", "Choose which type of analysis to perform on scanned geometry.");
+ _analysisType = new MenuDropDown(23, "Ouput type", "Choose type of geometry to output.");
+ _analysisType.AddEnum(typeof(Structs.AnalysisTypes));
+ _analysisType.Value = (int)Structs.AnalysisTypes.Elevation;
+ _analysisType.ValueChanged += _analysisType__valueChanged;
+
+ MenuStaticText colorPaletteHeader = new MenuStaticText("Color palette", "Choose color palette for elevation analysis.");
+ _colorPalette = new MenuDropDown(231, "Color palette", "Choose color palette for elevation analysis.");
+ _colorPalette.AddEnum(typeof(Structs.ColorPalettes));
+ _colorPalette.Value = (int)Structs.ColorPalettes.Europe;
+
+ MenuStaticText colorGradientHeader = new MenuStaticText("Color gradient range", "Define maximum elevation for color gradient. \nInput should be in millimeters.");
+ _colorGradientRange = new MenuSlider(colorGradientHeader, 24, 15, 100, 40, 0);
+
+ MenuStaticText labelSpacingHeader = new MenuStaticText("Label spacing", "Define spacing between labels. \nInput is defined as distance between individual rows/columns.");
+ _labelSpacing = new MenuSlider(labelSpacingHeader, 241, 0, 100, 20, 0)
+ {
+ Step = 5
+ };
+
+ MenuStaticText labelBrightnessHeader = new MenuStaticText("Label brightness", "Defines the colour of labels and contours, going from white to black.");
+ _labelBrightness = new MenuSlider(labelBrightnessHeader, 242, 0, 20, 20, 0);
+
+ MenuStaticText contourIntervalHeader = new MenuStaticText("Contour interval", "Define spacing between contours. \nInput should be in millimeters.");
+ _contourIntervalRange = new MenuSlider(contourIntervalHeader, 25, 0, 30, 0, 0);
+
+ MenuStaticText contourRoughnessHeader = new MenuStaticText("Contour roughness", "Specify how rough contour sampling should be.");
+ _contourRoughness = new MenuSlider(contourRoughnessHeader, 26, 1, 20, 2, 0);
+
+ analysisMenu.AddControl(analysisPanel);
+ attr.AddMenu(analysisMenu);
+
+ analysisPanel.AddControl(outputTypeHeader);
+ analysisPanel.AddControl(_outputType);
+ analysisPanel.AddControl(analysisTypeHeader);
+ analysisPanel.AddControl(_analysisType);
+ analysisPanel.AddControl(colorPaletteHeader);
+ analysisPanel.AddControl(_colorPalette);
+ analysisPanel.AddControl(colorGradientHeader);
+ analysisPanel.AddControl(_colorGradientRange);
+ analysisPanel.AddControl(labelSpacingHeader);
+ analysisPanel.AddControl(_labelSpacing);
+ analysisPanel.AddControl(labelBrightnessHeader);
+ analysisPanel.AddControl(_labelBrightness);
+ analysisPanel.AddControl(contourIntervalHeader);
+ analysisPanel.AddControl(_contourIntervalRange);
+ //analysisPanel.AddControl(contourRoughnessHeader);
+ //analysisPanel.AddControl(_contourRoughness);
+
+ _previousAnalysisMode = _colorPalette.Value;
+ _previousColorGradientRange = _colorGradientRange.Value;
+ #endregion
+
+ #region Water flow
+ MenuPanel waterPanel = new MenuPanel(30, "panel_water")
+ {
+ Name = "Water Flow",
+ Header = "Simulate water flow on the surface."
+ };
+ GH_ExtendableMenu waterMenu = new GH_ExtendableMenu(31, "menu_rainwater")
+ {
+ Name = "Water Flow",
+ Header = "Simulate water flow on the surface."
+ };
+
+ _simulateFloodEvent = new MenuCheckBox(10002, "Simulate flood event", "Simulate flood event");
+ _makeItRain = new MenuButton(10003, "Make it rain", "Equally distribute water over the model.");
+
+ MenuStaticText waterLevelHeader = new MenuStaticText("Water level", "Define distance between the table and a simulated water surface. \nInput should be in millimeters.");
+ _waterLevel = new MenuSlider(waterLevelHeader, 32, 0, 100, 0, 0);
+
+ MenuStaticText flowLinesLengthHeader = new MenuStaticText("Flowlines length", "Define the maximum length of each flowline. \nInput is defined as distance between individual rows/columns.");
+ _flowLinesLength = new MenuSlider(flowLinesLengthHeader, 34, 0, 100, 0, 0);
+
+ MenuStaticText raindropSpacingHeader = new MenuStaticText("Raindrop spacing", "Define distance between the rain drops. \nInput is defined as distance between individual rows/columns.");
+ _raindropSpacing = new MenuSlider(raindropSpacingHeader, 33, 50, 1000, 500, 0)
+ {
+ Step = 10
+ };
+
+ waterMenu.AddControl(waterPanel);
+ attr.AddMenu(waterMenu);
+
+ waterPanel.AddControl(_simulateFloodEvent);
+ waterPanel.AddControl(_makeItRain);
+ waterPanel.AddControl(waterLevelHeader);
+ waterPanel.AddControl(_waterLevel);
+ waterPanel.AddControl(flowLinesLengthHeader);
+ waterPanel.AddControl(_flowLinesLength);
+ waterPanel.AddControl(raindropSpacingHeader);
+ waterPanel.AddControl(_raindropSpacing);
+ #endregion
+
+ #region Post processing
+
+ MenuPanel postProcessingPanel = new MenuPanel(40, "panel_analysis")
+ {
+ Name = "Post Processing",
+ Header = "Define custom post processing parameters."
+ };
+ GH_ExtendableMenu postProcessingMenu = new GH_ExtendableMenu(41, "menu_analysis")
+ {
+ Name = "Post Processing",
+ Header = "Define custom post processing parameters."
+ };
+
+ MenuStaticText averagedFramesHeader = new MenuStaticText("Averaged frames", "Number of frames to average across.");
+ _averagedFrames = new MenuSlider(averagedFramesHeader, 42, 1, 30, 5, 0);
+ _averagedFrames.ValueChanged += _slider__ValueChanged;
+
+ MenuStaticText blurRadiusHeader = new MenuStaticText("Blur Radius", "Define the extent of gaussian blurring.");
+ _blurRadius = new MenuSlider(blurRadiusHeader, 43, 0, 15, 5, 0);
+
+ postProcessingMenu.AddControl(postProcessingPanel);
+ attr.AddMenu(postProcessingMenu);
+
+ postProcessingPanel.AddControl(averagedFramesHeader);
+ postProcessingPanel.AddControl(_averagedFrames);
+ postProcessingPanel.AddControl(blurRadiusHeader);
+ postProcessingPanel.AddControl(_blurRadius);
+
+ #endregion
+
+ #region Callbacks
+ void _sensorType__ValueChanged(object sender, EventArgs e)
+ {
+ _reset = true;
+ }
+
+ void _slider__ValueChanged(object sender, EventArgs e)
+ {
+ _resize = true;
+ }
+
+ void _analysisType__valueChanged(object sender, EventArgs e)
+ {
+ // Set color palette to CutFill when user chooses the corresponding analysis type
+ if ((Structs.AnalysisTypes)_analysisType.Value == Structs.AnalysisTypes.CutFill)
+ {
+ _previousAnalysisMode = _colorPalette.Value;
+ _previousColorGradientRange = _colorGradientRange.Value;
+ _colorPalette.Value = (int)Structs.ColorPalettes.CutFill;
+ _colorGradientRange.Value = 15;
+ }
+
+ // Switch back to previous color palette
+ if ((Structs.AnalysisTypes)_analysisType.Value == Structs.AnalysisTypes.Elevation)
+ {
+ _colorPalette.Value = _previousAnalysisMode;
+ _colorGradientRange.Value = _previousColorGradientRange;
+ }
+ }
+ #endregion
+ }
+ }
+}
diff --git a/SandWorm/Core.cs b/SandWorm/Core.cs
deleted file mode 100644
index e23f65a..0000000
--- a/SandWorm/Core.cs
+++ /dev/null
@@ -1,160 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Drawing;
-using System.Runtime.CompilerServices;
-using Rhino.Geometry;
-
-namespace SandWorm
-{
- public static class Core
- {
- public static Mesh CreateQuadMesh(Mesh mesh, Point3f[] vertices, Color[] colors, int xStride, int yStride)
- {
- int xd = xStride; // The x-dimension of the data
- int yd = yStride; // They y-dimension of the data
-
-
- if (mesh.Faces.Count != (xStride - 2) * (yStride - 2))
- {
- mesh = new Mesh();
- mesh.Vertices.Capacity = vertices.Length; // Don't resize array
- mesh.Vertices.UseDoublePrecisionVertices = false;
- mesh.Vertices.AddVertices(vertices);
-
- for (int y = 1; y < yd - 1; y++) // Iterate over y dimension
- {
- for (int x = 1; x < xd - 1; x++) // Iterate over x dimension
- {
- int i = y * xd + x;
- int j = (y - 1) * xd + x;
-
- mesh.Faces.AddFace(j - 1, j, i, i - 1);
- }
- }
- }
- else
- {
- mesh.Vertices.UseDoublePrecisionVertices = false;
-
- unsafe
- {
- using (var meshAccess = mesh.GetUnsafeLock(true))
- {
- int arrayLength;
- Point3f* points = meshAccess.VertexPoint3fArray(out arrayLength);
- for (int i = 0; i < arrayLength; i++)
- {
- points->Z = vertices[i].Z;
- points++;
- }
- mesh.ReleaseUnsafeLock(meshAccess);
- }
- }
- }
-
- if (colors.Length > 0) // Colors only provided if the mesh style permits
- {
- mesh.VertexColors.SetColors(colors);
- }
-
- return mesh;
- }
-
- public struct PixelSize // Unfortunately no nice tuples in this version of C# :(
- {
- public double x;
- public double y;
- }
-
- public static PixelSize GetDepthPixelSpacing(double sensorHeight)
- {
- double kinect2FOVForX = 70.6;
- double kinect2FOVForY = 60.0;
- double kinect2ResolutionForX = 512;
- double kinect2ResolutionForY = 424;
-
- PixelSize pixelsForHeight = new PixelSize
- {
- x = GetDepthPixelSizeInDimension(kinect2FOVForX, kinect2ResolutionForX, sensorHeight),
- y = GetDepthPixelSizeInDimension(kinect2FOVForY, kinect2ResolutionForY, sensorHeight)
- };
- return pixelsForHeight;
- }
-
- private static double GetDepthPixelSizeInDimension(double fovAngle, double resolution, double height)
- {
- double fovInRadians = (Math.PI / 180) * fovAngle;
- double dimensionSpan = 2 * height * Math.Tan(fovInRadians / 2);
- return dimensionSpan / resolution;
- }
-
- public static void CopyAsIntArray(ushort[] source, int[] destination, int leftColumns, int rightColumns, int topRows, int bottomRows, int height, int width)
- {
- if (source == null)
- {
- return; // Triggers on initial setup
- }
-
- ref ushort ru0 = ref source[0];
- ref int ri0 = ref destination[0];
- int j = 0;
-
- for (int rows = topRows; rows < height - bottomRows; rows++)
- {
- for (int columns = rightColumns; columns < width - leftColumns; columns++)
- {
- int i = rows * width + columns;
- Unsafe.Add(ref ri0, j) = Unsafe.Add(ref ru0, i);
- j++;
- }
- }
-
- }
-
- public static double ConvertDrawingUnits (Rhino.UnitSystem units)
- {
- double unitsMultiplier = 1.0;
-
- switch (units.ToString())
- {
- case "Kilometers":
- unitsMultiplier = 0.0001;
- break;
-
- case "Meters":
- unitsMultiplier = 0.001;
- break;
-
- case "Decimeters":
- unitsMultiplier = 0.01;
- break;
-
- case "Centimeters":
- unitsMultiplier = 0.1;
- break;
-
- case "Millimeters":
- unitsMultiplier = 1.0;
- break;
-
- case "Inches":
- unitsMultiplier = 0.0393701;
- break;
-
- case "Feet":
- unitsMultiplier = 0.0328084;
- break;
- }
- return unitsMultiplier;
- }
-
- public static void LogTiming(ref List output, Stopwatch timer, string eventDescription)
- {
- var logInfo = eventDescription + ": ";
- timer.Stop();
- output.Add(logInfo.PadRight(28, ' ') + timer.ElapsedMilliseconds.ToString() + " ms");
- timer.Restart();
- }
- }
-}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/EvaluationUnit.cs b/SandWorm/CustomComponent/EvaluationUnit.cs
new file mode 100644
index 0000000..a3b74c9
--- /dev/null
+++ b/SandWorm/CustomComponent/EvaluationUnit.cs
@@ -0,0 +1,312 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Reflection;
+using GH_IO.Serialization;
+using Grasshopper.Kernel;
+using Grasshopper.Kernel.Types;
+using Rhino;
+
+namespace SandWorm
+{
+public class EvaluationUnit
+{
+ private GH_SwitcherComponent component;
+
+ private string name;
+
+ private string displayName;
+
+ private string description;
+
+ private List inputs;
+
+ private List outputs;
+
+ private bool active;
+
+ private Bitmap icon;
+
+ private bool keepLinks;
+
+ private EvaluationUnitContext cxt;
+
+ public GH_SwitcherComponent Component
+ {
+ get
+ {
+ return component;
+ }
+ set
+ {
+ component = value;
+ }
+ }
+
+ public List Inputs => inputs;
+
+ public List Outputs => outputs;
+
+ public string Name
+ {
+ get
+ {
+ return name;
+ }
+ set
+ {
+ name = value;
+ }
+ }
+
+ public string DisplayName
+ {
+ get
+ {
+ return displayName;
+ }
+ set
+ {
+ displayName = value;
+ }
+ }
+
+ public string Description
+ {
+ get
+ {
+ return description;
+ }
+ set
+ {
+ description = value;
+ }
+ }
+
+ public bool Active
+ {
+ get
+ {
+ return active;
+ }
+ set
+ {
+ active = value;
+ }
+ }
+
+ public bool KeepLinks
+ {
+ get
+ {
+ return keepLinks;
+ }
+ set
+ {
+ keepLinks = value;
+ }
+ }
+
+ public Bitmap Icon
+ {
+ get
+ {
+ return icon;
+ }
+ set
+ {
+ icon = value;
+ }
+ }
+
+ public EvaluationUnitContext Context => cxt;
+
+ public EvaluationUnit(string name, string displayName, string description, Bitmap icon = null)
+ {
+ this.name = name;
+ this.displayName = displayName;
+ this.description = description;
+ inputs = new List();
+ outputs = new List();
+ keepLinks = false;
+ this.icon = icon;
+ cxt = new EvaluationUnitContext(this);
+ }
+
+ private static Type GetGenericType(Type generic, Type toCheck)
+ {
+ while (toCheck != null && toCheck != typeof(object))
+ {
+ Type right = (toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck);
+ if (generic == right)
+ {
+ return toCheck;
+ }
+ toCheck = toCheck.BaseType;
+ }
+ return null;
+ }
+
+ public void RegisterInputParam(IGH_Param param, string name, string nickName, string description, GH_ParamAccess access, IGH_Goo defaultValue)
+ {
+ param.Name = name;
+ param.NickName = nickName;
+ param.Description = description;
+ param.Access = access;
+ try
+ {
+ if (defaultValue != null && typeof(IGH_Goo).IsAssignableFrom(param.Type))
+ {
+ Type genericType = GetGenericType(typeof(GH_PersistentParam<>), param.GetType());
+ if (genericType != null)
+ {
+ Type[] genericArguments = genericType.GetGenericArguments();
+ if (genericArguments.Length != 0)
+ {
+ _ = genericArguments[0];
+ genericType.GetMethod("SetPersistentData", BindingFlags.Instance | BindingFlags.Public, null, new Type[1]
+ {
+ genericArguments[0]
+ }, null).Invoke(param, new object[1]
+ {
+ defaultValue
+ });
+ }
+ }
+ }
+ }
+ catch (Exception)
+ {
+ }
+ ExtendedPlug extendedPlug = new ExtendedPlug(param);
+ extendedPlug.Unit = this;
+ inputs.Add(extendedPlug);
+ }
+
+ public void RegisterInputParam(IGH_Param param, string name, string nickName, string description, GH_ParamAccess access)
+ {
+ RegisterInputParam(param, name, nickName, description, access, null);
+ }
+
+ public void RegisterOutputParam(IGH_Param param, string name, string nickName, string description)
+ {
+ param.Name = name;
+ param.NickName = nickName;
+ param.Description = description;
+ ExtendedPlug extendedPlug = new ExtendedPlug(param);
+ extendedPlug.Unit = this;
+ outputs.Add(extendedPlug);
+ }
+
+ public void NewParameterIds()
+ {
+ foreach (ExtendedPlug input in inputs)
+ {
+ input.Parameter.NewInstanceGuid();
+ }
+ foreach (ExtendedPlug output in outputs)
+ {
+ output.Parameter.NewInstanceGuid();
+ }
+ }
+
+ public void AddMenu(GH_ExtendableMenu menu)
+ {
+ Context.Collection.AddMenu(menu);
+ }
+
+ public bool Write(GH_IWriter writer)
+ {
+ writer.SetString("name", Name);
+ GH_IWriter gH_IWriter = writer.CreateChunk("params");
+ GH_IWriter gH_IWriter2 = gH_IWriter.CreateChunk("input");
+ gH_IWriter2.SetInt32("index", 0);
+ gH_IWriter2.SetInt32("count", Inputs.Count);
+ for (int i = 0; i < inputs.Count; i++)
+ {
+ if (inputs[i].Parameter.Attributes != null)
+ {
+ GH_IWriter writer2 = gH_IWriter2.CreateChunk("p", i);
+ inputs[i].Parameter.Write(writer2);
+ }
+ }
+ GH_IWriter gH_IWriter3 = gH_IWriter.CreateChunk("output");
+ gH_IWriter3.SetInt32("index", 0);
+ gH_IWriter3.SetInt32("count", Outputs.Count);
+ for (int j = 0; j < outputs.Count; j++)
+ {
+ if (outputs[j].Parameter.Attributes != null)
+ {
+ GH_IWriter writer3 = gH_IWriter3.CreateChunk("p", j);
+ outputs[j].Parameter.Write(writer3);
+ }
+ }
+ GH_IWriter writer4 = writer.CreateChunk("context");
+ return cxt.Collection.Write(writer4);
+ }
+
+ public bool Read(GH_IReader reader)
+ {
+ if (reader.ChunkExists("params"))
+ {
+ GH_IReader gH_IReader = reader.FindChunk("params");
+ if (gH_IReader.ChunkExists("input") && inputs != null)
+ {
+ GH_IReader gH_IReader2 = gH_IReader.FindChunk("input");
+ int value = -1;
+ if (gH_IReader2.TryGetInt32("count", ref value) && inputs.Count == value)
+ {
+ for (int i = 0; i < value; i++)
+ {
+ if (gH_IReader2.ChunkExists("p", i))
+ {
+ inputs[i].Parameter.Read(gH_IReader2.FindChunk("p", i));
+ }
+ else if (gH_IReader2.ChunkExists("param", i))
+ {
+ inputs[i].Parameter.Read(gH_IReader2.FindChunk("param", i));
+ }
+ }
+ }
+ }
+ if (gH_IReader.ChunkExists("output") && outputs != null)
+ {
+ GH_IReader gH_IReader3 = gH_IReader.FindChunk("output");
+ int value2 = -1;
+ if (gH_IReader3.TryGetInt32("count", ref value2) && outputs.Count == value2)
+ {
+ for (int j = 0; j < value2; j++)
+ {
+ if (gH_IReader3.ChunkExists("p", j))
+ {
+ outputs[j].Parameter.Read(gH_IReader3.FindChunk("p", j));
+ }
+ else if (gH_IReader3.ChunkExists("param", j))
+ {
+ outputs[j].Parameter.Read(gH_IReader3.FindChunk("param", j));
+ }
+ }
+ }
+ }
+ }
+ try
+ {
+ GH_IReader gH_IReader4 = reader.FindChunk("context");
+ if (gH_IReader4 != null)
+ {
+ cxt.Collection.Read(gH_IReader4);
+ }
+ }
+ catch (Exception ex)
+ {
+ RhinoApp.WriteLine("Component error:" + ex.Message + "\n" + ex.StackTrace);
+ }
+ return true;
+ }
+
+ public string GetMenuDescription()
+ {
+ return Context.Collection.GetMenuDescription();
+ }
+}
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/EvaluationUnitContext.cs b/SandWorm/CustomComponent/EvaluationUnitContext.cs
new file mode 100644
index 0000000..df2bde5
--- /dev/null
+++ b/SandWorm/CustomComponent/EvaluationUnitContext.cs
@@ -0,0 +1,27 @@
+namespace SandWorm
+{
+public class EvaluationUnitContext
+{
+ private EvaluationUnit unit;
+
+ private GH_MenuCollection collection;
+
+ public GH_MenuCollection Collection
+ {
+ get
+ {
+ return collection;
+ }
+ set
+ {
+ collection = value;
+ }
+ }
+
+ public EvaluationUnitContext(EvaluationUnit unit)
+ {
+ this.unit = unit;
+ collection = new GH_MenuCollection();
+ }
+}
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/EvaluationUnitManager.cs b/SandWorm/CustomComponent/EvaluationUnitManager.cs
new file mode 100644
index 0000000..5856ffb
--- /dev/null
+++ b/SandWorm/CustomComponent/EvaluationUnitManager.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+
+namespace SandWorm
+{
+ public class EvaluationUnitManager
+ {
+ private GH_SwitcherComponent component;
+
+ private List units;
+
+ public List Units => units;
+
+ public EvaluationUnitManager(GH_SwitcherComponent component)
+ {
+ units = new List();
+ this.component = component;
+ }
+
+ public EvaluationUnit GetUnit(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ return null;
+ }
+ foreach (EvaluationUnit unit in units)
+ {
+ if (unit.Name.Equals(name))
+ {
+ return unit;
+ }
+ }
+ return null;
+ }
+
+ public void RegisterUnit(EvaluationUnit unit)
+ {
+ string name = unit.Name;
+ if (name != null)
+ {
+ if (GetUnit(name) != null)
+ {
+ throw new ArgumentException("Duplicate evaluation unit[" + name + "] detected");
+ }
+ unit.Component = component;
+ units.Add(unit);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/ExtendedPlug.cs b/SandWorm/CustomComponent/ExtendedPlug.cs
new file mode 100644
index 0000000..8b68764
--- /dev/null
+++ b/SandWorm/CustomComponent/ExtendedPlug.cs
@@ -0,0 +1,40 @@
+using Grasshopper.Kernel;
+
+namespace SandWorm
+{
+ public class ExtendedPlug
+ {
+ private bool isMenu;
+ private EvaluationUnit unit;
+
+ public IGH_Param Parameter { get; }
+ public bool IsMenu
+ {
+ get
+ {
+ return isMenu;
+ }
+ set
+ {
+ isMenu = value;
+ }
+ }
+
+ public EvaluationUnit Unit
+ {
+ get
+ {
+ return unit;
+ }
+ set
+ {
+ unit = value;
+ }
+ }
+
+ public ExtendedPlug(IGH_Param parameter)
+ {
+ this.Parameter = parameter;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/GH_Attr_Widget.cs b/SandWorm/CustomComponent/GH_Attr_Widget.cs
new file mode 100644
index 0000000..c94a2e6
--- /dev/null
+++ b/SandWorm/CustomComponent/GH_Attr_Widget.cs
@@ -0,0 +1,301 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using GH_IO.Serialization;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+
+namespace SandWorm
+{
+ public abstract class GH_Attr_Widget
+ {
+ protected RectangleF bounds;
+
+ protected RectangleF canvasBounds;
+
+ protected GH_Attr_Widget parent;
+
+ protected PointF transfromation;
+
+ protected GH_PaletteStyle style;
+
+ protected GH_Palette palette;
+
+ protected int _index;
+
+ protected bool _enabled = true;
+
+ protected string _description;
+
+ protected string _header;
+
+ protected string _name;
+
+ protected bool _showToolTip = true;
+
+ public RectangleF CanvasBounds => canvasBounds;
+
+ public RectangleF Bounds => bounds;
+
+ public virtual bool ShowToolTip
+ {
+ get
+ {
+ return _showToolTip;
+ }
+ set
+ {
+ _showToolTip = value;
+ }
+ }
+
+ public virtual string Name
+ {
+ get
+ {
+ return _name;
+ }
+ set
+ {
+ _name = value;
+ }
+ }
+
+ public virtual string Description
+ {
+ get
+ {
+ return _description;
+ }
+ set
+ {
+ _description = value;
+ }
+ }
+
+ ///
+ /// Tooltip on mouse over
+ ///
+ public virtual string Header
+ {
+ get
+ {
+ return _header;
+ }
+ set
+ {
+ _header = value;
+ }
+ }
+
+ public virtual GH_Attr_Widget Parent
+ {
+ get
+ {
+ return parent;
+ }
+ set
+ {
+ parent = value;
+ }
+ }
+
+ public float Width
+ {
+ get
+ {
+ return bounds.Width;
+ }
+ set
+ {
+ bounds.Width = value;
+ UpdateCanvasBounds();
+ }
+ }
+
+ public float Height
+ {
+ get
+ {
+ return bounds.Height;
+ }
+ set
+ {
+ bounds.Height = value;
+ UpdateCanvasBounds();
+ }
+ }
+
+ public virtual int Index => _index;
+
+ public virtual bool Enabled
+ {
+ get
+ {
+ return _enabled;
+ }
+ set
+ {
+ _enabled = value;
+ }
+ }
+
+ public virtual GH_MenuCollection TopCollection
+ {
+ get
+ {
+ GH_Attr_Widget gH_Attr_Widget = Parent.parent;
+ while (!(gH_Attr_Widget is GH_ExtendableMenu) && gH_Attr_Widget.Parent != null)
+ {
+ gH_Attr_Widget = gH_Attr_Widget.Parent;
+ }
+ if (gH_Attr_Widget != null)
+ {
+ return ((GH_ExtendableMenu)gH_Attr_Widget).Collection;
+ }
+ return null;
+ }
+ }
+
+ public GH_PaletteStyle Style
+ {
+ get
+ {
+ return style;
+ }
+ set
+ {
+ style = value;
+ }
+ }
+
+ public GH_Palette Palette
+ {
+ get
+ {
+ return palette;
+ }
+ set
+ {
+ palette = value;
+ }
+ }
+
+ public PointF Transform
+ {
+ get
+ {
+ return transfromation;
+ }
+ set
+ {
+ transfromation = value;
+ }
+ }
+
+ public PointF CanvasPivot => new PointF(CanvasBounds.X, CanvasBounds.Y);
+
+ public GH_Attr_Widget(int index, string id)
+ {
+ _index = index;
+ }
+
+ public static Rectangle Convert(RectangleF rect)
+ {
+ return new Rectangle((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height);
+ }
+
+ public static RectangleF Shrink(RectangleF rect, float left, float right, float top, float bot)
+ {
+ return new RectangleF(rect.Left + left, rect.Top + top, rect.Width - (left + right), rect.Height - (top + bot));
+ }
+
+ public void OnRender(WidgetRenderArgs args)
+ {
+ Render(args);
+ }
+
+ public abstract void Render(WidgetRenderArgs args);
+
+ public virtual bool Contains(PointF pt)
+ {
+ return CanvasBounds.Contains(pt);
+ }
+
+ public virtual GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public virtual GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public virtual GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public virtual GH_ObjectResponse RespondToMouseDoubleClick(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public virtual GH_ObjectResponse RespondToKeyDown(GH_Canvas sender, KeyEventArgs e)
+ {
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public virtual bool Write(GH_IWriter writer)
+ {
+ return true;
+ }
+
+ public virtual bool Read(GH_IReader reader)
+ {
+ return true;
+ }
+
+ public abstract SizeF ComputeMinSize();
+
+ public void UpdateBounds(PointF transform, float width)
+ {
+ Transform = transform;
+ bounds.Width = width;
+ UpdateCanvasBounds();
+ PostUpdateBounds(out var outHeight);
+ Height = outHeight;
+ }
+
+ private void UpdateCanvasBounds()
+ {
+ canvasBounds = new RectangleF(Transform.X, Transform.Y, bounds.Width, bounds.Height);
+ }
+
+ public virtual void PostUpdateBounds(out float outHeight)
+ {
+ outHeight = Height;
+ }
+
+ public virtual void Layout()
+ {
+ }
+
+ public virtual GH_Attr_Widget IsTtipPoint(PointF pt)
+ {
+ return null;
+ }
+
+ public virtual void TooltipSetup(PointF canvasPoint, GH_TooltipDisplayEventArgs e)
+ {
+ }
+
+ public virtual string GetWidgetDescription()
+ {
+ return GetType().Name + " name" + Name + " index:" + Index;
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/GH_ExtendableComponent.cs b/SandWorm/CustomComponent/GH_ExtendableComponent.cs
new file mode 100644
index 0000000..3c307df
--- /dev/null
+++ b/SandWorm/CustomComponent/GH_ExtendableComponent.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Collections.Generic;
+using GH_IO.Serialization;
+using Grasshopper.Kernel;
+
+namespace SandWorm
+{
+ public abstract class GH_ExtendableComponent : GH_Component
+ {
+ public enum GH_ComponentState
+ {
+ COMPUTED,
+ EMPTY
+ }
+
+ private GH_RuntimeMessageLevel m_state;
+
+ protected internal GH_ExtendableComponent(string sName, string sAbbreviation, string sDescription, string sCategory, string sSubCategory)
+ : base(sName, sAbbreviation, sDescription, sCategory, sSubCategory)
+ {
+ base.Phase = GH_SolutionPhase.Blank;
+ }
+
+ protected virtual void Setup(GH_ExtendableComponentAttributes attr)
+ {
+ }
+
+ protected virtual void OnComponentLoaded()
+ {
+ }
+
+ protected virtual void OnComponentReset(GH_ExtendableComponentAttributes attr)
+ {
+ }
+
+
+
+ ///
+ /// Updates a reference based on the output. Use me in a lambda like:
+ /// _levels.ValueChanged += (s, e) => ValueUpdate(s, e, ref levels);
+ ///
+ /// double, int or enum
+ ///
+ /// Can be any object that can be converted to double, int or enum
+ public void ValueUpdate(object sender, System.EventArgs e, ref T reference) where T : IConvertible
+ {
+ if (sender is MenuSlider sl)
+ {
+ if (typeof(T).IsAssignableFrom(typeof(double)))
+ reference = (T)(object)sl.Value;
+ else if (typeof(T).IsAssignableFrom(typeof(int)))
+ reference = (T)(object)Convert.ToInt32(Math.Round(sl.Value));
+ }
+
+ else if (sender is MenuDropDown down)
+ {
+ if (typeof(T).IsEnum)
+ {
+ reference = (T)Enum.Parse(typeof(T), down.Items[down.Value].name);
+ base.Params.OnParametersChanged();
+ }
+ else if (typeof(T) == typeof(string))
+ {
+ reference = (T)(object)down.Items[down.Value].name;
+ }
+
+ }
+ else if (sender is MenuRadioButtonGroup rbg && rbg.GetActiveInt().Count > 0)
+ {
+
+ if (typeof(T) == typeof(bool))
+ {
+
+ foreach (var radio in rbg.GetActive())
+ {
+ if (radio.Tag.ToLower() == "no" || radio.Tag.ToLower() == "false" || radio.Tag.ToLower() == "off")
+ {
+ reference = (T)(object)false;
+
+ break;
+
+ }
+ if (radio.Tag.ToLower() == "yes" || radio.Tag.ToLower() == "true" || radio.Tag.ToLower() == "on")
+ {
+ reference = (T)(object)true;
+
+ break;
+ }
+ }
+ }
+ else if (typeof(T) == typeof(int))
+ {
+
+ reference = (T)(object)rbg.GetActiveInt()[0];
+ }
+ }
+
+ else
+ {
+ throw new NotImplementedException($"Trying to update a type that is not yet implemented in GH_ExtendableComponent.cs/ValueUpdate. Type {typeof(T)} sender: {sender}");
+ }
+
+ setModelProps();
+ }
+
+ ///
+ /// Added to GH_ExtendableComponent. Do feel free to use this one or override to your own.
+ ///
+ protected void setModelProps()
+ {
+ ExpireSolution(recompute: true);
+ }
+
+
+ public override void ComputeData()
+ {
+ base.ComputeData();
+ if (m_state != RuntimeMessageLevel && RuntimeMessageLevel == GH_RuntimeMessageLevel.Warning)
+ {
+ List input = base.Params.Input;
+ int count = input.Count;
+ bool flag = true;
+ for (int i = 0; i < count; i++)
+ {
+ if (input[i].SourceCount == 0 && !input[i].Optional && input[i].VolatileData.IsEmpty)
+ {
+ flag = false;
+ break;
+ }
+ }
+ if (!flag && RuntimeMessageLevel == GH_RuntimeMessageLevel.Warning)
+ {
+ OnComponentReset((GH_ExtendableComponentAttributes)base.Attributes);
+ }
+ }
+ m_state = RuntimeMessageLevel;
+ }
+
+ public override bool Read(GH_IReader reader)
+ {
+ bool result = base.Read(reader);
+ OnComponentLoaded();
+ return result;
+ }
+
+ public override void CreateAttributes()
+ {
+ Setup((GH_ExtendableComponentAttributes)(m_attributes = new GH_ExtendableComponentAttributes(this)));
+ }
+
+ public bool OutputConnected(int ind)
+ {
+ return base.Params.Output[ind].Recipients.Count != 0;
+ }
+
+ public bool OutputInUse(int ind)
+ {
+ if (base.Params.Output[ind] is IGH_PreviewObject && !base.Hidden)
+ {
+ return true;
+ }
+ if (base.Params.Output[ind].Recipients.Count != 0)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public bool OutputInUse()
+ {
+ for (int i = 0; i < base.Params.Output.Count; i++)
+ {
+ if (OutputInUse(i))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public string GetMenuDescription()
+ {
+ return ((GH_ExtendableComponentAttributes)base.Attributes).GetMenuDescription();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/GH_ExtendableComponentAttributes.cs b/SandWorm/CustomComponent/GH_ExtendableComponentAttributes.cs
new file mode 100644
index 0000000..12fe7ac
--- /dev/null
+++ b/SandWorm/CustomComponent/GH_ExtendableComponentAttributes.cs
@@ -0,0 +1,326 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using GH_IO.Serialization;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+using Grasshopper.Kernel;
+using Grasshopper.Kernel.Attributes;
+
+namespace SandWorm
+{
+ public class GH_ExtendableComponentAttributes : GH_ComponentAttributes
+ {
+ private float _minWidth;
+
+ private GH_Attr_Widget _activeToolTip;
+
+ private GH_MenuCollection _collection;
+
+ private const bool RENDER_OVERLAY_OVERWIDGETS = true;
+
+ public float MinWidth
+ {
+ get
+ {
+ return _minWidth;
+ }
+ set
+ {
+ _minWidth = value;
+ }
+ }
+
+ public GH_ExtendableComponentAttributes(IGH_Component nComponent)
+ : base(nComponent)
+ {
+ _collection = new GH_MenuCollection();
+ }
+
+ public void AddMenu(GH_ExtendableMenu _menu)
+ {
+ _collection.AddMenu(_menu);
+ }
+
+ public override bool Write(GH_IWriter writer)
+ {
+ try
+ {
+ _collection.Write(writer);
+ }
+ catch (Exception)
+ {
+ }
+ return base.Write(writer);
+ }
+
+ public override bool Read(GH_IReader reader)
+ {
+ try
+ {
+ _collection.Read(reader);
+ }
+ catch (Exception)
+ {
+ }
+ return base.Read(reader);
+ }
+
+ protected override void PrepareForRender(GH_Canvas canvas)
+ {
+ base.PrepareForRender(canvas);
+ LayoutStyle();
+ }
+
+ protected override void Layout()
+ {
+ Pivot = GH_Convert.ToPoint(Pivot);
+ base.Layout();
+ FixLayout();
+ LayoutMenu();
+ }
+
+ protected void FixLayout()
+ {
+ _ = Bounds.Width;
+ float num = Math.Max(_collection.GetMinLayoutSize().Width, _minWidth);
+ float num2 = 0f;
+ if (num > Bounds.Width)
+ {
+ num2 = num - Bounds.Width;
+ Bounds = new RectangleF(Bounds.X - num2 / 2f, Bounds.Y, num, Bounds.Height);
+ }
+ foreach (IGH_Param item in base.Owner.Params.Output)
+ {
+ PointF pivot = item.Attributes.Pivot;
+ RectangleF bounds = item.Attributes.Bounds;
+ item.Attributes.Pivot = new PointF(pivot.X, pivot.Y);
+ item.Attributes.Bounds = new RectangleF(bounds.Location.X, bounds.Location.Y, bounds.Width + num2 / 2f, bounds.Height);
+ }
+ foreach (IGH_Param item2 in base.Owner.Params.Input)
+ {
+ PointF pivot2 = item2.Attributes.Pivot;
+ RectangleF bounds2 = item2.Attributes.Bounds;
+ item2.Attributes.Pivot = new PointF(pivot2.X - num2 / 2f, pivot2.Y);
+ item2.Attributes.Bounds = new RectangleF(bounds2.Location.X - num2 / 2f, bounds2.Location.Y, bounds2.Width + num2 / 2f, bounds2.Height);
+ }
+ }
+
+ private void LayoutStyle()
+ {
+ _collection.Palette = GH_CapsuleRenderEngine.GetImpliedPalette(base.Owner);
+ _collection.Style = GH_CapsuleRenderEngine.GetImpliedStyle(_collection.Palette, Selected, base.Owner.Locked, base.Owner.Hidden);
+ _collection.Layout();
+ }
+
+ protected void LayoutMenu()
+ {
+ _collection.Width = Bounds.Width;
+ _collection.Pivot = new PointF(Bounds.Left, Bounds.Bottom);
+ LayoutStyle();
+ Bounds = new RectangleF(Bounds.X, Bounds.Y, Bounds.Width, Bounds.Height + _collection.Height+ 2);
+ }
+
+ protected override void Render(GH_Canvas iCanvas, Graphics graph, GH_CanvasChannel iChannel)
+ {
+ if (iChannel == GH_CanvasChannel.First)
+ {
+ iCanvas.CanvasPostPaintWidgets -= RenderPostWidgets;
+ iCanvas.CanvasPostPaintWidgets += RenderPostWidgets;
+ }
+ base.Render(iCanvas, graph, iChannel);
+ if (iChannel != GH_CanvasChannel.Objects)
+ {
+ _ = 30;
+ }
+ else
+ {
+ // Cache current styles
+ GH_PaletteStyle styleStandard = GH_Skin.palette_normal_standard;
+ GH_PaletteStyle styleSelected = GH_Skin.palette_normal_selected;
+ GH_PaletteStyle styleWarningStandard = GH_Skin.palette_warning_standard;
+ GH_PaletteStyle styleWarningSelected = GH_Skin.palette_warning_selected;
+ GH_PaletteStyle styleErrorStandard = GH_Skin.palette_error_standard;
+ GH_PaletteStyle styleErrorSelected = GH_Skin.palette_error_selected;
+ GH_PaletteStyle styleHiddenStandard = GH_Skin.palette_hidden_standard;
+ GH_PaletteStyle styleHiddenSelected = GH_Skin.palette_hidden_selected;
+ GH_PaletteStyle styleLockedStandard = GH_Skin.palette_locked_standard;
+ GH_PaletteStyle styleLockedSelected = GH_Skin.palette_locked_selected;
+
+ //Create custom styles
+ GH_Skin.palette_normal_standard = new GH_PaletteStyle(Color.FromArgb(226, 230, 231), Color.FromArgb(226, 230, 231), Color.FromArgb(45, 45, 45));
+ GH_Skin.palette_normal_selected = new GH_PaletteStyle(Color.FromArgb(174, 213, 129), Color.FromArgb(236, 240, 241), Color.FromArgb(45, 45, 45));
+ GH_Skin.palette_warning_standard = new GH_PaletteStyle(Color.FromArgb(253, 216, 53), Color.FromArgb(253, 216, 53), Color.FromArgb(45, 45, 45));
+ //GH_Skin.palette_warning_selected = new GH_PaletteStyle(Color.FromArgb(223, 196, 33), Color.FromArgb(253, 216, 53), Color.FromArgb(45, 45, 45));
+ GH_Skin.palette_warning_selected = new GH_PaletteStyle(Color.FromArgb(174, 213, 129), Color.FromArgb(236, 240, 241), Color.FromArgb(45, 45, 45));
+ GH_Skin.palette_error_standard = new GH_PaletteStyle(Color.FromArgb(231, 76, 60), Color.FromArgb(231, 76, 60), Color.FromArgb(45, 45, 45));
+ //GH_Skin.palette_error_selected = new GH_PaletteStyle(Color.FromArgb(211, 56, 40), Color.FromArgb(231, 76, 60), Color.FromArgb(45, 45, 45));
+ GH_Skin.palette_error_selected = new GH_PaletteStyle(Color.FromArgb(174, 213, 129), Color.FromArgb(236, 240, 241), Color.FromArgb(45, 45, 45));
+ GH_Skin.palette_hidden_standard = new GH_PaletteStyle(Color.FromArgb(158, 158, 158), Color.FromArgb(158, 158, 158), Color.FromArgb(45, 45, 45));
+ //GH_Skin.palette_hidden_selected = new GH_PaletteStyle(Color.FromArgb(128, 128, 128), Color.FromArgb(158, 158, 158), Color.FromArgb(45, 45, 45));
+ GH_Skin.palette_hidden_selected = new GH_PaletteStyle(Color.FromArgb(174, 213, 129), Color.FromArgb(236, 240, 241), Color.FromArgb(45, 45, 45));
+ GH_Skin.palette_locked_standard = new GH_PaletteStyle(Color.FromArgb(108, 108, 108), Color.FromArgb(158, 158, 158), Color.FromArgb(45, 45, 45));
+ //GH_Skin.palette_locked_selected = new GH_PaletteStyle(Color.FromArgb(78, 78, 78), Color.FromArgb(158, 158, 158), Color.FromArgb(45, 45, 45));
+ GH_Skin.palette_locked_selected = new GH_PaletteStyle(Color.FromArgb(134, 173, 89), Color.FromArgb(236, 240, 241), Color.FromArgb(45, 45, 45));
+
+
+ base.Render(iCanvas, graph, iChannel);
+ _collection.Render(new WidgetRenderArgs(iCanvas, WidgetChannel.Object));
+
+ // Restore cached styles
+ GH_Skin.palette_normal_standard = styleStandard;
+ GH_Skin.palette_normal_selected = styleSelected;
+ GH_Skin.palette_warning_standard = styleWarningStandard;
+ GH_Skin.palette_warning_selected = styleWarningSelected;
+ GH_Skin.palette_error_standard = styleErrorStandard;
+ GH_Skin.palette_error_selected = styleErrorSelected;
+ GH_Skin.palette_hidden_standard = styleHiddenStandard;
+ GH_Skin.palette_hidden_selected = styleHiddenSelected;
+ GH_Skin.palette_locked_standard = styleLockedStandard;
+ GH_Skin.palette_locked_selected = styleLockedSelected;
+ }
+ }
+
+ private void RenderPostWidgets(GH_Canvas sender)
+ {
+ _collection.Render(new WidgetRenderArgs(sender, WidgetChannel.Overlay));
+ }
+
+ public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ GH_ObjectResponse gH_ObjectResponse = _collection.RespondToMouseUp(sender, e);
+ switch (gH_ObjectResponse)
+ {
+ case GH_ObjectResponse.Capture:
+ ExpireLayout();
+ sender.Invalidate();
+ return gH_ObjectResponse;
+ default:
+ ExpireLayout();
+ sender.Invalidate();
+ return GH_ObjectResponse.Release;
+ case GH_ObjectResponse.Ignore:
+ return base.RespondToMouseUp(sender, e);
+ }
+ }
+
+ public override GH_ObjectResponse RespondToMouseDoubleClick(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ GH_ObjectResponse gH_ObjectResponse = _collection.RespondToMouseDoubleClick(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ ExpireLayout();
+ sender.Refresh();
+ return gH_ObjectResponse;
+ }
+ return base.RespondToMouseDoubleClick(sender, e);
+ }
+
+ public override GH_ObjectResponse RespondToKeyDown(GH_Canvas sender, KeyEventArgs e)
+ {
+ GH_ObjectResponse gH_ObjectResponse = _collection.RespondToKeyDown(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ ExpireLayout();
+ sender.Refresh();
+ return gH_ObjectResponse;
+ }
+ return base.RespondToKeyDown(sender, e);
+ }
+
+ public override GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ GH_ObjectResponse gH_ObjectResponse = _collection.RespondToMouseMove(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ ExpireLayout();
+ sender.Refresh();
+ return gH_ObjectResponse;
+ }
+ return base.RespondToMouseMove(sender, e);
+ }
+
+ public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ try
+ {
+ GH_ObjectResponse gH_ObjectResponse = _collection.RespondToMouseDown(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ ExpireLayout();
+ sender.Refresh();
+ return gH_ObjectResponse;
+ }
+ return base.RespondToMouseDown(sender, e);
+ }
+ catch (Exception)
+ {
+ return base.RespondToMouseDown(sender, e);
+ }
+ }
+
+ public override bool IsTooltipRegion(PointF pt)
+ {
+ _activeToolTip = null;
+ bool flag = base.IsTooltipRegion(pt);
+ if (flag)
+ {
+ return flag;
+ }
+ if (m_innerBounds.Contains(pt))
+ {
+ GH_Attr_Widget gH_Attr_Widget = _collection.IsTtipPoint(pt);
+ if (gH_Attr_Widget != null)
+ {
+ _activeToolTip = gH_Attr_Widget;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public bool GetActiveTooltip(PointF pt)
+ {
+ GH_Attr_Widget gH_Attr_Widget = _collection.IsTtipPoint(pt);
+ if (gH_Attr_Widget != null)
+ {
+ _activeToolTip = gH_Attr_Widget;
+ return true;
+ }
+ return false;
+ }
+
+ public override void SetupTooltip(PointF canvasPoint, GH_TooltipDisplayEventArgs e)
+ {
+ GetActiveTooltip(canvasPoint);
+ if (_activeToolTip != null)
+ {
+ _activeToolTip.TooltipSetup(canvasPoint, e);
+ return;
+ }
+ e.Title = PathName;
+ e.Text = base.Owner.Description;
+ e.Description = base.Owner.InstanceDescription;
+ e.Icon = base.Owner.Icon_24x24;
+ if (base.Owner is IGH_Param)
+ {
+ IGH_Param obj = (IGH_Param)base.Owner;
+ string text = obj.TypeName;
+ if (obj.Access == GH_ParamAccess.list)
+ {
+ text += "[…]";
+ }
+ if (obj.Access == GH_ParamAccess.tree)
+ {
+ text += "{…;…;…}";
+ }
+ e.Title = $"{PathName} ({text})";
+ }
+ }
+
+ public string GetMenuDescription()
+ {
+ return _collection.GetMenuDescription();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/GH_ExtendableMenu.cs b/SandWorm/CustomComponent/GH_ExtendableMenu.cs
new file mode 100644
index 0000000..ad6d309
--- /dev/null
+++ b/SandWorm/CustomComponent/GH_ExtendableMenu.cs
@@ -0,0 +1,449 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Reflection;
+using System.Windows.Forms;
+using GH_IO.Serialization;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+using Grasshopper.Kernel;
+using Grasshopper.Kernel.Attributes;
+
+namespace SandWorm
+{
+ public class GH_ExtendableMenu : GH_Attr_Widget
+ {
+ private List inputs;
+
+ private List outputs;
+
+ private string name;
+
+ private GH_MenuCollection collection;
+
+ private GH_Capsule _menu;
+
+ private RectangleF _headBounds;
+
+ private RectangleF _contentBounds;
+
+ private List _controls;
+
+ private GH_Attr_Widget _activeControl;
+
+ private bool _expanded;
+
+ public List Inputs => inputs;
+
+ public List Outputs => outputs;
+
+ public bool Expanded => _expanded;
+
+ public GH_MenuCollection Collection
+ {
+ get
+ {
+ return collection;
+ }
+ set
+ {
+ collection = value;
+ }
+ }
+
+ public List Controlls => _controls;
+
+ ///
+ /// The text on the foldable button
+ ///
+ public override string Name
+ {
+ get
+ {
+ return name;
+ }
+ set
+ {
+ name = value;
+ }
+ }
+
+
+
+
+ public float TotalHeight
+ {
+ get
+ {
+ if (_expanded)
+ {
+ int num = Math.Max(inputs.Count, outputs.Count) * 20;
+ if (num > 0)
+ {
+ num += 5;
+ }
+ return base.Height + (float)num;
+ }
+ return base.Height;
+ }
+ }
+
+ public GH_ExtendableMenu(int index, string id)
+ : base(index, id)
+ {
+ inputs = new List();
+ outputs = new List();
+ _controls = new List();
+ _headBounds = default;
+ _contentBounds = default;
+ }
+
+ public void RegisterInputPlug(ExtendedPlug plug)
+ {
+ plug.IsMenu = true;
+ inputs.Add(plug);
+ }
+
+ public void RegisterOutputPlug(ExtendedPlug plug)
+ {
+ plug.IsMenu = true;
+ outputs.Add(plug);
+ }
+
+ public void Expand()
+ {
+ if (!_expanded)
+ {
+ _expanded = true;
+ }
+ }
+
+ public void Collapse()
+ {
+ if (_expanded)
+ {
+ _expanded = false;
+ }
+ }
+
+ public void AddControl(GH_Attr_Widget control)
+ {
+ control.Parent = this;
+ _controls.Add(control);
+ }
+
+ public void MakeAllInActive()
+ {
+ int count = _controls.Count;
+ for (int i = 0; i < count; i++)
+ {
+ if (_controls[i] is MenuPanel panel)
+ {
+ panel._activeControl = null;
+ }
+ }
+ _activeControl = null;
+ }
+
+ public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (e.Button != MouseButtons.Left)
+ return GH_ObjectResponse.Ignore;
+
+ if (_activeControl != null)
+ {
+ GH_ObjectResponse gH_ObjectResponse = _activeControl.RespondToMouseUp(sender, e);
+ switch (gH_ObjectResponse)
+ {
+ case GH_ObjectResponse.Release:
+ _activeControl = null;
+ return gH_ObjectResponse;
+ default:
+ return gH_ObjectResponse;
+ case GH_ObjectResponse.Ignore:
+ break;
+ }
+ _activeControl = null;
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (e.Button != MouseButtons.Left)
+ return GH_ObjectResponse.Ignore;
+ if (_headBounds.Contains(e.CanvasLocation))
+ {
+ if (Expanded)
+ {
+ _activeControl = null;
+ }
+ _expanded = !_expanded;
+ return GH_ObjectResponse.Handled;
+ }
+ if (_expanded)
+ {
+ if (_contentBounds.Contains(e.CanvasLocation))
+ {
+ for (int i = 0; i < inputs.Count; i++)
+ {
+ if (inputs[i].Parameter.Attributes.Bounds.Contains(e.CanvasLocation))
+ {
+ return inputs[i].Parameter.Attributes.RespondToMouseDown(sender, e);
+ }
+ }
+ for (int j = 0; j < _controls.Count; j++)
+ {
+ if (_controls[j].Contains(e.CanvasLocation))
+ {
+ _activeControl = _controls[j];
+ return _controls[j].RespondToMouseDown(sender, e);
+ }
+ }
+ }
+ else if (_activeControl != null)
+ {
+ _activeControl.RespondToMouseDown(sender, e);
+ _activeControl = null;
+ return GH_ObjectResponse.Handled;
+ }
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_Attr_Widget IsTtipPoint(PointF pt)
+ {
+ if (_headBounds.Contains(pt))
+ {
+ return this;
+ }
+ if (_expanded && _contentBounds.Contains(pt))
+ {
+ int count = _controls.Count;
+ for (int i = 0; i < count; i++)
+ {
+ if (_controls[i].Contains(pt))
+ {
+ GH_Attr_Widget gH_Attr_Widget = _controls[i].IsTtipPoint(pt);
+ if (gH_Attr_Widget != null)
+ {
+ return gH_Attr_Widget;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public override void TooltipSetup(PointF canvasPoint, GH_TooltipDisplayEventArgs e)
+ {
+ e.Icon = null;
+ e.Title = "Menu (" + name + ")";
+ e.Text = _header;
+ if (_header != null)
+ {
+ e.Text += "\n";
+ }
+ if (_expanded)
+ {
+ e.Text += "Click to close menu";
+ }
+ else
+ {
+ e.Text += "Click to open menu";
+ }
+ e.Description = _description;
+ }
+
+ public override GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (e.Button != MouseButtons.Left)
+ return GH_ObjectResponse.Ignore;
+
+ if (_activeControl != null)
+ {
+ return _activeControl.RespondToMouseMove(sender, e);
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_ObjectResponse RespondToMouseDoubleClick(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ _ = base.CanvasPivot;
+ if (_headBounds.Contains(e.CanvasLocation))
+ {
+ return GH_ObjectResponse.Handled;
+ }
+ if (_expanded && _contentBounds.Contains(e.CanvasLocation))
+ {
+ int count = _controls.Count;
+ for (int i = 0; i < count; i++)
+ {
+ if (_controls[i].Contains(e.CanvasLocation))
+ {
+ return _controls[i].RespondToMouseDoubleClick(sender, e);
+ }
+ }
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override bool Write(GH_IWriter writer)
+ {
+ writer.SetBoolean("Expanded", _expanded);
+ for (int i = 0; i < _controls.Count; i++)
+ {
+ _controls[i].Write(writer);
+ }
+ return base.Write(writer);
+ }
+
+ public override bool Read(GH_IReader reader)
+ {
+ _expanded = reader.GetBoolean("Expanded");
+ for (int i = 0; i < _controls.Count; i++)
+ {
+ _controls[i].Read(reader);
+ }
+ return base.Read(reader);
+ }
+
+ public override SizeF ComputeMinSize()
+ {
+ SizeF menuHeadTextSize = GetMenuHeadTextSize();
+ float num = menuHeadTextSize.Width;
+ float num2 = menuHeadTextSize.Height;
+ foreach (GH_Attr_Widget control in _controls)
+ {
+ SizeF sizeF = control.ComputeMinSize();
+ num = Math.Max(sizeF.Width, num);
+ if (_expanded)
+ {
+ num2 += sizeF.Height;
+ }
+ }
+ return new SizeF(num, num2);
+ }
+
+ private SizeF GetMenuHeadTextSize()
+ {
+ Size size = TextRenderer.MeasureText(name, WidgetServer.Instance.MenuHeaderFont);
+ return new SizeF(size.Width + 8, size.Height + 4);
+ }
+
+ public override void Layout()
+ {
+ SizeF menuHeadTextSize = GetMenuHeadTextSize();
+ _headBounds = new RectangleF(base.CanvasPivot.X, base.CanvasPivot.Y, base.Width, menuHeadTextSize.Height);
+ _contentBounds = new RectangleF(base.CanvasPivot.X, base.CanvasPivot.Y + menuHeadTextSize.Height, base.Width, base.Height - menuHeadTextSize.Height);
+ Rectangle rectangle = new Rectangle((int)_headBounds.X + 3, (int)_headBounds.Y + 1, (int)_headBounds.Width - 6, (int)_headBounds.Height - 2);
+ _menu = GH_Capsule.CreateTextCapsule(rectangle, rectangle, GH_Palette.Normal, name, WidgetServer.Instance.MenuHeaderFont, GH_Orientation.horizontal_center, 2, 0); //TODO Button color
+ float num = menuHeadTextSize.Height;
+ if (_expanded)
+ {
+ PointF transform = new PointF(base.CanvasPivot.X, base.CanvasPivot.Y + menuHeadTextSize.Height);
+ foreach (GH_Attr_Widget control in _controls)
+ {
+ control.UpdateBounds(transform, base.Width);
+ control.Transform = transform;
+ control.Style = style;
+ control.Palette = palette;
+ control.Layout();
+ num += control.Height;
+ }
+ }
+ base.Height = num;
+ }
+
+ public override void Render(WidgetRenderArgs args)
+ {
+ GH_Canvas canvas = args.Canvas;
+ _ = args.Channel;
+ float zoom = canvas.Viewport.Zoom;
+ int num = 255;
+ if (zoom < 1f)
+ {
+ float num2 = (zoom - 0.5f) * 2f;
+ num = (int)((float)num * num2);
+ }
+ _menu.Render(canvas.Graphics, selected: false, locked: false, hidden: false);
+ if (!_expanded || num <= 0)
+ {
+ return;
+ }
+ RenderMenuParameters(canvas, canvas.Graphics);
+ foreach (GH_Attr_Widget control in _controls)
+ {
+ control.OnRender(args);
+ }
+ }
+
+ public void RenderMenuParameters(GH_Canvas canvas, Graphics graphics)
+ {
+ if (Math.Max(inputs.Count, outputs.Count) == 0)
+ {
+ return;
+ }
+ int zoomFadeLow = GH_Canvas.ZoomFadeLow;
+ if (zoomFadeLow < 5)
+ {
+ return;
+ }
+ StringFormat farCenter = GH_TextRenderingConstants.FarCenter;
+ canvas.SetSmartTextRenderingHint();
+ SolidBrush solidBrush = new SolidBrush(Color.FromArgb(zoomFadeLow, style.Text));
+ foreach (ExtendedPlug input in inputs)
+ {
+ IGH_Param parameter = input.Parameter;
+ RectangleF bounds = parameter.Attributes.Bounds;
+ if (!(bounds.Width >= 1f))
+ {
+ continue;
+ }
+ graphics.DrawString(parameter.NickName, StandardFont.Font(), solidBrush, bounds, farCenter);
+ GH_LinkedParamAttributes obj = (GH_LinkedParamAttributes)parameter.Attributes;
+ FieldInfo field = typeof(GH_LinkedParamAttributes).GetField("m_renderTags", BindingFlags.Instance | BindingFlags.NonPublic);
+ if (field != null)
+ {
+ object value = field.GetValue(obj);
+ if (value != null)
+ {
+ ((GH_StateTagList)value).RenderStateTags(graphics);
+ }
+ }
+ }
+ farCenter = GH_TextRenderingConstants.NearCenter;
+ foreach (ExtendedPlug output in outputs)
+ {
+ IGH_Param parameter2 = output.Parameter;
+ RectangleF bounds2 = parameter2.Attributes.Bounds;
+ if (!(bounds2.Width >= 1f))
+ {
+ continue;
+ }
+ graphics.DrawString(parameter2.NickName, StandardFont.Font(), solidBrush, bounds2, farCenter);
+ GH_LinkedParamAttributes obj2 = (GH_LinkedParamAttributes)parameter2.Attributes;
+ FieldInfo field2 = typeof(GH_LinkedParamAttributes).GetField("m_renderTags", BindingFlags.Instance | BindingFlags.NonPublic);
+ if (field2 != null)
+ {
+ object value2 = field2.GetValue(obj2);
+ if (value2 != null)
+ {
+ ((GH_StateTagList)value2).RenderStateTags(graphics);
+ }
+ }
+ }
+ solidBrush.Dispose();
+ }
+
+ public override string GetWidgetDescription()
+ {
+ string str = base.GetWidgetDescription() + "{\n";
+ foreach (GH_Attr_Widget control in _controls)
+ {
+ str = str + control.GetWidgetDescription() + "\n";
+ }
+ return str + "}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/GH_MenuCollection.cs b/SandWorm/CustomComponent/GH_MenuCollection.cs
new file mode 100644
index 0000000..c0542ee
--- /dev/null
+++ b/SandWorm/CustomComponent/GH_MenuCollection.cs
@@ -0,0 +1,335 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+using GH_IO.Serialization;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+using Rhino;
+
+namespace SandWorm
+{
+public class GH_MenuCollection
+{
+ private List _menus;
+
+ private PointF pivot;
+
+ private float width;
+
+ private GH_PaletteStyle style;
+
+ private GH_Palette palette;
+
+ private GH_Attr_Widget _activeWidget;
+
+ public List Menus
+ {
+ get
+ {
+ return _menus;
+ }
+ set
+ {
+ _menus = value;
+ }
+ }
+
+ public PointF Pivot
+ {
+ get
+ {
+ return pivot;
+ }
+ set
+ {
+ pivot = value;
+ }
+ }
+
+ public float Width
+ {
+ get
+ {
+ return width;
+ }
+ set
+ {
+ width = value;
+ }
+ }
+
+ public GH_PaletteStyle Style
+ {
+ get
+ {
+ return style;
+ }
+ set
+ {
+ style = value;
+ }
+ }
+
+ public GH_Palette Palette
+ {
+ get
+ {
+ return palette;
+ }
+ set
+ {
+ palette = value;
+ }
+ }
+
+ public GH_Attr_Widget ActiveWidget
+ {
+ get
+ {
+ return _activeWidget;
+ }
+ set
+ {
+ _activeWidget = value;
+ }
+ }
+
+ public float Height
+ {
+ get
+ {
+ float num = 0f;
+ for (int i = 0; i < _menus.Count; i++)
+ {
+ num += _menus[i].TotalHeight;
+ }
+ return num;
+ }
+ }
+
+ public GH_MenuCollection()
+ {
+ _menus = new List();
+ }
+
+ public void Merge(GH_MenuCollection other)
+ {
+ for (int i = 0; i < other._menus.Count; i++)
+ {
+ AddMenu(other._menus[i]);
+ }
+ }
+
+ public void GetMenuPlugs(ref List inputs, ref List outputs, bool onlyVisible)
+ {
+ for (int i = 0; i < _menus.Count; i++)
+ {
+ if (!onlyVisible || (onlyVisible && _menus[i].Expanded))
+ {
+ inputs.AddRange(_menus[i].Inputs);
+ outputs.AddRange(_menus[i].Outputs);
+ }
+ }
+ }
+
+ public void AddMenu(GH_ExtendableMenu _menu)
+ {
+ _menu.Collection = this;
+ _menus.Add(_menu);
+ }
+
+ public void CollapseAllMenu()
+ {
+ MakeAllInActive();
+ for (int i = 0; i < _menus.Count; i++)
+ {
+ _menus[i].Collapse();
+ }
+ }
+
+ public void MakeAllInActive()
+ {
+ for (int i = 0; i < _menus.Count; i++)
+ {
+ _menus[i].MakeAllInActive();
+ }
+ }
+
+ public GH_ObjectResponse RespondToMouseDoubleClick(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ for (int i = 0; i < _menus.Count; i++)
+ {
+ GH_ObjectResponse gH_ObjectResponse = _menus[i].RespondToMouseDoubleClick(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ return gH_ObjectResponse;
+ }
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (_activeWidget != null)
+ {
+ return _activeWidget.RespondToMouseUp(sender, e);
+ }
+ for (int i = 0; i < _menus.Count; i++)
+ {
+ GH_ObjectResponse gH_ObjectResponse = _menus[i].RespondToMouseUp(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ return gH_ObjectResponse;
+ }
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (_activeWidget != null)
+ {
+ return _activeWidget.RespondToMouseMove(sender, e);
+ }
+ int count = _menus.Count;
+ for (int i = 0; i < count; i++)
+ {
+ GH_ObjectResponse gH_ObjectResponse = _menus[i].RespondToMouseMove(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ return gH_ObjectResponse;
+ }
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (_activeWidget != null)
+ {
+ return _activeWidget.RespondToMouseDown(sender, e);
+ }
+ int count = _menus.Count;
+ for (int i = 0; i < count; i++)
+ {
+ GH_ObjectResponse gH_ObjectResponse = _menus[i].RespondToMouseDown(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ return gH_ObjectResponse;
+ }
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public GH_Attr_Widget IsTtipPoint(PointF pt)
+ {
+ if (_activeWidget != null)
+ {
+ return null;
+ }
+ int count = _menus.Count;
+ for (int i = 0; i < count; i++)
+ {
+ GH_Attr_Widget gH_Attr_Widget = _menus[i].IsTtipPoint(pt);
+ if (gH_Attr_Widget != null)
+ {
+ return gH_Attr_Widget;
+ }
+ }
+ return null;
+ }
+
+ public GH_ObjectResponse RespondToKeyDown(GH_Canvas sender, KeyEventArgs e)
+ {
+ if (_activeWidget != null)
+ {
+ return _activeWidget.RespondToKeyDown(sender, e);
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public bool Write(GH_IWriter writer)
+ {
+ GH_IWriter gH_IWriter = writer.CreateChunk("Collection");
+ for (int i = 0; i < _menus.Count; i++)
+ {
+ GH_IWriter writer2 = gH_IWriter.CreateChunk("Menu", i);
+ _menus[i].Write(writer2);
+ }
+ return true;
+ }
+
+ public bool Read(GH_IReader reader)
+ {
+ GH_IReader gH_IReader = reader.FindChunk("Collection");
+ if (gH_IReader == null)
+ {
+ RhinoApp.WriteLine("Invalid menu collection");
+ return false;
+ }
+ for (int i = 0; i < _menus.Count; i++)
+ {
+ GH_IReader gH_IReader2 = gH_IReader.FindChunk("Menu", i);
+ if (gH_IReader2 == null)
+ {
+ return false;
+ }
+ _menus[i].Read(gH_IReader2);
+ }
+ return true;
+ }
+
+ public SizeF GetMinLayoutSize()
+ {
+ float val = 0f;
+ float num = 0f;
+ foreach (GH_ExtendableMenu menu in _menus)
+ {
+ SizeF sizeF = menu.ComputeMinSize();
+ val = Math.Max(val, sizeF.Width);
+ num += sizeF.Height;
+ }
+ return new SizeF(val, num);
+ }
+
+ public void Layout()
+ {
+ int num = 0;
+ _ = _menus.Count;
+ foreach (GH_ExtendableMenu menu in _menus)
+ {
+ menu.UpdateBounds(new PointF(pivot.X, pivot.Y + (float)num), width);
+ menu.Style = style;
+ menu.Palette = palette;
+ menu.Layout();
+ num += (int)menu.TotalHeight;
+ }
+ }
+
+ public void Render(WidgetRenderArgs args)
+ {
+ if (args.Channel == WidgetChannel.Object)
+ {
+ foreach (GH_ExtendableMenu menu in _menus)
+ {
+ menu.OnRender(args);
+ }
+ }
+ else if (args.Channel == WidgetChannel.Overlay && _activeWidget != null)
+ {
+ _activeWidget.OnRender(args);
+ }
+ }
+
+ public string GetMenuDescription()
+ {
+ string str = "collection {\n";
+ foreach (GH_ExtendableMenu menu in _menus)
+ {
+ str = str + menu.GetWidgetDescription() + "\n";
+ }
+ return str + "}";
+ }
+}
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/GH_MenuSliderForm.cs b/SandWorm/CustomComponent/GH_MenuSliderForm.cs
new file mode 100644
index 0000000..4bc7412
--- /dev/null
+++ b/SandWorm/CustomComponent/GH_MenuSliderForm.cs
@@ -0,0 +1,323 @@
+using System;
+using System.Windows.Forms;
+using Grasshopper.GUI;
+
+namespace SandWorm
+{
+public class GH_MenuSliderForm : Form
+{
+ private Button btnCancel;
+
+ private Button btnOK;
+
+ private GroupBox grpDomain;
+
+ private Label lblLower;
+
+ private Label lblUpper;
+
+ private Label lblValue;
+
+ private NumericUpDown numLower;
+
+ private NumericUpDown numUpper;
+
+ private NumericUpDown numValue;
+
+ private Panel Panel1;
+
+ private Panel Panel2;
+
+ private Panel pnSep1;
+
+ private TableLayoutPanel tblDomain;
+
+ private TableLayoutPanel tblType;
+
+ private TableLayoutPanel tblValue;
+
+ private MenuSlider _slider;
+
+ private double _minValue;
+
+ private double _maxValue = 1.0;
+
+ private double _value;
+
+ public GH_MenuSliderForm(MenuSlider slider)
+ {
+ _slider = slider;
+ base.Load += GH_DoubleSliderPopup_Load;
+ InitializeComponent();
+ update();
+ }
+
+ private void btnOK_Click(object sender, EventArgs e)
+ {
+ if (numLower.Value > numUpper.Value)
+ {
+ numLower.Value = numUpper.Value - -10m;
+ }
+ if (numValue.Value < numLower.Value)
+ {
+ numValue.Value = numLower.Value;
+ }
+ else if (numValue.Value > numUpper.Value)
+ {
+ numValue.Value = numUpper.Value;
+ }
+ _slider.MinValue = (double)numLower.Value;
+ _slider.MaxValue = (double)numUpper.Value;
+ _slider.Value = (double)numValue.Value;
+ _slider.FireChangedEvent();
+ }
+
+ private void GH_DoubleSliderPopup_Load(object sender, EventArgs e)
+ {
+ GH_WindowsControlUtil.FixTextRenderingDefault(base.Controls);
+ }
+
+ private void InitializeComponent()
+ {
+ btnOK = new System.Windows.Forms.Button();
+ btnCancel = new System.Windows.Forms.Button();
+ numUpper = new System.Windows.Forms.NumericUpDown();
+ numLower = new System.Windows.Forms.NumericUpDown();
+ Panel2 = new System.Windows.Forms.Panel();
+ numValue = new System.Windows.Forms.NumericUpDown();
+ lblValue = new System.Windows.Forms.Label();
+ tblType = new System.Windows.Forms.TableLayoutPanel();
+ grpDomain = new System.Windows.Forms.GroupBox();
+ tblDomain = new System.Windows.Forms.TableLayoutPanel();
+ lblUpper = new System.Windows.Forms.Label();
+ lblLower = new System.Windows.Forms.Label();
+ tblValue = new System.Windows.Forms.TableLayoutPanel();
+ lblValue = new System.Windows.Forms.Label();
+ pnSep1 = new System.Windows.Forms.Panel();
+ Panel1 = new System.Windows.Forms.Panel();
+ numUpper.BeginInit();
+ numLower.BeginInit();
+ Panel2.SuspendLayout();
+ numValue.BeginInit();
+ tblType.SuspendLayout();
+ grpDomain.SuspendLayout();
+ tblDomain.SuspendLayout();
+ tblValue.SuspendLayout();
+ SuspendLayout();
+ btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
+ btnOK.Dock = System.Windows.Forms.DockStyle.Right;
+ btnOK.Click += new System.EventHandler(btnOK_Click);
+ System.Drawing.Point location = new System.Drawing.Point(120, 8);
+ btnOK.Location = location;
+ btnOK.Name = "btnOK";
+ System.Drawing.Size size = new System.Drawing.Size(80, 24);
+ btnOK.Size = size;
+ btnOK.TabIndex = 5;
+ btnOK.Text = "OK";
+ btnOK.UseVisualStyleBackColor = true;
+ numUpper.DecimalPlaces = 4;
+ numUpper.Dock = System.Windows.Forms.DockStyle.Fill;
+ System.Drawing.Point location2 = new System.Drawing.Point(94, 23);
+ numUpper.Location = location2;
+ System.Windows.Forms.Padding margin = new System.Windows.Forms.Padding(0);
+ numUpper.Margin = margin;
+ decimal maximum = decimal.MaxValue;
+ numUpper.Maximum = maximum;
+ lblValue.Dock = System.Windows.Forms.DockStyle.Fill;
+ location = new System.Drawing.Point(1, 1);
+ lblValue.Location = location;
+ margin = new System.Windows.Forms.Padding(1);
+ lblValue.Margin = margin;
+ lblValue.Name = "lblValue";
+ size = new System.Drawing.Size(92, 23);
+ lblValue.Size = size;
+ lblValue.TabIndex = 4;
+ lblValue.Text = "Value";
+ lblValue.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ maximum = 0m;
+ numUpper.Minimum = maximum;
+ numUpper.Name = "numUpper";
+ size = new System.Drawing.Size(176, 20);
+ numUpper.Size = size;
+ numUpper.TabIndex = 5;
+ numUpper.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+ numUpper.ThousandsSeparator = true;
+ maximum = 0m;
+ numUpper.Value = maximum;
+ numLower.DecimalPlaces = 4;
+ numLower.Dock = System.Windows.Forms.DockStyle.Fill;
+ location = new System.Drawing.Point(94, 0);
+ numLower.Location = location;
+ margin = new System.Windows.Forms.Padding(0);
+ numLower.Margin = margin;
+ maximum = decimal.MaxValue;
+ numLower.Maximum = maximum;
+ maximum = decimal.MinValue;
+ numLower.Minimum = maximum;
+ numLower.Name = "numLower";
+ size = new System.Drawing.Size(176, 20);
+ numLower.Size = size;
+ numLower.TabIndex = 3;
+ numLower.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+ numLower.ThousandsSeparator = true;
+ Panel2.Controls.Add(btnOK);
+ Panel2.Controls.Add(btnCancel);
+ Panel2.Dock = System.Windows.Forms.DockStyle.Bottom;
+ location = new System.Drawing.Point(5, 273);
+ Panel2.Location = location;
+ Panel2.Name = "Panel2";
+ margin = new System.Windows.Forms.Padding(0, 8, 0, 0);
+ Panel2.Padding = margin;
+ size = new System.Drawing.Size(280, 32);
+ Panel2.Size = size;
+ Panel2.TabIndex = 9;
+ btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ btnCancel.Dock = System.Windows.Forms.DockStyle.Right;
+ location = new System.Drawing.Point(200, 8);
+ btnCancel.Location = location;
+ btnCancel.Name = "btnCancel";
+ size = new System.Drawing.Size(80, 24);
+ btnCancel.Size = size;
+ btnCancel.TabIndex = 6;
+ btnCancel.Text = "Cancel";
+ btnCancel.UseVisualStyleBackColor = true;
+ numValue.DecimalPlaces = 4;
+ numValue.Dock = System.Windows.Forms.DockStyle.Fill;
+ location = new System.Drawing.Point(94, 0);
+ numValue.Location = location;
+ margin = new System.Windows.Forms.Padding(0);
+ numValue.Margin = margin;
+ maximum = decimal.MaxValue;
+ numValue.Maximum = maximum;
+ maximum = decimal.MinValue;
+ numValue.Minimum = maximum;
+ numValue.Name = "numValue";
+ size = new System.Drawing.Size(176, 20);
+ numValue.Size = size;
+ numValue.TabIndex = 11;
+ numValue.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+ grpDomain.Controls.Add(tblDomain);
+ grpDomain.Dock = System.Windows.Forms.DockStyle.Top;
+ location = new System.Drawing.Point(5, 88);
+ grpDomain.Location = location;
+ grpDomain.Name = "grpDomain";
+ margin = new System.Windows.Forms.Padding(5);
+ grpDomain.Padding = margin;
+ size = new System.Drawing.Size(280, 94);
+ grpDomain.Size = size;
+ grpDomain.TabIndex = 12;
+ grpDomain.TabStop = false;
+ grpDomain.Text = "Numeric domain";
+ tblDomain.ColumnCount = 2;
+ tblDomain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 35f));
+ tblDomain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 65f));
+ tblDomain.Controls.Add(lblLower, 0, 0);
+ tblDomain.Controls.Add(numLower, 1, 0);
+ tblDomain.Controls.Add(lblUpper, 0, 1);
+ tblDomain.Controls.Add(numUpper, 1, 1);
+ tblDomain.Controls.Add(lblValue, 0, 2);
+ tblDomain.Controls.Add(numValue, 1, 2);
+ tblDomain.Dock = System.Windows.Forms.DockStyle.Fill;
+ location = new System.Drawing.Point(5, 18);
+ tblDomain.Location = location;
+ tblDomain.Name = "tblDomain";
+ tblDomain.RowCount = 3;
+ tblDomain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333f));
+ tblDomain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333f));
+ tblDomain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333f));
+ size = new System.Drawing.Size(270, 71);
+ tblDomain.Size = size;
+ tblDomain.TabIndex = 0;
+ lblUpper.Dock = System.Windows.Forms.DockStyle.Fill;
+ location = new System.Drawing.Point(1, 24);
+ lblUpper.Location = location;
+ margin = new System.Windows.Forms.Padding(1);
+ lblUpper.Margin = margin;
+ lblUpper.Name = "lblUpper";
+ size = new System.Drawing.Size(92, 21);
+ lblUpper.Size = size;
+ lblUpper.TabIndex = 12;
+ lblUpper.Text = "Upper limit";
+ lblUpper.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ lblLower.Dock = System.Windows.Forms.DockStyle.Fill;
+ location = new System.Drawing.Point(1, 1);
+ lblLower.Location = location;
+ margin = new System.Windows.Forms.Padding(1);
+ lblLower.Margin = margin;
+ lblLower.Name = "lblLower";
+ size = new System.Drawing.Size(92, 21);
+ lblLower.Size = size;
+ lblLower.TabIndex = 4;
+ lblLower.Text = "Lower limit";
+ lblLower.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ lblValue.Dock = System.Windows.Forms.DockStyle.Fill;
+ location = new System.Drawing.Point(1, 1);
+ lblValue.Location = location;
+ margin = new System.Windows.Forms.Padding(1);
+ lblValue.Margin = margin;
+ lblValue.Name = "lblValue";
+ size = new System.Drawing.Size(92, 23);
+ lblValue.Size = size;
+ lblValue.TabIndex = 4;
+ lblValue.Text = "Value";
+ lblValue.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ pnSep1.Dock = System.Windows.Forms.DockStyle.Top;
+ location = new System.Drawing.Point(5, 78);
+ pnSep1.Location = location;
+ pnSep1.Name = "pnSep1";
+ size = new System.Drawing.Size(280, 10);
+ pnSep1.Size = size;
+ pnSep1.TabIndex = 14;
+ Panel1.Dock = System.Windows.Forms.DockStyle.Top;
+ location = new System.Drawing.Point(5, 182);
+ Panel1.Location = location;
+ Panel1.Name = "Panel1";
+ size = new System.Drawing.Size(280, 10);
+ Panel1.Size = size;
+ Panel1.TabIndex = 15;
+ System.Drawing.SizeF sizeF2 = (base.AutoScaleDimensions = new System.Drawing.SizeF(6f, 13f));
+ base.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ size = (base.ClientSize = new System.Drawing.Size(290, 155));
+ base.Controls.Add(Panel1);
+ base.Controls.Add(grpDomain);
+ base.Controls.Add(Panel2);
+ base.Controls.Add(pnSep1);
+ base.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ base.HelpButton = true;
+ base.KeyPreview = true;
+ base.MaximizeBox = false;
+ base.MinimizeBox = false;
+ size = (MinimumSize = new System.Drawing.Size(260, 140));
+ base.Name = "GH_DoubleSliderPopup";
+ margin = (base.Padding = new System.Windows.Forms.Padding(5));
+ base.ShowIcon = false;
+ base.ShowInTaskbar = false;
+ base.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ base.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
+ Text = "SliderSettings";
+ numUpper.EndInit();
+ numLower.EndInit();
+ Panel2.ResumeLayout(false);
+ numValue.EndInit();
+ tblType.ResumeLayout(false);
+ grpDomain.ResumeLayout(false);
+ tblDomain.ResumeLayout(false);
+ tblValue.ResumeLayout(false);
+ ResumeLayout(false);
+ }
+
+ private void update()
+ {
+ _minValue = _slider.MinValue;
+ _maxValue = _slider.MaxValue;
+ _value = _slider.Value;
+ numLower.Value = new decimal(_minValue);
+ numUpper.Value = new decimal(_maxValue);
+ numValue.Value = new decimal(_value);
+ }
+
+ private void numLower_ValueChanged(object sender, EventArgs e)
+ {
+ }
+}
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/GH_SwitchAction.cs b/SandWorm/CustomComponent/GH_SwitchAction.cs
new file mode 100644
index 0000000..30998ff
--- /dev/null
+++ b/SandWorm/CustomComponent/GH_SwitchAction.cs
@@ -0,0 +1,230 @@
+using System;
+using System.Collections.Generic;
+using Grasshopper.Kernel;
+using Grasshopper.Kernel.Undo;
+
+namespace SandWorm
+{
+public class GH_SwitchAction : GH_UndoAction
+{
+ private class GH_SwitcherConnectivity
+ {
+ private Guid componentId;
+
+ private bool noUnit;
+
+ private string unit;
+
+ private List inputs;
+
+ private List outputs;
+
+ public string Unit
+ {
+ get
+ {
+ return unit;
+ }
+ set
+ {
+ unit = value;
+ }
+ }
+
+ public List Inputs
+ {
+ get
+ {
+ return inputs;
+ }
+ set
+ {
+ inputs = value;
+ }
+ }
+
+ public List Outputs
+ {
+ get
+ {
+ return outputs;
+ }
+ set
+ {
+ outputs = value;
+ }
+ }
+
+ public Guid ComponentId => componentId;
+
+ public GH_SwitcherConnectivity()
+ {
+ inputs = new List();
+ outputs = new List();
+ }
+
+ public static GH_SwitcherConnectivity Create(GH_SwitcherComponent component)
+ {
+ GH_SwitcherConnectivity gH_SwitcherConnectivity = new GH_SwitcherConnectivity();
+ gH_SwitcherConnectivity.componentId = component.InstanceGuid;
+ EvaluationUnit activeUnit = component.ActiveUnit;
+ if (activeUnit != null)
+ {
+ gH_SwitcherConnectivity.noUnit = false;
+ gH_SwitcherConnectivity.unit = activeUnit.Name;
+ }
+ else if (component.UnitlessExistence)
+ {
+ gH_SwitcherConnectivity.noUnit = true;
+ }
+ foreach (IGH_Param item in component.Params.Input)
+ {
+ GH_SwitcherParamState gH_SwitcherParamState = new GH_SwitcherParamState(GH_ParameterSide.Input, item.InstanceGuid);
+ gH_SwitcherConnectivity.inputs.Add(gH_SwitcherParamState);
+ foreach (IGH_Param source in item.Sources)
+ {
+ gH_SwitcherParamState.Targets.Add(source.InstanceGuid);
+ }
+ }
+ foreach (IGH_Param item2 in component.Params.Output)
+ {
+ GH_SwitcherParamState gH_SwitcherParamState2 = new GH_SwitcherParamState(GH_ParameterSide.Output, item2.InstanceGuid);
+ gH_SwitcherConnectivity.outputs.Add(gH_SwitcherParamState2);
+ foreach (IGH_Param recipient in item2.Recipients)
+ {
+ gH_SwitcherParamState2.Targets.Add(recipient.InstanceGuid);
+ }
+ }
+ return gH_SwitcherConnectivity;
+ }
+
+ public void Apply(GH_SwitcherComponent component, GH_Document document)
+ {
+ if (noUnit)
+ {
+ component.ClearUnit(recompute: true, recordEvent: false);
+ }
+ else
+ {
+ component.SwitchUnit(unit, recompute: true, recordEvent: false);
+ }
+ for (int i = 0; i < inputs.Count; i++)
+ {
+ GH_SwitcherParamState gH_SwitcherParamState = inputs[i];
+ int num = component.Params.IndexOfInputParam(gH_SwitcherParamState.ParameterId);
+ if (num == -1)
+ {
+ continue;
+ }
+ IGH_Param iGH_Param = component.Params.Input[num];
+ for (int j = 0; j < gH_SwitcherParamState.Targets.Count; j++)
+ {
+ IGH_Param iGH_Param2 = document.FindParameter(gH_SwitcherParamState.Targets[j]);
+ if (iGH_Param2 != null)
+ {
+ iGH_Param.AddSource(iGH_Param2);
+ }
+ }
+ }
+ for (int k = 0; k < outputs.Count; k++)
+ {
+ GH_SwitcherParamState gH_SwitcherParamState2 = outputs[k];
+ int num2 = component.Params.IndexOfOutputParam(gH_SwitcherParamState2.ParameterId);
+ if (num2 != -1)
+ {
+ IGH_Param source = component.Params.Output[num2];
+ for (int l = 0; l < gH_SwitcherParamState2.Targets.Count; l++)
+ {
+ document.FindParameter(gH_SwitcherParamState2.Targets[l])?.AddSource(source);
+ }
+ }
+ }
+ }
+ }
+
+ private class GH_SwitcherParamState
+ {
+ private GH_ParameterSide side;
+
+ private Guid paramId;
+
+ private List targets;
+
+ public Guid ParameterId
+ {
+ get
+ {
+ return paramId;
+ }
+ set
+ {
+ paramId = value;
+ }
+ }
+
+ public List Targets
+ {
+ get
+ {
+ return targets;
+ }
+ set
+ {
+ targets = value;
+ }
+ }
+
+ public GH_SwitcherParamState(GH_ParameterSide side, Guid paramId)
+ {
+ this.side = side;
+ this.paramId = paramId;
+ targets = new List();
+ }
+ }
+
+ private GH_SwitcherConnectivity oldState;
+
+ private string newUnit;
+
+ public override bool ExpiresSolution => true;
+
+ public GH_SwitchAction(GH_SwitcherComponent component, string newUnit)
+ {
+ if (component == null)
+ {
+ throw new ArgumentNullException("component");
+ }
+ oldState = GH_SwitcherConnectivity.Create(component);
+ this.newUnit = newUnit;
+ }
+
+ protected override void Internal_Redo(GH_Document doc)
+ {
+ IGH_DocumentObject iGH_DocumentObject = doc.FindObject(oldState.ComponentId, topLevelOnly: true);
+ if (iGH_DocumentObject == null || !(iGH_DocumentObject is GH_SwitcherComponent))
+ {
+ throw new GH_UndoException("Switcher component with id[" + oldState.ComponentId.ToString() + "] not found");
+ }
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)iGH_DocumentObject;
+ if (newUnit != null)
+ {
+ gH_SwitcherComponent.SwitchUnit(newUnit, recompute: true, recordEvent: false);
+ }
+ else
+ {
+ gH_SwitcherComponent.ClearUnit(recompute: false);
+ }
+ }
+
+ protected override void Internal_Undo(GH_Document doc)
+ {
+ IGH_DocumentObject iGH_DocumentObject = doc.FindObject(oldState.ComponentId, topLevelOnly: true);
+ if (iGH_DocumentObject == null || !(iGH_DocumentObject is GH_SwitcherComponent))
+ {
+ throw new GH_UndoException("Switcher component with id[" + oldState.ComponentId.ToString() + "] not found");
+ }
+ GH_SwitcherComponent component = (GH_SwitcherComponent)iGH_DocumentObject;
+ oldState.Apply(component, doc);
+ }
+}
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/GH_SwitcherComponent.cs b/SandWorm/CustomComponent/GH_SwitcherComponent.cs
new file mode 100644
index 0000000..3f58e46
--- /dev/null
+++ b/SandWorm/CustomComponent/GH_SwitcherComponent.cs
@@ -0,0 +1,336 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+using GH_IO.Serialization;
+using Grasshopper.Kernel;
+using Rhino;
+
+namespace SandWorm
+{
+public abstract class GH_SwitcherComponent : GH_Component
+{
+ protected EvaluationUnitManager evalUnits;
+
+ protected EvaluationUnit activeUnit;
+
+ protected RuntimeComponentData staticData;
+
+ public RuntimeComponentData StaticData => staticData;
+
+ public List EvalUnits => evalUnits.Units;
+
+ public EvaluationUnit ActiveUnit => activeUnit;
+
+ protected virtual string DefaultEvaluationUnit => null;
+
+ public virtual string UnitMenuName => "Evaluation Units";
+
+ public virtual string UnitMenuHeader => "Select evaluation unit";
+
+ public virtual bool UnitlessExistence => false;
+
+ protected internal GH_SwitcherComponent(string sName, string sAbbreviation, string sDescription, string sCategory, string sSubCategory)
+ : base(sName, sAbbreviation, sDescription, sCategory, sSubCategory)
+ {
+ base.Phase = GH_SolutionPhase.Blank;
+ SetupEvaluationUnits();
+ }
+
+ protected override void PostConstructor()
+ {
+ evalUnits = new EvaluationUnitManager(this);
+ RegisterEvaluationUnits(evalUnits);
+ base.PostConstructor();
+ staticData = new RuntimeComponentData(this);
+ }
+
+ private void SetupEvaluationUnits()
+ {
+ if (activeUnit != null)
+ {
+ throw new ArgumentException("Invalid switcher state. No evaluation unit must be active at this point.");
+ }
+ EvaluationUnit evaluationUnit = GetUnit(DefaultEvaluationUnit);
+ if (evaluationUnit == null && !UnitlessExistence)
+ {
+ if (EvalUnits.Count == 0)
+ {
+ throw new ArgumentException("Switcher has no evaluation units registered and UnitlessExistence is false.");
+ }
+ evaluationUnit = EvalUnits[0];
+ }
+ if (OnPingDocument() != null)
+ {
+ RhinoApp.WriteLine("Component belongs to a document at a stage where it should not belong to one.");
+ }
+ SwitchUnit(evaluationUnit, recompute: false, recordEvent: false);
+ }
+
+ public EvaluationUnit GetUnit(string name)
+ {
+ return evalUnits.GetUnit(name);
+ }
+
+ protected override void SolveInstance(IGH_DataAccess DA)
+ {
+ SolveInstance(DA, activeUnit);
+ }
+
+ protected abstract void SolveInstance(IGH_DataAccess DA, EvaluationUnit unit);
+
+ public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
+ {
+ base.AppendAdditionalMenuItems(menu);
+ if (evalUnits.Units.Count <= 0)
+ {
+ return;
+ }
+ GH_DocumentObject.Menu_AppendSeparator(menu);
+ ToolStripMenuItem toolStripMenuItem = GH_DocumentObject.Menu_AppendItem(menu, "Units");
+ foreach (EvaluationUnit unit in evalUnits.Units)
+ {
+ GH_DocumentObject.Menu_AppendItem(toolStripMenuItem.DropDown, unit.Name, Menu_ActivateUnit, null, enabled: true, unit.Active).Tag = unit;
+ }
+ GH_DocumentObject.Menu_AppendSeparator(menu);
+ }
+
+ private void Menu_ActivateUnit(object sender, EventArgs e)
+ {
+ try
+ {
+ EvaluationUnit evaluationUnit = (EvaluationUnit)((ToolStripMenuItem)sender).Tag;
+ if (evaluationUnit != null)
+ {
+ SwitchUnit(evaluationUnit);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw ex;
+ }
+ }
+
+ private void SetReadState()
+ {
+ if (activeUnit != null)
+ {
+ staticData.UnregisterUnit(activeUnit);
+ activeUnit.Active = false;
+ activeUnit = null;
+ }
+ GH_Document gH_Document = OnPingDocument();
+ if (gH_Document != null)
+ {
+ gH_Document.Modified();
+ gH_Document.DestroyAttributeCache();
+ gH_Document.DestroyObjectTable();
+ }
+ if (base.Attributes != null)
+ {
+ ((GH_SwitcherComponentAttributes)base.Attributes).OnSwitchUnit();
+ }
+ Name = staticData.CachedName;
+ NickName = staticData.CachedNickname;
+ Description = staticData.CachedDescription;
+ SetIconOverride(staticData.CachedIcon);
+ if (base.Attributes != null)
+ {
+ base.Attributes.ExpireLayout();
+ }
+ }
+
+ public void ClearUnit(bool recompute = true, bool recordEvent = true)
+ {
+ if (!UnitlessExistence)
+ {
+ return;
+ }
+ if (activeUnit != null)
+ {
+ if (recordEvent)
+ {
+ RecordUndoEvent("Switch Unit", new GH_SwitchAction(this, null));
+ }
+ staticData.UnregisterUnit(activeUnit);
+ activeUnit.Active = false;
+ activeUnit = null;
+ }
+ GH_Document gH_Document = OnPingDocument();
+ if (gH_Document != null)
+ {
+ gH_Document.Modified();
+ gH_Document.DestroyAttributeCache();
+ gH_Document.DestroyObjectTable();
+ }
+ if (base.Attributes != null)
+ {
+ ((GH_SwitcherComponentAttributes)base.Attributes).OnSwitchUnit();
+ }
+ Name = staticData.CachedName;
+ NickName = staticData.CachedNickname;
+ Description = staticData.CachedDescription;
+ SetIconOverride(staticData.CachedIcon);
+ if (base.Attributes != null)
+ {
+ base.Attributes.ExpireLayout();
+ }
+ if (recompute)
+ {
+ ExpireSolution(recompute: true);
+ }
+ }
+
+ public virtual void SwitchUnit(string unitName, bool recompute = true, bool recordEvent = true)
+ {
+ EvaluationUnit unit = evalUnits.GetUnit(unitName);
+ if (unit != null)
+ {
+ SwitchUnit(unit, recompute, recordEvent);
+ }
+ }
+
+ protected virtual void SwitchUnit(EvaluationUnit unit, bool recompute = true, bool recordEvent = true)
+ {
+ if (unit != null && (activeUnit == null || activeUnit != unit))
+ {
+ if (recordEvent)
+ {
+ RecordUndoEvent("Switch Unit", new GH_SwitchAction(this, unit.Name));
+ }
+ if (activeUnit != null)
+ {
+ staticData.UnregisterUnit(activeUnit);
+ activeUnit.Active = false;
+ activeUnit = null;
+ }
+ staticData.RegisterUnit(unit);
+ activeUnit = unit;
+ activeUnit.Active = true;
+ GH_Document gH_Document = OnPingDocument();
+ if (gH_Document != null)
+ {
+ gH_Document.Modified();
+ gH_Document.DestroyAttributeCache();
+ gH_Document.DestroyObjectTable();
+ }
+ if (base.Attributes != null)
+ {
+ ((GH_SwitcherComponentAttributes)base.Attributes).OnSwitchUnit();
+ }
+ if (unit.DisplayName != null)
+ {
+ SetIconOverride(unit.Icon);
+ }
+ if (base.Attributes != null)
+ {
+ base.Attributes.ExpireLayout();
+ }
+ if (recompute)
+ {
+ ExpireSolution(recompute: true);
+ }
+ }
+ }
+
+ protected virtual void RegisterEvaluationUnits(EvaluationUnitManager mngr)
+ {
+ }
+
+ private void _Setup()
+ {
+ Setup((GH_SwitcherComponentAttributes)base.Attributes);
+ }
+
+ protected virtual void Setup(GH_SwitcherComponentAttributes attr)
+ {
+ }
+
+ protected virtual void OnComponentLoaded()
+ {
+ }
+
+ protected virtual void OnComponentReset(GH_ExtendableComponentAttributes attr)
+ {
+ }
+
+ public override bool Write(GH_IWriter writer)
+ {
+ staticData.PrepareWrite(activeUnit);
+ bool result = base.Write(writer);
+ staticData.RestoreWrite(activeUnit);
+ if (activeUnit != null)
+ {
+ writer.CreateChunk("ActiveUnit").SetString("unitname", activeUnit.Name);
+ }
+ try
+ {
+ GH_IWriter gH_IWriter = writer.CreateChunk("EvalUnits");
+ gH_IWriter.SetInt32("count", evalUnits.Units.Count);
+ for (int i = 0; i < evalUnits.Units.Count; i++)
+ {
+ EvaluationUnit evaluationUnit = evalUnits.Units[i];
+ GH_IWriter writer2 = gH_IWriter.CreateChunk("unit", i);
+ evaluationUnit.Write(writer2);
+ }
+ return result;
+ }
+ catch (Exception ex)
+ {
+ RhinoApp.WriteLine(ex.Message + "\n" + ex.StackTrace);
+ throw ex;
+ }
+ }
+
+ public override bool Read(GH_IReader reader)
+ {
+ bool flag = true;
+ SetReadState();
+ flag &= base.Read(reader);
+ string text = null;
+ if (reader.ChunkExists("ActiveUnit"))
+ {
+ text = reader.FindChunk("ActiveUnit").GetString("unitname");
+ }
+ if (reader.ChunkExists("EvalUnits"))
+ {
+ GH_IReader gH_IReader = reader.FindChunk("EvalUnits");
+ int value = -1;
+ if (gH_IReader.TryGetInt32("count", ref value))
+ {
+ for (int i = 0; i < value; i++)
+ {
+ if (gH_IReader.ChunkExists("unit", i))
+ {
+ GH_IReader gH_IReader2 = gH_IReader.FindChunk("unit", i);
+ string @string = gH_IReader2.GetString("name");
+ if (text != null)
+ {
+ @string.Equals(text);
+ }
+ evalUnits.GetUnit(@string)?.Read(gH_IReader2);
+ }
+ }
+ }
+ }
+ if (text != null)
+ {
+ GetUnit(text);
+ SwitchUnit(GetUnit(text), recompute: false, recordEvent: false);
+ }
+ for (int j = 0; j < EvalUnits.Count; j++)
+ {
+ if (!EvalUnits[j].Active)
+ {
+ EvalUnits[j].NewParameterIds();
+ }
+ }
+ OnComponentLoaded();
+ return flag;
+ }
+
+ public override void CreateAttributes()
+ {
+ Setup((GH_SwitcherComponentAttributes)(m_attributes = new GH_SwitcherComponentAttributes(this)));
+ }
+}
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/GH_SwitcherComponentAttributes.cs b/SandWorm/CustomComponent/GH_SwitcherComponentAttributes.cs
new file mode 100644
index 0000000..c2d65fa
--- /dev/null
+++ b/SandWorm/CustomComponent/GH_SwitcherComponentAttributes.cs
@@ -0,0 +1,1024 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Reflection;
+using System.Windows.Forms;
+using GH_IO.Serialization;
+using Grasshopper;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+using Grasshopper.Kernel;
+using Grasshopper.Kernel.Attributes;
+using Rhino;
+
+namespace SandWorm
+{
+ public class GH_SwitcherComponentAttributes : GH_ComponentAttributes
+ {
+ private int offset;
+
+ private float _minWidth;
+
+ private GH_Attr_Widget _activeToolTip;
+
+ protected GH_MenuCollection unitMenuCollection;
+
+ protected GH_MenuCollection collection;
+
+ private GH_MenuCollection composedCollection;
+
+ private MenuDropDown _UnitDrop;
+
+ public float MinWidth
+ {
+ get
+ {
+ return _minWidth;
+ }
+ set
+ {
+ _minWidth = value;
+ }
+ }
+
+ public GH_SwitcherComponentAttributes(GH_SwitcherComponent component)
+ : base(component)
+ {
+ collection = new GH_MenuCollection();
+ composedCollection = new GH_MenuCollection();
+ composedCollection.Merge(collection);
+ CreateUnitDropDown();
+ InitializeUnitParameters();
+ }
+
+ public void InitializeUnitParameters()
+ {
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)base.Owner;
+ if (gH_SwitcherComponent.EvalUnits == null)
+ {
+ return;
+ }
+ foreach (EvaluationUnit evalUnit in gH_SwitcherComponent.EvalUnits)
+ {
+ foreach (ExtendedPlug input in evalUnit.Inputs)
+ {
+ if (input.Parameter.Attributes == null)
+ {
+ input.Parameter.Attributes = new GH_LinkedParamAttributes(input.Parameter, this);
+ }
+ }
+ foreach (ExtendedPlug output in evalUnit.Outputs)
+ {
+ if (output.Parameter.Attributes == null)
+ {
+ output.Parameter.Attributes = new GH_LinkedParamAttributes(output.Parameter, this);
+ }
+ }
+ }
+ }
+
+ private void ComposeMenu()
+ {
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)base.Owner;
+ composedCollection = new GH_MenuCollection();
+ EvaluationUnit activeUnit = gH_SwitcherComponent.ActiveUnit;
+ if (activeUnit != null && activeUnit.Context.Collection != null)
+ {
+ composedCollection.Merge(gH_SwitcherComponent.ActiveUnit.Context.Collection);
+ }
+ if (collection != null)
+ {
+ composedCollection.Merge(collection);
+ }
+ if (unitMenuCollection != null)
+ {
+ composedCollection.Merge(unitMenuCollection);
+ }
+ }
+
+ protected void CreateUnitDropDown()
+ {
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)base.Owner;
+ if (gH_SwitcherComponent.EvalUnits != null && gH_SwitcherComponent.EvalUnits.Count != 0 && (gH_SwitcherComponent.EvalUnits.Count != 1 || gH_SwitcherComponent.UnitlessExistence))
+ {
+ MenuPanel menuPanel = new MenuPanel(0, "panel_units")
+ {
+ Header = "Unit selection"
+ };
+ string text = gH_SwitcherComponent.UnitMenuName;
+ if (text == null)
+ {
+ text = "Evaluation Units";
+ }
+ string text2 = gH_SwitcherComponent.UnitMenuHeader;
+ if (text2 == null)
+ {
+ text2 = "Select evaluation unit";
+ }
+ unitMenuCollection = new GH_MenuCollection();
+ GH_ExtendableMenu gH_ExtendableMenu = new GH_ExtendableMenu(0, "menu_units")
+ {
+ Name = text,
+ Header = text2
+ };
+ gH_ExtendableMenu.AddControl(menuPanel);
+ _UnitDrop = new MenuDropDown(0, "dropdown_units", "units")
+ {
+ //VisibleItemCount = 10
+ };
+ _UnitDrop.ValueChanged += _UnitDrop__valueChanged;
+ _UnitDrop.Header = "Evaluation unit selector";
+ menuPanel.AddControl(_UnitDrop);
+ List evalUnits = gH_SwitcherComponent.EvalUnits;
+ if (gH_SwitcherComponent.UnitlessExistence)
+ {
+ _UnitDrop.AddItem("--NONE--", null);
+ }
+ for (int i = 0; i < evalUnits.Count; i++)
+ {
+ _UnitDrop.AddItem(evalUnits[i].Name, evalUnits[i].DisplayName, evalUnits[i]);
+ }
+ gH_ExtendableMenu.Expand();
+ unitMenuCollection.AddMenu(gH_ExtendableMenu);
+ }
+ }
+
+ private void _UnitDrop__valueChanged(object sender, EventArgs e)
+ {
+ try
+ {
+ MenuDropDown menuDropDown = (MenuDropDown)sender;
+ MenuDropDown.Entry entry = menuDropDown.Items[menuDropDown.Value];
+ if (entry.data != null)
+ {
+ EvaluationUnit evaluationUnit = (EvaluationUnit)entry.data;
+ ((GH_SwitcherComponent)base.Owner).SwitchUnit(evaluationUnit.Name);
+ }
+ else
+ {
+ ((GH_SwitcherComponent)base.Owner).ClearUnit();
+ }
+ }
+ catch (Exception ex)
+ {
+ RhinoApp.WriteLine("Error selection:" + ex.StackTrace);
+ }
+ }
+
+ public void OnSwitchUnit()
+ {
+ EvaluationUnit activeUnit = ((GH_SwitcherComponent)base.Owner).ActiveUnit;
+ ComposeMenu();
+ if (activeUnit != null)
+ {
+ if (_UnitDrop != null)
+ {
+ int num = _UnitDrop.FindIndex(activeUnit.Name);
+ if (num != -1)
+ {
+ _UnitDrop.Value = num;
+ }
+ }
+ }
+ else if (((GH_SwitcherComponent)base.Owner).UnitlessExistence && _UnitDrop != null)
+ {
+ _UnitDrop.Value = 0;
+ }
+ }
+
+ public void AddMenu(GH_ExtendableMenu menu)
+ {
+ collection.AddMenu(menu);
+ }
+
+ public override bool Write(GH_IWriter writer)
+ {
+ try
+ {
+ collection.Write(writer);
+ }
+ catch (Exception)
+ {
+ }
+ return base.Write(writer);
+ }
+
+ public override bool Read(GH_IReader reader)
+ {
+ try
+ {
+ collection.Read(reader);
+ }
+ catch (Exception)
+ {
+ }
+ return base.Read(reader);
+ }
+
+ protected override void PrepareForRender(GH_Canvas canvas)
+ {
+ base.PrepareForRender(canvas);
+ LayoutMenuCollection();
+ }
+
+ protected void LayoutBaseComponent()
+ {
+ _ = (GH_SwitcherComponent)base.Owner;
+ Pivot = GH_Convert.ToPoint(Pivot);
+ m_innerBounds = LayoutComponentBox2(base.Owner);
+ int num = ComputeW_ico(base.Owner);
+ float width = composedCollection.GetMinLayoutSize().Width;
+ float num2 = Math.Max(MinWidth, width);
+ int add_offset = 0;
+ if (num2 > (float)num)
+ {
+ add_offset = (int)((double)(num2 - (float)num) / 2.0);
+ }
+ LayoutInputParams2(base.Owner, m_innerBounds, add_offset);
+ LayoutOutputParams2(base.Owner, m_innerBounds, add_offset);
+ Bounds = LayoutBounds2(base.Owner, m_innerBounds);
+ }
+
+ private int ComputeW_ico(IGH_Component owner)
+ {
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)owner;
+ int num = 24;
+ int num2 = 0;
+ int num3 = 0;
+ foreach (IGH_Param componentInput in gH_SwitcherComponent.StaticData.GetComponentInputs())
+ {
+ int val = componentInput.StateTags.Count * 20;
+ num3 = Math.Max(num3, val);
+ num2 = Math.Max(num2, GH_FontServer.StringWidth(componentInput.NickName, StandardFont.Font()));
+ }
+ num2 = Math.Max(num2 + 6, 12);
+ num2 += num3;
+ int num4 = 0;
+ int num5 = 0;
+ foreach (IGH_Param componentOutput in gH_SwitcherComponent.StaticData.GetComponentOutputs())
+ {
+ int val2 = componentOutput.StateTags.Count * 20;
+ num5 = Math.Max(num5, val2);
+ num4 = Math.Max(num4, GH_FontServer.StringWidth(componentOutput.NickName, StandardFont.Font()));
+ }
+ num4 = Math.Max(num4 + 6, 12);
+ num4 += num5;
+ return num2 + num + num4 + 6;
+ }
+
+ public RectangleF LayoutBounds2(IGH_Component owner, RectangleF bounds)
+ {
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)owner;
+ foreach (IGH_Param item in gH_SwitcherComponent.StaticData.GetComponentInputSection())
+ {
+ bounds = RectangleF.Union(bounds, item.Attributes.Bounds);
+ }
+ foreach (IGH_Param item2 in gH_SwitcherComponent.StaticData.GetComponentOutputSection())
+ {
+ bounds = RectangleF.Union(bounds, item2.Attributes.Bounds);
+ }
+ bounds.Inflate(2f, 2f);
+ return bounds;
+ }
+
+ public RectangleF LayoutComponentBox2(IGH_Component owner)
+ {
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)owner;
+ int val = Math.Max(gH_SwitcherComponent.StaticData.GetComponentInputSection().Count, gH_SwitcherComponent.StaticData.GetComponentOutputSection().Count) * 20;
+ val = Math.Max(val, 24);
+ int num = 24;
+ if (!GH_Attributes.IsIconMode(owner.IconDisplayMode))
+ {
+ val = Math.Max(val, GH_Convert.ToSize(GH_FontServer.MeasureString(owner.NickName, StandardFont.LargeFont())).Width + 6);
+ }
+ return GH_Convert.ToRectangle(new RectangleF(owner.Attributes.Pivot.X - 0.5f * (float)num, owner.Attributes.Pivot.Y - 0.5f * (float)val, num, val));
+ }
+
+ public void LayoutInputParams2(IGH_Component owner, RectangleF componentBox, int add_offset)
+ {
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)owner;
+ List componentInputSection = gH_SwitcherComponent.StaticData.GetComponentInputSection();
+ int count = componentInputSection.Count;
+ if (count == 0)
+ {
+ return;
+ }
+ int num = 0;
+ int num2 = 0;
+ foreach (IGH_Param componentInput in gH_SwitcherComponent.StaticData.GetComponentInputs())
+ {
+ int val = componentInput.StateTags.Count * 20;
+ num2 = Math.Max(num2, val);
+ num = Math.Max(num, GH_FontServer.StringWidth(componentInput.NickName, StandardFont.Font()));
+ }
+ num = Math.Max(num + 6, 12);
+ num += num2 + add_offset;
+ float num3 = componentBox.Height / (float)count;
+ for (int i = 0; i < count; i++)
+ {
+ IGH_Param iGH_Param = componentInputSection[i];
+ if (iGH_Param.Attributes == null)
+ {
+ iGH_Param.Attributes = new GH_LinkedParamAttributes(iGH_Param, owner.Attributes);
+ }
+ float num4 = componentBox.X - (float)num;
+ float num5 = componentBox.Y + (float)i * num3;
+ float width = num - 3;
+ float height = num3;
+ PointF pivot = new PointF(num4 + 0.5f * (float)num, num5 + 0.5f * num3);
+ iGH_Param.Attributes.Pivot = pivot;
+ RectangleF @in = new RectangleF(num4, num5, width, height);
+ iGH_Param.Attributes.Bounds = GH_Convert.ToRectangle(@in);
+ }
+ bool flag = false;
+ for (int j = 0; j < count; j++)
+ {
+ IGH_Param iGH_Param2 = componentInputSection[j];
+ GH_LinkedParamAttributes gH_LinkedParamAttributes = (GH_LinkedParamAttributes)iGH_Param2.Attributes;
+ FieldInfo field = typeof(GH_LinkedParamAttributes).GetField("m_renderTags", BindingFlags.Instance | BindingFlags.NonPublic);
+ GH_StateTagList gH_StateTagList = iGH_Param2.StateTags;
+ if (!(field != null))
+ {
+ continue;
+ }
+ if (gH_StateTagList.Count == 0)
+ {
+ gH_StateTagList = null;
+ field.SetValue(gH_LinkedParamAttributes, gH_StateTagList);
+ }
+ if (gH_StateTagList != null)
+ {
+ flag = true;
+ Rectangle box = GH_Convert.ToRectangle(gH_LinkedParamAttributes.Bounds);
+ box.X += num2;
+ box.Width -= num2;
+ gH_StateTagList.Layout(box, GH_StateTagLayoutDirection.Left);
+ box = gH_StateTagList.BoundingBox;
+ if (!box.IsEmpty)
+ {
+ gH_LinkedParamAttributes.Bounds = RectangleF.Union(gH_LinkedParamAttributes.Bounds, box);
+ }
+ field.SetValue(gH_LinkedParamAttributes, gH_StateTagList);
+ }
+ }
+ if (flag)
+ {
+ float num6 = float.MaxValue;
+ for (int k = 0; k < count; k++)
+ {
+ IGH_Attributes attributes = componentInputSection[k].Attributes;
+ num6 = Math.Min(num6, attributes.Bounds.X);
+ }
+ for (int l = 0; l < count; l++)
+ {
+ IGH_Attributes attributes2 = componentInputSection[l].Attributes;
+ RectangleF bounds = attributes2.Bounds;
+ bounds.Width = bounds.Right - num6;
+ bounds.X = num6;
+ attributes2.Bounds = bounds;
+ }
+ }
+ }
+
+ public void LayoutOutputParams2(IGH_Component owner, RectangleF componentBox, int add_offset)
+ {
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)owner;
+ List componentOutputSection = gH_SwitcherComponent.StaticData.GetComponentOutputSection();
+ int count = componentOutputSection.Count;
+ if (count == 0)
+ {
+ return;
+ }
+ int num = 0;
+ int num2 = 0;
+ foreach (IGH_Param componentOutput in gH_SwitcherComponent.StaticData.GetComponentOutputs())
+ {
+ int val = componentOutput.StateTags.Count * 20;
+ num2 = Math.Max(num2, val);
+ num = Math.Max(num, GH_FontServer.StringWidth(componentOutput.NickName, StandardFont.Font()));
+ }
+ num = Math.Max(num + 6, 12);
+ num += num2 + add_offset;
+ float num3 = componentBox.Height / (float)count;
+ for (int i = 0; i < count; i++)
+ {
+ IGH_Param iGH_Param = componentOutputSection[i];
+ if (iGH_Param.Attributes == null)
+ {
+ iGH_Param.Attributes = new GH_LinkedParamAttributes(iGH_Param, owner.Attributes);
+ }
+ float num4 = componentBox.Right + 3f;
+ float num5 = componentBox.Y + (float)i * num3;
+ float width = num;
+ float height = num3;
+ PointF pivot = new PointF(num4 + 0.5f * (float)num, num5 + 0.5f * num3);
+ iGH_Param.Attributes.Pivot = pivot;
+ RectangleF @in = new RectangleF(num4, num5, width, height);
+ iGH_Param.Attributes.Bounds = GH_Convert.ToRectangle(@in);
+ }
+ bool flag = false;
+ for (int j = 0; j < count; j++)
+ {
+ IGH_Param iGH_Param2 = componentOutputSection[j];
+ GH_LinkedParamAttributes gH_LinkedParamAttributes = (GH_LinkedParamAttributes)iGH_Param2.Attributes;
+ FieldInfo field = typeof(GH_LinkedParamAttributes).GetField("m_renderTags", BindingFlags.Instance | BindingFlags.NonPublic);
+ GH_StateTagList gH_StateTagList = iGH_Param2.StateTags;
+ if (!(field != null))
+ {
+ continue;
+ }
+ if (gH_StateTagList.Count == 0)
+ {
+ gH_StateTagList = null;
+ field.SetValue(gH_LinkedParamAttributes, gH_StateTagList);
+ }
+ if (gH_StateTagList != null)
+ {
+ flag = true;
+ Rectangle box = GH_Convert.ToRectangle(gH_LinkedParamAttributes.Bounds);
+ box.Width -= num2;
+ gH_StateTagList.Layout(box, GH_StateTagLayoutDirection.Right);
+ box = gH_StateTagList.BoundingBox;
+ if (!box.IsEmpty)
+ {
+ gH_LinkedParamAttributes.Bounds = RectangleF.Union(gH_LinkedParamAttributes.Bounds, box);
+ }
+ field.SetValue(gH_LinkedParamAttributes, gH_StateTagList);
+ }
+ }
+ if (flag)
+ {
+ float num6 = float.MinValue;
+ for (int k = 0; k < count; k++)
+ {
+ IGH_Attributes attributes = componentOutputSection[k].Attributes;
+ num6 = Math.Max(num6, attributes.Bounds.Right);
+ }
+ for (int l = 0; l < count; l++)
+ {
+ IGH_Attributes attributes2 = componentOutputSection[l].Attributes;
+ RectangleF bounds = attributes2.Bounds;
+ bounds.Width = num6 - bounds.X;
+ attributes2.Bounds = bounds;
+ }
+ }
+ }
+
+ protected override void Layout()
+ {
+ Pivot = GH_Convert.ToPoint(Pivot);
+ LayoutBaseComponent();
+ _ = (GH_SwitcherComponent)base.Owner;
+ List inputs = new List();
+ List outputs = new List();
+ composedCollection.GetMenuPlugs(ref inputs, ref outputs, onlyVisible: true);
+ LayoutMenuInputs(m_innerBounds);
+ LayoutMenuOutputs(m_innerBounds);
+ Bounds = LayoutExtBounds(m_innerBounds, inputs, outputs);
+ FixLayout(outputs);
+ LayoutMenu();
+ }
+
+ public RectangleF LayoutExtBounds(RectangleF bounds, List ins, List outs)
+ {
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)base.Owner;
+ foreach (IGH_Param componentInput in gH_SwitcherComponent.StaticData.GetComponentInputs())
+ {
+ RectangleF bounds2 = componentInput.Attributes.Bounds;
+ if (bounds2.X < bounds.X)
+ {
+ float num = bounds.X - bounds2.X;
+ bounds.X = bounds2.X;
+ bounds.Width += num;
+ }
+ if (bounds2.X + bounds2.Width > bounds.X + bounds.Width)
+ {
+ float num2 = bounds2.X + bounds2.Width - (bounds.X + bounds.Width);
+ bounds.Width += num2;
+ }
+ }
+ foreach (IGH_Param componentOutput in gH_SwitcherComponent.StaticData.GetComponentOutputs())
+ {
+ RectangleF bounds3 = componentOutput.Attributes.Bounds;
+ if (bounds3.X < bounds.X)
+ {
+ float num3 = bounds.X - bounds3.X;
+ bounds.X = bounds3.X;
+ bounds.Width += num3;
+ }
+ if (bounds3.X + bounds3.Width > bounds.X + bounds.Width)
+ {
+ float num4 = bounds3.X + bounds3.Width - (bounds.X + bounds.Width);
+ bounds.Width += num4;
+ }
+ }
+ bounds.Inflate(2f, 2f);
+ return bounds;
+ }
+
+ public void LayoutMenuInputs(RectangleF componentBox)
+ {
+ GH_SwitcherComponent obj = (GH_SwitcherComponent)base.Owner;
+ float num = 0f;
+ int num2 = 0;
+ foreach (IGH_Param componentInput in obj.StaticData.GetComponentInputs())
+ {
+ int val = 20 * componentInput.StateTags.Count;
+ num2 = Math.Max(num2, val);
+ num = Math.Max(num, GH_FontServer.StringWidth(componentInput.NickName, StandardFont.Font()));
+ }
+ num = Math.Max(num + 6f, 12f);
+ num += (float)num2;
+ float num3 = Bounds.Height;
+ for (int i = 0; i < composedCollection.Menus.Count; i++)
+ {
+ float num4 = -1f;
+ float num5 = 0f;
+ bool expanded = composedCollection.Menus[i].Expanded;
+ if (expanded)
+ {
+ num4 = num3 + composedCollection.Menus[i].Height;
+ num5 = Math.Max(composedCollection.Menus[i].Inputs.Count, composedCollection.Menus[i].Outputs.Count) * 20;
+ }
+ else
+ {
+ num4 = num3 + 5f;
+ num5 = 0f;
+ }
+ List inputs = composedCollection.Menus[i].Inputs;
+ int count = inputs.Count;
+ if (count != 0)
+ {
+ float num6 = num5 / (float)count;
+ for (int j = 0; j < count; j++)
+ {
+ IGH_Param parameter = inputs[j].Parameter;
+ if (parameter.Attributes == null)
+ {
+ parameter.Attributes = new GH_LinkedParamAttributes(parameter, this);
+ }
+ float num7 = componentBox.X - num;
+ float num8 = num4 + componentBox.Y + (float)j * num6;
+ float width = num - 3f;
+ float height = num6;
+ PointF pivot = new PointF(num7 + 0.5f * num, num8 + 0.5f * num6);
+ parameter.Attributes.Pivot = pivot;
+ RectangleF @in = new RectangleF(num7, num8, width, height);
+ parameter.Attributes.Bounds = GH_Convert.ToRectangle(@in);
+ }
+ for (int k = 0; k < count; k++)
+ {
+ IGH_Param parameter2 = inputs[k].Parameter;
+ GH_LinkedParamAttributes gH_LinkedParamAttributes = (GH_LinkedParamAttributes)parameter2.Attributes;
+ FieldInfo field = typeof(GH_LinkedParamAttributes).GetField("m_renderTags", BindingFlags.Instance | BindingFlags.NonPublic);
+ GH_StateTagList gH_StateTagList = parameter2.StateTags;
+ if (field != null)
+ {
+ if (gH_StateTagList.Count == 0)
+ {
+ gH_StateTagList = null;
+ field.SetValue(gH_LinkedParamAttributes, gH_StateTagList);
+ }
+ if (gH_StateTagList != null)
+ {
+ Rectangle box = GH_Convert.ToRectangle(gH_LinkedParamAttributes.Bounds);
+ box.X += num2;
+ box.Width -= num2;
+ gH_StateTagList.Layout(box, GH_StateTagLayoutDirection.Left);
+ box = gH_StateTagList.BoundingBox;
+ if (!box.IsEmpty)
+ {
+ gH_LinkedParamAttributes.Bounds = RectangleF.Union(gH_LinkedParamAttributes.Bounds, box);
+ }
+ field.SetValue(gH_LinkedParamAttributes, gH_StateTagList);
+ }
+ }
+ if (!expanded)
+ {
+ gH_LinkedParamAttributes.Bounds = new RectangleF(gH_LinkedParamAttributes.Bounds.X, gH_LinkedParamAttributes.Bounds.Y, 5f, gH_LinkedParamAttributes.Bounds.Height);
+ }
+ }
+ }
+ num3 += composedCollection.Menus[i].TotalHeight;
+ }
+ }
+
+ public void LayoutMenuOutputs(RectangleF componentBox)
+ {
+ GH_SwitcherComponent obj = (GH_SwitcherComponent)base.Owner;
+ float num = 0f;
+ int num2 = 0;
+ foreach (IGH_Param componentOutput in obj.StaticData.GetComponentOutputs())
+ {
+ int val = 20 * componentOutput.StateTags.Count;
+ num2 = Math.Max(num2, val);
+ num = Math.Max(num, GH_FontServer.StringWidth(componentOutput.NickName, StandardFont.Font()));
+ }
+ num = Math.Max(num + 6f, 12f);
+ num += (float)num2;
+ float num3 = Bounds.Height;
+ for (int i = 0; i < composedCollection.Menus.Count; i++)
+ {
+ float num4 = -1f;
+ float num5 = 0f;
+ bool expanded = composedCollection.Menus[i].Expanded;
+ if (expanded)
+ {
+ num4 = num3 + composedCollection.Menus[i].Height;
+ num5 = Math.Max(composedCollection.Menus[i].Inputs.Count, composedCollection.Menus[i].Outputs.Count) * 20;
+ }
+ else
+ {
+ num4 = num3 + 5f;
+ num5 = 0f;
+ }
+ List outputs = composedCollection.Menus[i].Outputs;
+ int count = outputs.Count;
+ if (count != 0)
+ {
+ float num6 = num5 / (float)count;
+ for (int j = 0; j < count; j++)
+ {
+ IGH_Param parameter = outputs[j].Parameter;
+ if (parameter.Attributes == null)
+ {
+ parameter.Attributes = new GH_LinkedParamAttributes(parameter, this);
+ }
+ float num7 = componentBox.Right + 3f;
+ float num8 = num4 + componentBox.Y + (float)j * num6;
+ float width = num;
+ float height = num6;
+ PointF pivot = new PointF(num7 + 0.5f * num, num8 + 0.5f * num6);
+ parameter.Attributes.Pivot = pivot;
+ RectangleF @in = new RectangleF(num7, num8, width, height);
+ parameter.Attributes.Bounds = GH_Convert.ToRectangle(@in);
+ }
+ for (int k = 0; k < count; k++)
+ {
+ IGH_Param parameter2 = outputs[k].Parameter;
+ GH_LinkedParamAttributes gH_LinkedParamAttributes = (GH_LinkedParamAttributes)parameter2.Attributes;
+ FieldInfo field = typeof(GH_LinkedParamAttributes).GetField("m_renderTags", BindingFlags.Instance | BindingFlags.NonPublic);
+ GH_StateTagList gH_StateTagList = parameter2.StateTags;
+ if (field != null)
+ {
+ if (gH_StateTagList.Count == 0)
+ {
+ gH_StateTagList = null;
+ field.SetValue(gH_LinkedParamAttributes, gH_StateTagList);
+ }
+ if (gH_StateTagList != null)
+ {
+ Rectangle box = GH_Convert.ToRectangle(gH_LinkedParamAttributes.Bounds);
+ box.Width -= num2;
+ gH_StateTagList.Layout(box, GH_StateTagLayoutDirection.Right);
+ box = gH_StateTagList.BoundingBox;
+ if (!box.IsEmpty)
+ {
+ gH_LinkedParamAttributes.Bounds = RectangleF.Union(gH_LinkedParamAttributes.Bounds, box);
+ }
+ field.SetValue(gH_LinkedParamAttributes, gH_StateTagList);
+ }
+ }
+ if (!expanded)
+ {
+ gH_LinkedParamAttributes.Bounds = new RectangleF(gH_LinkedParamAttributes.Bounds.X + gH_LinkedParamAttributes.Bounds.Width - 5f, gH_LinkedParamAttributes.Bounds.Y, 5f, gH_LinkedParamAttributes.Bounds.Height);
+ }
+ }
+ }
+ num3 += composedCollection.Menus[i].TotalHeight;
+ }
+ }
+
+ protected void FixLayout(List outs)
+ {
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)base.Owner;
+ float width = Bounds.Width;
+ if (_minWidth > width)
+ {
+ Bounds = new RectangleF(Bounds.X, Bounds.Y, _minWidth, Bounds.Height);
+ }
+ float num = Bounds.Width - width;
+ foreach (IGH_Param componentOutput in gH_SwitcherComponent.StaticData.GetComponentOutputs())
+ {
+ PointF pivot = componentOutput.Attributes.Pivot;
+ RectangleF bounds = componentOutput.Attributes.Bounds;
+ componentOutput.Attributes.Pivot = new PointF(pivot.X + num, pivot.Y);
+ componentOutput.Attributes.Bounds = new RectangleF(bounds.Location.X + num, bounds.Location.Y, bounds.Width, bounds.Height);
+ }
+ foreach (IGH_Param componentInput in gH_SwitcherComponent.StaticData.GetComponentInputs())
+ {
+ PointF pivot2 = componentInput.Attributes.Pivot;
+ RectangleF bounds2 = componentInput.Attributes.Bounds;
+ componentInput.Attributes.Pivot = new PointF(pivot2.X + num, pivot2.Y);
+ componentInput.Attributes.Bounds = new RectangleF(bounds2.Location.X + num, bounds2.Location.Y, bounds2.Width, bounds2.Height);
+ }
+ }
+
+ private void LayoutMenuCollection()
+ {
+ GH_Palette impliedPalette = GH_CapsuleRenderEngine.GetImpliedPalette(base.Owner);
+ GH_PaletteStyle impliedStyle = GH_CapsuleRenderEngine.GetImpliedStyle(impliedPalette, Selected, base.Owner.Locked, base.Owner.Hidden);
+ composedCollection.Style = impliedStyle;
+ composedCollection.Palette = impliedPalette;
+ composedCollection.Layout();
+ }
+
+ protected void LayoutMenu()
+ {
+ offset = (int)Bounds.Height;
+ composedCollection.Pivot = new PointF(Bounds.X, (int)Bounds.Y + offset);
+ composedCollection.Width = Bounds.Width;
+ LayoutMenuCollection();
+ Bounds = new RectangleF(Bounds.X, Bounds.Y, Bounds.Width, Bounds.Height + composedCollection.Height);
+ }
+
+ protected override void Render(GH_Canvas iCanvas, Graphics graph, GH_CanvasChannel iChannel)
+ {
+ if (iChannel == GH_CanvasChannel.First)
+ {
+ iCanvas.CanvasPostPaintWidgets -= RenderPostWidgets;
+ iCanvas.CanvasPostPaintWidgets += RenderPostWidgets;
+ }
+ switch (iChannel)
+ {
+ default:
+ _ = 30;
+ break;
+ case GH_CanvasChannel.Wires:
+ foreach (IGH_Param item in base.Owner.Params.Input)
+ {
+ item.Attributes.RenderToCanvas(iCanvas, GH_CanvasChannel.Wires);
+ }
+ break;
+ case GH_CanvasChannel.Objects:
+ RenderComponentCapsule2(iCanvas, graph);
+ composedCollection.Render(new WidgetRenderArgs(iCanvas, WidgetChannel.Object));
+ break;
+ }
+ }
+
+ private void RenderPostWidgets(GH_Canvas sender)
+ {
+ composedCollection.Render(new WidgetRenderArgs(sender, WidgetChannel.Overlay));
+ }
+
+ protected void RenderComponentCapsule2(GH_Canvas canvas, Graphics graphics)
+ {
+ RenderComponentCapsule2(canvas, graphics, drawComponentBaseBox: true, drawComponentNameBox: true, drawJaggedEdges: true, drawParameterGrips: true, drawParameterNames: true, drawZuiElements: true);
+ }
+
+ protected void RenderComponentCapsule2(GH_Canvas canvas, Graphics graphics, bool drawComponentBaseBox, bool drawComponentNameBox, bool drawJaggedEdges, bool drawParameterGrips, bool drawParameterNames, bool drawZuiElements)
+ {
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)base.Owner;
+ RectangleF rec = (Bounds = Bounds);
+ if (!canvas.Viewport.IsVisible(ref rec, 10f))
+ {
+ return;
+ }
+ GH_Palette gH_Palette = GH_CapsuleRenderEngine.GetImpliedPalette(base.Owner);
+ if (gH_Palette == GH_Palette.Normal && !base.Owner.IsPreviewCapable)
+ {
+ gH_Palette = GH_Palette.Hidden;
+ }
+ GH_Capsule gH_Capsule = GH_Capsule.CreateCapsule(Bounds, gH_Palette);
+ bool left = base.Owner.Params.Input.Count == 0;
+ bool right = base.Owner.Params.Output.Count == 0;
+ gH_Capsule.SetJaggedEdges(left, right);
+ GH_PaletteStyle impliedStyle = GH_CapsuleRenderEngine.GetImpliedStyle(gH_Palette, Selected, base.Owner.Locked, base.Owner.Hidden);
+ if (drawParameterGrips)
+ {
+ foreach (IGH_Param staticInput in gH_SwitcherComponent.StaticData.StaticInputs)
+ {
+ gH_Capsule.AddInputGrip(staticInput.Attributes.InputGrip.Y);
+ }
+ foreach (IGH_Param dynamicInput in gH_SwitcherComponent.StaticData.DynamicInputs)
+ {
+ gH_Capsule.AddInputGrip(dynamicInput.Attributes.InputGrip.Y);
+ }
+ foreach (IGH_Param staticOutput in gH_SwitcherComponent.StaticData.StaticOutputs)
+ {
+ gH_Capsule.AddOutputGrip(staticOutput.Attributes.OutputGrip.Y);
+ }
+ foreach (IGH_Param dynamicOutput in gH_SwitcherComponent.StaticData.DynamicOutputs)
+ {
+ gH_Capsule.AddOutputGrip(dynamicOutput.Attributes.OutputGrip.Y);
+ }
+ }
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ canvas.SetSmartTextRenderingHint();
+ if (GH_Attributes.IsIconMode(base.Owner.IconDisplayMode))
+ {
+ if (drawComponentBaseBox)
+ {
+ if (base.Owner.Message != null)
+ {
+ gH_Capsule.RenderEngine.RenderMessage(graphics, base.Owner.Message, impliedStyle);
+ }
+ gH_Capsule.Render(graphics, impliedStyle);
+ }
+ if (drawComponentNameBox && base.Owner.Icon_24x24 != null)
+ {
+ if (base.Owner.Locked)
+ {
+ gH_Capsule.RenderEngine.RenderIcon(graphics, base.Owner.Icon_24x24_Locked, m_innerBounds);
+ }
+ else
+ {
+ gH_Capsule.RenderEngine.RenderIcon(graphics, base.Owner.Icon_24x24, m_innerBounds);
+ }
+ }
+ }
+ else
+ {
+ if (drawComponentBaseBox)
+ {
+ if (base.Owner.Message != null)
+ {
+ gH_Capsule.RenderEngine.RenderMessage(graphics, base.Owner.Message, impliedStyle);
+ }
+ gH_Capsule.Render(graphics, impliedStyle);
+ }
+ if (drawComponentNameBox)
+ {
+ GH_Capsule gH_Capsule2 = GH_Capsule.CreateTextCapsule(m_innerBounds, m_innerBounds, GH_Palette.Black, base.Owner.NickName, StandardFont.LargeFont(), GH_Orientation.vertical_center, 3, 6);
+ gH_Capsule2.Render(graphics, Selected, base.Owner.Locked, hidden: false);
+ gH_Capsule2.Dispose();
+ }
+ }
+ if (drawComponentNameBox && base.Owner.Obsolete && CentralSettings.CanvasObsoleteTags && canvas.DrawingMode == GH_CanvasMode.Control)
+ {
+ GH_GraphicsUtil.RenderObjectOverlay(graphics, base.Owner, m_innerBounds);
+ }
+ if (drawParameterNames)
+ {
+ RenderComponentParameters2(canvas, graphics, base.Owner, impliedStyle);
+ }
+ if (drawZuiElements)
+ {
+ RenderVariableParameterUI(canvas, graphics);
+ }
+ gH_Capsule.Dispose();
+ }
+
+ public static void RenderComponentParameters2(GH_Canvas canvas, Graphics graphics, IGH_Component owner, GH_PaletteStyle style)
+ {
+ GH_SwitcherComponent gH_SwitcherComponent = (GH_SwitcherComponent)owner;
+ int zoomFadeLow = GH_Canvas.ZoomFadeLow;
+ if (zoomFadeLow < 5)
+ {
+ return;
+ }
+ StringFormat farCenter = GH_TextRenderingConstants.FarCenter;
+ canvas.SetSmartTextRenderingHint();
+ SolidBrush solidBrush = new SolidBrush(Color.FromArgb(zoomFadeLow, style.Text));
+ foreach (IGH_Param staticInput in gH_SwitcherComponent.StaticData.StaticInputs)
+ {
+ RectangleF bounds = staticInput.Attributes.Bounds;
+ if (bounds.Width >= 1f)
+ {
+ graphics.DrawString(staticInput.NickName, StandardFont.Font(), solidBrush, bounds, farCenter);
+ GH_LinkedParamAttributes obj = (GH_LinkedParamAttributes)staticInput.Attributes;
+ ((GH_StateTagList)typeof(GH_LinkedParamAttributes).GetField("m_renderTags", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj))?.RenderStateTags(graphics);
+ }
+ }
+ farCenter = GH_TextRenderingConstants.NearCenter;
+ foreach (IGH_Param staticOutput in gH_SwitcherComponent.StaticData.StaticOutputs)
+ {
+ RectangleF bounds2 = staticOutput.Attributes.Bounds;
+ if (bounds2.Width >= 1f)
+ {
+ graphics.DrawString(staticOutput.NickName, StandardFont.Font(), solidBrush, bounds2, farCenter);
+ GH_LinkedParamAttributes obj2 = (GH_LinkedParamAttributes)staticOutput.Attributes;
+ ((GH_StateTagList)typeof(GH_LinkedParamAttributes).GetField("m_renderTags", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj2))?.RenderStateTags(graphics);
+ }
+ }
+ solidBrush.Dispose();
+ }
+
+ public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ GH_ObjectResponse gH_ObjectResponse = composedCollection.RespondToMouseUp(sender, e);
+ switch (gH_ObjectResponse)
+ {
+ case GH_ObjectResponse.Capture:
+ ExpireLayout();
+ sender.Invalidate();
+ return gH_ObjectResponse;
+ default:
+ ExpireLayout();
+ sender.Invalidate();
+ return GH_ObjectResponse.Release;
+ case GH_ObjectResponse.Ignore:
+ return base.RespondToMouseUp(sender, e);
+ }
+ }
+
+ public override GH_ObjectResponse RespondToMouseDoubleClick(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ GH_ObjectResponse gH_ObjectResponse = composedCollection.RespondToMouseDoubleClick(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ ExpireLayout();
+ sender.Refresh();
+ return gH_ObjectResponse;
+ }
+ return base.RespondToMouseDoubleClick(sender, e);
+ }
+
+ public override GH_ObjectResponse RespondToKeyDown(GH_Canvas sender, KeyEventArgs e)
+ {
+ GH_ObjectResponse gH_ObjectResponse = composedCollection.RespondToKeyDown(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ ExpireLayout();
+ sender.Refresh();
+ return gH_ObjectResponse;
+ }
+ return base.RespondToKeyDown(sender, e);
+ }
+
+ public override GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ GH_ObjectResponse gH_ObjectResponse = composedCollection.RespondToMouseMove(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ ExpireLayout();
+ sender.Refresh();
+ return gH_ObjectResponse;
+ }
+ return base.RespondToMouseMove(sender, e);
+ }
+
+ public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ GH_ObjectResponse gH_ObjectResponse = composedCollection.RespondToMouseDown(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ ExpireLayout();
+ sender.Refresh();
+ return gH_ObjectResponse;
+ }
+ return base.RespondToMouseDown(sender, e);
+ }
+
+ public override bool IsTooltipRegion(PointF pt)
+ {
+ _activeToolTip = null;
+ bool flag = base.IsTooltipRegion(pt);
+ if (flag)
+ {
+ return flag;
+ }
+ if (m_innerBounds.Contains(pt))
+ {
+ GH_Attr_Widget gH_Attr_Widget = collection.IsTtipPoint(pt);
+ if (gH_Attr_Widget != null)
+ {
+ _activeToolTip = gH_Attr_Widget;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public bool GetActiveTooltip(PointF pt)
+ {
+ GH_Attr_Widget gH_Attr_Widget = composedCollection.IsTtipPoint(pt);
+ if (gH_Attr_Widget != null)
+ {
+ _activeToolTip = gH_Attr_Widget;
+ return true;
+ }
+ return false;
+ }
+
+ public override void SetupTooltip(PointF canvasPoint, GH_TooltipDisplayEventArgs e)
+ {
+ GetActiveTooltip(canvasPoint);
+ if (_activeToolTip != null)
+ {
+ _activeToolTip.TooltipSetup(canvasPoint, e);
+ return;
+ }
+ e.Title = PathName;
+ e.Text = base.Owner.Description;
+ e.Description = base.Owner.InstanceDescription;
+ e.Icon = base.Owner.Icon_24x24;
+ if (base.Owner is IGH_Param)
+ {
+ IGH_Param obj = (IGH_Param)base.Owner;
+ string text = obj.TypeName;
+ if (obj.Access == GH_ParamAccess.list)
+ {
+ text += "[…]";
+ }
+ if (obj.Access == GH_ParamAccess.tree)
+ {
+ text += "{…;…;…}";
+ }
+ e.Title = $"{PathName} ({text})";
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/MenuButton.cs b/SandWorm/CustomComponent/MenuButton.cs
new file mode 100644
index 0000000..21dc2af
--- /dev/null
+++ b/SandWorm/CustomComponent/MenuButton.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using GH_IO.Serialization;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+
+namespace SandWorm
+{
+ public class MenuButton : GH_Attr_Widget
+ {
+ private bool _active;
+ private int _buttonHeight = 20;
+ private Rectangle _buttonBounds;
+ private int _verticalPadding = 8;
+ private string _toolTipText;
+
+ public bool Active
+ {
+ get
+ {
+ return _active;
+ }
+ set
+ {
+ _active = value;
+ }
+ }
+
+ public override string Name
+ {
+ get
+ {
+ return _name;
+ }
+ set
+ {
+ _name = value;
+ }
+ }
+
+ public MenuButton(int index, string id, string toolTip) : base(index, id)
+ {
+ Name = id;
+ _toolTipText = toolTip;
+ }
+
+ public override SizeF ComputeMinSize()
+ {
+ float height = TextRenderer.MeasureText("1", WidgetServer.Instance.SliderValueTagFont).Height;
+ return new System.Drawing.SizeF(20f, _buttonHeight + _verticalPadding);
+ }
+
+ public override void PostUpdateBounds(out float outHeight)
+ {
+ outHeight = ComputeMinSize().Height;
+ }
+
+ public override void Layout()
+ {
+ _buttonBounds = new Rectangle(
+ (int)base.CanvasPivot.X, (int)base.CanvasPivot.Y + (int)(_verticalPadding / 2),
+ (int)base.Width, _buttonHeight + _verticalPadding);
+ }
+
+ public override void Render(WidgetRenderArgs args)
+ {
+ Graphics graphics = args.Canvas.Graphics;
+ GH_Capsule button;
+ var buttonBox = _buttonBounds;
+ buttonBox.Height -= _verticalPadding;
+
+ if (_active)
+ button = GH_Capsule.CreateTextCapsule(buttonBox, buttonBox, GH_Palette.Grey, "Calibrating...", 1, 0);
+ else
+ button = GH_Capsule.CreateTextCapsule(buttonBox, buttonBox, GH_Palette.Black, this.Name, 1, 0);
+
+ button.Render(graphics, _active, false, false);
+ button.Dispose();
+ }
+
+ public override void TooltipSetup(System.Drawing.PointF canvasPoint, GH_TooltipDisplayEventArgs e)
+ {
+ e.Icon = null;
+ e.Title = _name + " (Button)";
+ e.Text = _toolTipText;
+ }
+
+ public override GH_Attr_Widget IsTtipPoint(PointF pt)
+ {
+ if (_buttonBounds.Contains((int)pt.X, (int)pt.Y))
+ {
+ return this;
+ }
+ return null;
+ }
+
+ public override GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Capture;
+ }
+
+ public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (base.CanvasBounds.Contains(e.CanvasLocation))
+ {
+ _active = true;
+ }
+ return GH_ObjectResponse.Release;
+ }
+
+ public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ // Checking if it's a left click, and if it's in the button's area
+ if (e.Button == System.Windows.Forms.MouseButtons.Left && ((RectangleF)_buttonBounds).Contains(e.CanvasLocation))
+ {
+ return GH_ObjectResponse.Handled;
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/MenuCheckBox.cs b/SandWorm/CustomComponent/MenuCheckBox.cs
new file mode 100644
index 0000000..f0daa6d
--- /dev/null
+++ b/SandWorm/CustomComponent/MenuCheckBox.cs
@@ -0,0 +1,180 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using GH_IO.Serialization;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+
+namespace SandWorm
+{
+
+public class MenuCheckBox : GH_Attr_Widget
+{
+ private bool _active;
+
+ private Rectangle _checkBounds;
+
+ private Size _checkSize;
+
+ private float _padding = 4f;
+
+ public bool RenderTag
+ {
+ get;
+ set;
+ }
+
+ public string Tag
+ {
+ get;
+ set;
+ }
+
+ public bool Active
+ {
+ get
+ {
+ return _active;
+ }
+ set
+ {
+ _active = value;
+ }
+ }
+
+ public event ValueChangeEventHandler ValueChanged;
+
+ public MenuCheckBox(int index, string id, string tag)
+ : base(index, id)
+ {
+ _checkSize = WidgetServer.Instance.CheckBoxSize;
+ _padding = WidgetServer.Instance.CheckBoxPadding;
+ _active = false;
+ Tag = tag;
+ RenderTag = true;
+ }
+
+ public override SizeF ComputeMinSize()
+ {
+ int num = _checkSize.Width;
+ int num2 = _checkSize.Height;
+ if (RenderTag && Tag != null)
+ {
+ Size size = TextRenderer.MeasureText(Tag, WidgetServer.Instance.TextFont);
+ num += size.Width + (int)_padding;
+ num2 = Math.Max(size.Height, num2);
+ }
+ return new SizeF(num, num2);
+ }
+
+ public override void PostUpdateBounds(out float outHeight)
+ {
+ outHeight = ComputeMinSize().Height;
+ }
+
+ public override void Layout()
+ {
+ float num = base.CanvasPivot.Y + (base.Height - (float)_checkSize.Height) / 2f;
+ _checkBounds = new Rectangle((int)(base.CanvasPivot.X + base.CanvasBounds.Width - (float)_checkSize.Width), (int)num, _checkSize.Width, _checkSize.Height);
+ }
+
+ public override bool Write(GH_IWriter writer)
+ {
+ writer.CreateChunk("Checkbox", Index).SetBoolean("Active", _active);
+ return true;
+ }
+
+ public override bool Read(GH_IReader reader)
+ {
+ GH_IReader gH_IReader = reader.FindChunk("Checkbox", Index);
+ if (gH_IReader != null)
+ _active = gH_IReader.GetBoolean("Active");
+ return true;
+ }
+
+ public override void Render(WidgetRenderArgs args)
+ {
+ GH_Canvas canvas = args.Canvas;
+ Graphics graphics = canvas.Graphics;
+ float zoom = canvas.Viewport.Zoom;
+ int num = 255;
+ if (zoom < 1f)
+ {
+ float num2 = (zoom - 0.5f) * 2f;
+ num = (int)((float)num * num2);
+ }
+ if (num < 0)
+ {
+ num = 0;
+ }
+ num = GH_Canvas.ZoomFadeLow;
+ Pen pen = new Pen(Color.FromArgb(num, 0, 0, 0), 1.5f);
+ SolidBrush brush = new SolidBrush(Color.FromArgb(num, 255, 255, 255));
+ SolidBrush brush2 = new SolidBrush(Color.FromArgb(num, 0, 0, 0));
+ if (RenderTag && Tag != null)
+ {
+ PointF point = new PointF(base.CanvasPivot.X, base.CanvasPivot.Y);
+ graphics.DrawString(Tag, WidgetServer.Instance.TextFont, brush2, point);
+ }
+ graphics.FillRectangle(brush, _checkBounds);
+ graphics.DrawRectangle(pen, _checkBounds);
+ if (_active)
+ {
+ int num3 = (int)((double)_padding / 2.0);
+ Rectangle rect = new Rectangle(_checkBounds.X + num3, _checkBounds.Y + num3, _checkBounds.Width - num3 * 2, _checkBounds.Height - num3 * 2);
+ graphics.FillRectangle(brush2, rect);
+ }
+ }
+
+ public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (base.CanvasBounds.Contains(e.CanvasLocation))
+ {
+ _active = !_active;
+ }
+ if (this.ValueChanged != null)
+ {
+ ValueChanged(this, new EventArgs());
+ }
+ return GH_ObjectResponse.Release;
+ }
+
+ public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (_checkBounds.Contains((int)e.CanvasX, (int)e.CanvasY))
+ {
+ return GH_ObjectResponse.Handled;
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Capture;
+ }
+
+ public override GH_Attr_Widget IsTtipPoint(PointF pt)
+ {
+ if (_checkBounds.Contains((int)pt.X, (int)pt.Y))
+ {
+ return this;
+ }
+ return null;
+ }
+
+ public override void TooltipSetup(PointF canvasPoint, GH_TooltipDisplayEventArgs e)
+ {
+ e.Icon = null;
+ e.Title = _name + " (Checkbox)";
+ e.Text = _header;
+ if (_active)
+ {
+ e.Description = "ON";
+ }
+ else
+ {
+ e.Description = "OFF";
+ }
+ }
+}
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/MenuDropDown.cs b/SandWorm/CustomComponent/MenuDropDown.cs
new file mode 100644
index 0000000..9bdcfed
--- /dev/null
+++ b/SandWorm/CustomComponent/MenuDropDown.cs
@@ -0,0 +1,565 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+using GH_IO.Serialization;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+
+namespace SandWorm
+{
+ public class MenuDropDown : GH_Attr_Widget
+ {
+ private class MenuDropDownWindow : GH_Attr_Widget
+ {
+ private MenuDropDown _dropMenu;
+
+ private int _tempActive = -1;
+
+ private int _tempStart;
+
+ private int _maxLen;
+
+ private Rectangle _contentBox;
+
+ public MenuDropDownWindow(MenuDropDown parent)
+ : base(0, "")
+ {
+ _dropMenu = parent;
+ }
+
+ public void Update()
+ {
+ int count = _dropMenu.Items.Count;
+ if (_dropMenu.LastValidValue > count)
+ _dropMenu.Value = -1;
+
+ if (_dropMenu.Items.Count == 0)
+ _tempStart = 0;
+
+ _maxLen = count;
+ int num2 = count * 20;
+ base.Height = num2;
+ _contentBox = new Rectangle((int)base.CanvasPivot.X, (int)base.CanvasPivot.Y, (int)base.Width, num2);
+ }
+
+ public override SizeF ComputeMinSize()
+ {
+ return new SizeF(10f, 10f);
+ }
+
+ public override void Layout()
+ {
+ Update();
+ }
+
+ public override void Render(WidgetRenderArgs args)
+ {
+ if (args.Channel != WidgetChannel.Overlay)
+ {
+ return;
+ }
+ Graphics graphics = args.Canvas.Graphics;
+ StringFormat stringFormat = new StringFormat();
+ stringFormat.Alignment = StringAlignment.Center;
+ Pen pen = new Pen(Brushes.Gray);
+
+ System.Drawing.Drawing2D.GraphicsPath path = RoundedRect(_contentBox, 2);
+ graphics.DrawPath(pen, path);
+ graphics.FillPath(Brushes.White, path);
+ int num = 0;
+ for (int i = _tempStart; i < _tempStart + _maxLen; i++)
+ {
+ Brush white = Brushes.White;
+ Brush white2 = Brushes.White;
+ if (i == _tempActive)
+ {
+ white = new SolidBrush(Color.FromArgb(174, 213, 129));
+ white2 = Brushes.White;
+ }
+ else if (i == _dropMenu.Value)
+ {
+ white = new SolidBrush(Color.FromArgb(238, 238, 238));
+ white2 = new SolidBrush(Color.FromArgb(45, 45, 45));
+ }
+ else
+ {
+ white = new SolidBrush(Color.White);
+ white2 = new SolidBrush(Color.FromArgb(45, 45, 45));
+ }
+ System.Drawing.Drawing2D.GraphicsPath rect = RoundedRect(new Rectangle((int)base.Transform.X, (int)base.Transform.Y + 20 * num, (int)base.Width, 20), 2);
+ graphics.FillPath(white, rect);
+ graphics.DrawString(_dropMenu.Items[i].content, WidgetServer.Instance.DropdownFont, white2, base.Transform.X + base.Width / 2f, (int)base.Transform.Y + 20 * num + 5, stringFormat);
+ num++;
+ }
+ }
+
+ public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Capture;
+ }
+
+ public override GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (Contains(e.CanvasLocation))
+ _tempActive = _tempStart + (int)((e.CanvasLocation.Y - base.Transform.Y) / 20f);
+ else
+ _tempActive = -1;
+
+ return GH_ObjectResponse.Capture;
+ }
+
+ public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+
+ if (_contentBox.Contains((int)e.CanvasLocation.X, (int)e.CanvasLocation.Y))
+ {
+ _dropMenu.Value = _tempStart + (int)((e.CanvasLocation.Y - base.Transform.Y) / 20f);
+ _tempActive = -1;
+ _dropMenu.HideWindow(fire: true);
+ return GH_ObjectResponse.Release;
+ }
+ _dropMenu.HideWindow(fire: false);
+ return GH_ObjectResponse.Release;
+ }
+
+ public override bool Contains(PointF pt)
+ {
+ return canvasBounds.Contains(pt);
+ }
+ }
+
+ public class Entry
+ {
+ public string content;
+
+ public string name;
+
+ public string Header { get; set; }
+
+ public int index;
+
+ public object data;
+
+ public Entry(string name, string content, int ind)
+ {
+ this.content = content;
+ this.name = name;
+ index = ind;
+ }
+
+ public Entry(string name, string content, int ind, string header)
+ {
+ this.content = content;
+ this.name = name;
+ index = ind;
+ Header = header;
+ }
+ }
+
+ private MenuDropDownWindow _window;
+
+ public bool expanded;
+
+ private static int default_item_index = 0;
+
+ private int current_value;
+
+ private int last_valid_value;
+
+ private List _items;
+
+ private string _emptyText = "empty";
+
+ public int Value
+ {
+ get
+ {
+ return current_value;
+ }
+ set
+ {
+ current_value = Math.Max(value, 0);
+ last_valid_value = ((value >= 0) ? value : 0);
+ }
+ }
+
+ public int LastValidValue => last_valid_value;
+
+ public List Items => _items;
+ ///
+ /// Gets selected item
+ ///
+ public Entry Item => _items[Value];
+
+ private bool Empty => _items.Count == 0;
+
+ public event ValueChangeEventHandler ValueChanged;
+
+ public int FindIndex(string name)
+ {
+ for (int i = 0; i < _items.Count; i++)
+ {
+ if (_items[i].name.Equals(name))
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public override void PostUpdateBounds(out float outHeight)
+ {
+ _window.Width = base.Width;
+ outHeight = ComputeMinSize().Height;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// currently UNUSED
+ public MenuDropDown(int index, string id, string tag = "")
+ : base(index, id)
+ {
+ _items = new List();
+ _window = new MenuDropDownWindow(this);
+ _window.Parent = this;
+ }
+
+ ///
+ /// Use this one if you want to create a dropdown but keep the desriptions from the textinput.
+ ///
+ ///
+ ///
+ ///
+ public MenuDropDown(MenuStaticText textInput, int index = 0, string id = "")
+ : base(index, id)
+ {
+ Header = textInput.Header;
+ Name = textInput.Text;
+ _items = new List();
+ _window = new MenuDropDownWindow(this);
+ _window.Parent = this;
+ }
+
+ public MenuDropDown AddItem(string name, string cont, string header = "")
+ {
+ Entry item = new Entry(name, cont, _items.Count, header);
+
+ _items.Add(item);
+ Update();
+
+ return this; //fluent interface ftw
+ }
+
+ public MenuDropDown AddItem(string name, string cont, object data)
+ {
+ Entry entry = new Entry(name, cont, _items.Count);
+ entry.data = data;
+ _items.Add(entry);
+ Update();
+
+ return this; //fluent interface ftw
+ }
+
+ ///
+ /// Populate your dropdown menus from an Enum list. use typeof(yourEnum)
+ ///
+ /// input typeof(MyEnum)
+ public MenuDropDown AddEnum(Type enumType)
+ {
+ if (!typeof(Enum).IsAssignableFrom(enumType))
+ throw new ArgumentException("enumType should describe an enum");
+
+ Array names = Enum.GetNames(enumType);
+
+ foreach (string name in names)
+ {
+ AddItem(name, name, name);
+ }
+
+ return this;
+
+ }
+
+
+
+
+ private void Update()
+ {
+ if (_items.Count == 0)
+ {
+ current_value = 0;
+ }
+ _window.Update();
+ }
+
+ public override void Layout()
+ {
+ _window.UpdateBounds(base.CanvasPivot, base.Width);
+ _window.Layout();
+ }
+
+ public void Clear()
+ {
+ _items.Clear();
+ Update();
+ }
+
+ public override SizeF ComputeMinSize()
+ {
+ int num = 0;
+ int num2 = 0;
+ if (Empty)
+ {
+ Size size = TextRenderer.MeasureText(_emptyText, WidgetServer.Instance.DropdownFont);
+ num = size.Width + 4 + 10;
+ num2 = size.Height + 2;
+ }
+ else
+ {
+ foreach (Entry item in _items)
+ {
+ Size size2 = TextRenderer.MeasureText(item.content, WidgetServer.Instance.DropdownFont);
+ int val = size2.Width + 4 + 10;
+ int val2 = size2.Height + 2;
+ num = Math.Max(num, val);
+ num2 = Math.Max(num2, val2);
+ }
+ }
+ return new SizeF(num, num2);
+ }
+
+ public override void Render(WidgetRenderArgs args)
+ {
+ GH_Canvas canvas = args.Canvas;
+ if (args.Channel == WidgetChannel.Overlay)
+ {
+ if (expanded)
+ {
+ _window.Render(args);
+ }
+ }
+ else if (args.Channel == WidgetChannel.Object)
+ {
+ Graphics graphics = canvas.Graphics;
+ float zoom = canvas.Viewport.Zoom;
+ int num = 255;
+ if (zoom < 1f)
+ {
+ float num2 = (zoom - 0.5f) * 2f;
+ num = (int)((float)num * num2);
+ }
+ if (num < 0)
+ {
+ num = 0;
+ }
+ num = GH_Canvas.ZoomFadeLow;
+ SolidBrush brush = new SolidBrush(Color.FromArgb(num, 90, 90, 90));
+ SolidBrush brush2 = new SolidBrush(Color.FromArgb(num, 190, 190, 190));
+ SolidBrush brush3 = new SolidBrush(Color.FromArgb(num, 45, 45, 45));
+ SolidBrush brush4 = new SolidBrush(Color.FromArgb(num, 255, 255, 255));
+ Pen pen = new Pen(brush2);
+ StringFormat stringFormat = new StringFormat();
+ stringFormat.Alignment = StringAlignment.Center;
+ if (Empty)
+ {
+ PointF point = new PointF(base.CanvasPivot.X + base.Width / 2f, base.CanvasBounds.Y + 2f);
+ var path = RoundedRect(GH_Attr_Widget.Convert(base.CanvasBounds), 2);
+ graphics.DrawPath(pen, path);
+ graphics.FillPath(brush4, path);
+ graphics.DrawString(_emptyText, WidgetServer.Instance.DropdownFont, brush, point, stringFormat);
+ }
+ else
+ {
+ PointF point2 = new PointF(base.CanvasPivot.X + (base.Width - 13f) / 2f, base.CanvasBounds.Y + 2f);
+
+ var path = RoundedRect(GH_Attr_Widget.Convert(base.CanvasBounds), 2);
+ graphics.DrawPath(pen, path);
+ graphics.FillPath(brush4, path);
+ graphics.DrawString(_items[current_value].content, WidgetServer.Instance.DropdownFont, brush, point2, stringFormat);
+
+ PointF p1 = new PointF(base.CanvasPivot.X + base.Width - 13, base.CanvasPivot.Y + 6);
+ PointF p2 = new PointF(base.CanvasPivot.X + base.Width - 5, base.CanvasPivot.Y + 6);
+ PointF p3 = new PointF(base.CanvasPivot.X + base.Width - 9, base.CanvasPivot.Y + 12);
+ PointF[] curvePoints = { p1, p2, p3 };
+
+ //graphics.DrawPolygon(pen, curvePoints);
+ graphics.FillPolygon(brush3, curvePoints);
+ }
+ }
+ }
+
+
+ // Fixed tooltip in the folded dropdown menu - TODO: Fix it in the unfolded!
+ // It is hidden in the MenuScrollBar class, but I didn't manage to get the popup to work.
+ public override void TooltipSetup(PointF canvasPoint, GH_TooltipDisplayEventArgs e)
+ {
+ e.Icon = null;
+ e.Title = _name;
+ e.Text = _header;
+ if (_header != null)
+ {
+ e.Text += "\n";
+ }
+
+ e.Description = _description;
+ }
+
+ // This allowed us to get the static text tooltip :-)
+ public override GH_Attr_Widget IsTtipPoint(System.Drawing.PointF pt)
+ {
+ if (new System.Drawing.RectangleF(transfromation.X, transfromation.Y, base.Width, base.Height).Contains(pt))
+ {
+ return this;
+ }
+ return null;
+ }
+
+ public static System.Drawing.Drawing2D.GraphicsPath RoundedRect(Rectangle bounds, int radius)
+ {
+ int diameter = radius * 2;
+ Size size = new Size(diameter, diameter);
+ Rectangle arc = new Rectangle(bounds.Location, size);
+ System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
+
+ if (radius == 0)
+ {
+ path.AddRectangle(bounds);
+ return path;
+ }
+
+ // top left arc
+ path.AddArc(arc, 180, 90);
+
+ // top right arc
+ arc.X = bounds.Right - diameter;
+ path.AddArc(arc, 270, 90);
+
+ // bottom right arc
+ arc.Y = bounds.Bottom - diameter;
+ path.AddArc(arc, 0, 90);
+
+ // bottom left arc
+ arc.X = bounds.Left;
+ path.AddArc(arc, 90, 90);
+
+ path.CloseFigure();
+ return path;
+ }
+
+ public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (Empty)
+ {
+ return GH_ObjectResponse.Release;
+ }
+ if (expanded)
+ {
+ return _window.RespondToMouseUp(sender, e);
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (expanded)
+ {
+ return _window.RespondToMouseMove(sender, e);
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (Empty)
+ {
+ return GH_ObjectResponse.Handled;
+ }
+ if (expanded)
+ {
+ if (_window.Contains(e.CanvasLocation))
+ {
+ return _window.RespondToMouseDown(sender, e);
+ }
+ HideWindow(fire: false);
+ return GH_ObjectResponse.Release;
+ }
+ ShowWindow();
+ return GH_ObjectResponse.Capture;
+ }
+
+ public void ShowWindow()
+ {
+ if (!expanded)
+ {
+ expanded = true;
+ TopCollection.ActiveWidget = this;
+ Update();
+ }
+ }
+
+ public void HideWindow(bool fire)
+ {
+ if (expanded)
+ {
+ expanded = false;
+ TopCollection.ActiveWidget = null;
+ TopCollection.MakeAllInActive();
+ if (fire && this.ValueChanged != null)
+ {
+ this.ValueChanged(this, new EventArgs());
+ }
+ }
+ }
+
+ public override bool Contains(PointF pt)
+ {
+ return base.CanvasBounds.Contains(pt);
+ }
+
+ public override bool Write(GH_IWriter writer)
+ {
+ writer.CreateChunk("MenuDropDown", Index).SetInt32("ActiveItemIndex", current_value);
+ return true;
+ }
+
+ public override bool Read(GH_IReader reader)
+ {
+ GH_IReader gH_IReader = reader.FindChunk("MenuDropDown", Index);
+ try
+ {
+ current_value = gH_IReader.GetInt32("ActiveItemIndex");
+ }
+ catch
+ {
+ current_value = default_item_index;
+ }
+ return true;
+ }
+
+
+ ///
+ /// updates the refered variable to be same enum as the dropdown. it does NOT update the document
+ ///
+ /// double, int or enum
+ /// Can be any field hosting an enum
+ public MenuDropDown SetDefault(ref T reference) where T : IConvertible
+ {
+ if (typeof(T).IsEnum)
+ {
+ reference = (T)Enum.Parse(typeof(T), Item.name);
+ }
+ else if (typeof(T) == typeof(String))
+ {
+ reference = (T)(object)Item.name;
+ }
+
+ return this; // return allows to fluent interface
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/MenuPanel.cs b/SandWorm/CustomComponent/MenuPanel.cs
new file mode 100644
index 0000000..e4b296e
--- /dev/null
+++ b/SandWorm/CustomComponent/MenuPanel.cs
@@ -0,0 +1,314 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using GH_IO.Serialization;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+
+namespace SandWorm
+ {
+
+public class MenuPanel : GH_Attr_Widget
+{
+ private List _controls;
+
+ public GH_Capsule _menu;
+
+ public GH_Attr_Widget _activeControl;
+
+ public float LeftMargin
+ {
+ get;
+ set;
+ }
+
+ public float RightMargin
+ {
+ get;
+ set;
+ }
+
+ public float TopMargin
+ {
+ get;
+ set;
+ }
+
+ public float BottomMargin
+ {
+ get;
+ set;
+ }
+
+ public float LeftInnerMargin
+ {
+ get;
+ set;
+ }
+
+ public float RightInnerMargin
+ {
+ get;
+ set;
+ }
+
+ public float TopInnerMargin
+ {
+ get;
+ set;
+ }
+
+ public float BottomInnerMargin
+ {
+ get;
+ set;
+ }
+
+ public int PanelRadius
+ {
+ get;
+ set;
+ }
+
+ public int Space
+ {
+ get;
+ set;
+ }
+
+ public float EffectiveWidth => base.Width - ((float)(PanelRadius * 2) + LeftMargin + RightMargin + LeftInnerMargin + RightInnerMargin);
+
+ public float EffectiveHeight => base.Height - ((float)(PanelRadius * 2) + TopMargin + BottomMargin + TopInnerMargin + BottomInnerMargin);
+
+ public MenuPanel(int index, string id)
+ : base(index, id)
+ {
+ _controls = new List();
+ LeftMargin = 3f;
+ RightMargin = 3f;
+ TopMargin = 3f;
+ BottomMargin = 3f;
+ LeftInnerMargin = 1f;
+ RightInnerMargin = 1f;
+ TopInnerMargin = 1f;
+ BottomInnerMargin = 1f;
+ PanelRadius = 3;
+ Space = 5;
+ }
+
+ public void AddControl(GH_Attr_Widget _control)
+ {
+ _controls.Add(_control);
+ _control.Parent = this;
+ }
+
+ public override bool Write(GH_IWriter writer)
+ {
+ GH_IWriter writer2 = writer.CreateChunk("Panel", Index);
+ for (int i = 0; i < _controls.Count; i++)
+ {
+ _controls[i].Write(writer2);
+ }
+ return base.Write(writer);
+ }
+
+ public override bool Read(GH_IReader reader)
+ {
+ GH_IReader reader2 = reader.FindChunk("Panel", Index);
+ for (int i = 0; i < _controls.Count; i++)
+ {
+ _controls[i].Read(reader2);
+ }
+ return base.Read(reader);
+ }
+
+ public override SizeF ComputeMinSize()
+ {
+ float num = LeftMargin + RightMargin + (float)(PanelRadius * 2) + LeftInnerMargin + RightInnerMargin;
+ float num2 = TopMargin + BottomMargin + (float)(PanelRadius * 2) + TopInnerMargin + BottomInnerMargin;
+ float num3 = num;
+ float num4 = num2;
+ int num5 = 0;
+ foreach (GH_Attr_Widget control in _controls)
+ {
+ SizeF sizeF = control.ComputeMinSize();
+ num3 = Math.Max(sizeF.Width + num, num3);
+ if (num5++ > 0)
+ {
+ num4 += (float)Space;
+ }
+ num4 += sizeF.Height;
+ }
+ return new SizeF(num3, num4);
+ }
+
+ public override void Layout()
+ {
+ float num = base.CanvasPivot.Y + TopMargin + TopInnerMargin + (float)PanelRadius;
+ float y = base.CanvasPivot.Y;
+ int num2 = 0;
+ foreach (GH_Attr_Widget control in _controls)
+ {
+ if (num2++ > 0)
+ {
+ num += (float)Space;
+ }
+ PointF transform = new PointF(base.CanvasPivot.X + LeftMargin + LeftInnerMargin + (float)PanelRadius, num);
+ control.UpdateBounds(transform, EffectiveWidth);
+ //control.Style = base.Style;
+ //control.Palette = base.Palette;
+ control.Layout();
+ float height = control.Height;
+ num += height;
+ }
+ num += (float)PanelRadius + BottomMargin + BottomInnerMargin;
+ base.Height = num - y;
+ RectangleF rectangleF = GH_Attr_Widget.Shrink(base.CanvasBounds, LeftMargin, RightMargin, TopMargin, BottomMargin);
+ //_menu = GH_Capsule.CreateTextCapsule(rectangleF, rectangleF, base.Palette, "", new Font(new FontFamily("Arial"), 8f, FontStyle.Bold), GH_Orientation.horizontal_center, PanelRadius, 0);
+ }
+
+ public override void Render(WidgetRenderArgs args)
+ {
+ GH_Canvas canvas = args.Canvas;
+ float zoom = canvas.Viewport.Zoom;
+ int num = 255;
+ if (zoom < 1f)
+ {
+ float num2 = (zoom - 0.5f) * 2f;
+ num = (int)((float)num * num2);
+ }
+ if (num < 0)
+ {
+ num = 0;
+ }
+ num = GH_Canvas.ZoomFadeLow;
+ int r = base.Style.Fill.R;
+ int g = base.Style.Fill.G;
+ int b = base.Style.Fill.B;
+ int red = 80;
+ int green = 80;
+ int blue = 80;
+ GH_PaletteStyle style = new GH_PaletteStyle(Color.FromArgb(num, r, g, b), Color.FromArgb(num, red, green, blue));
+ //_menu.Render(canvas.Graphics, style);
+ for (int i = 0; i < _controls.Count; i++)
+ {
+ _controls[i].OnRender(args);
+ }
+ }
+
+ public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (_activeControl != null)
+ {
+ GH_ObjectResponse gH_ObjectResponse = _activeControl.RespondToMouseUp(sender, e);
+ switch (gH_ObjectResponse)
+ {
+ case GH_ObjectResponse.Release:
+ _activeControl = null;
+ return gH_ObjectResponse;
+ default:
+ return gH_ObjectResponse;
+ case GH_ObjectResponse.Ignore:
+ break;
+ }
+ _activeControl = null;
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (base.CanvasBounds.Contains(e.CanvasLocation))
+ {
+ foreach (GH_Attr_Widget control in _controls)
+ {
+ if (control.Contains(e.CanvasLocation) && control.Enabled)
+ {
+ GH_ObjectResponse gH_ObjectResponse = control.RespondToMouseDown(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ _activeControl = control;
+ return gH_ObjectResponse;
+ }
+ }
+ }
+ }
+ else if (_activeControl != null)
+ {
+ _activeControl.RespondToMouseDown(sender, e);
+ _activeControl = null;
+ return GH_ObjectResponse.Handled;
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (_activeControl != null)
+ {
+ return _activeControl.RespondToMouseMove(sender, e);
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_ObjectResponse RespondToMouseDoubleClick(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (base.CanvasBounds.Contains(e.CanvasLocation))
+ {
+ int count = _controls.Count;
+ for (int i = 0; i < count; i++)
+ {
+ if (_controls[i].Contains(e.CanvasLocation) && _controls[i].Enabled)
+ {
+ return _controls[i].RespondToMouseDoubleClick(sender, e);
+ }
+ }
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_Attr_Widget IsTtipPoint(PointF pt)
+ {
+ if (base.CanvasBounds.Contains(pt))
+ {
+ int count = _controls.Count;
+ for (int i = 0; i < count; i++)
+ {
+ GH_Attr_Widget gH_Attr_Widget = _controls[i].IsTtipPoint(pt);
+ if (gH_Attr_Widget != null)
+ {
+ return gH_Attr_Widget;
+ }
+ }
+ if (_showToolTip)
+ {
+ return this;
+ }
+ }
+ return null;
+ }
+
+ public override void TooltipSetup(PointF canvasPoint, GH_TooltipDisplayEventArgs e)
+ {
+ e.Icon = null;
+ e.Title = _name + " (Group)";
+ e.Text = _header;
+ e.Description = _description;
+ }
+
+ public override bool Contains(PointF pt)
+ {
+ return base.CanvasBounds.Contains(pt);
+ }
+
+ public override string GetWidgetDescription()
+ {
+ string str = base.GetWidgetDescription() + "{\n";
+ foreach (GH_Attr_Widget control in _controls)
+ {
+ str = str + control.GetWidgetDescription() + "\n";
+ }
+ return str + "}";
+ }
+}
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/MenuRadioButton.cs b/SandWorm/CustomComponent/MenuRadioButton.cs
new file mode 100644
index 0000000..3321f40
--- /dev/null
+++ b/SandWorm/CustomComponent/MenuRadioButton.cs
@@ -0,0 +1,246 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using GH_IO.Serialization;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+
+namespace SandWorm
+{
+public class MenuRadioButton : GH_Attr_Widget
+{
+ public enum Alignment
+ {
+ Vertical,
+ Horizontal
+ }
+
+ private bool _active;
+
+ private Size _radioSize;
+
+ private Rectangle _radioBounds;
+
+ private float _padding = 4f;
+
+ public Alignment Align
+ {
+ get;
+ set;
+ }
+
+ public string Tag
+ {
+ get;
+ set;
+ }
+
+ public bool RenderTag
+ {
+ get;
+ set;
+ }
+
+ public bool Active
+ {
+ get
+ {
+ return _active;
+ }
+ set
+ {
+ _active = value;
+ }
+ }
+
+ public event ValueChangeEventHandler ValueChanged;
+
+ public MenuRadioButton(int index, string id, string tag, Alignment align)
+ : base(index, id)
+ {
+ _active = false;
+ _padding = WidgetServer.Instance.RadioButtonPadding;
+ _radioSize = WidgetServer.Instance.RadioButtonSize;
+ Tag = tag;
+ RenderTag = true;
+ Align = align;
+ }
+
+ public override bool Write(GH_IWriter writer)
+ {
+ writer.CreateChunk("RadioButton", Index).SetBoolean("Active", _active);
+ return true;
+ }
+
+ public override bool Read(GH_IReader reader)
+ {
+ GH_IReader gH_IReader = reader.FindChunk("RadioButton", Index);
+ _active = gH_IReader.GetBoolean("Active");
+ return true;
+ }
+
+ public override SizeF ComputeMinSize()
+ {
+ int num = _radioSize.Width;
+ int num2 = _radioSize.Height;
+ if (Align == Alignment.Horizontal)
+ {
+ if (RenderTag && Tag != null)
+ {
+ Size size = TextRenderer.MeasureText(Tag, WidgetServer.Instance.TextFont);
+ num += size.Width + (int)_padding;
+ num2 = Math.Max(size.Height, num2);
+ }
+ }
+ else
+ {
+ if (Align != 0)
+ {
+ throw new ArgumentException("invalid alignment(" + Align.ToString() + ")");
+ }
+ if (RenderTag && Tag != null)
+ {
+ Size size2 = TextRenderer.MeasureText(Tag, WidgetServer.Instance.TextFont);
+ num = Math.Max(size2.Width, num);
+ num2 += size2.Height + (int)_padding;
+ }
+ }
+ return new SizeF(num, num2);
+ }
+
+ public override void PostUpdateBounds(out float outHeight)
+ {
+ outHeight = ComputeMinSize().Height;
+ }
+
+ public override void Layout()
+ {
+ if (Align == Alignment.Horizontal)
+ {
+ float num = base.CanvasPivot.Y + (base.Height - (float)_radioSize.Height) / 2f;
+ _radioBounds = new Rectangle((int)(base.CanvasPivot.X + base.CanvasBounds.Width - (float)_radioSize.Width), (int)num, _radioSize.Width, _radioSize.Height);
+ }
+ else
+ {
+ float num2 = base.CanvasPivot.X + (base.Width - (float)_radioSize.Width) / 2f;
+ _radioBounds = new Rectangle((int)num2, (int)(base.CanvasPivot.Y + base.CanvasBounds.Height - (float)_radioSize.Height), _radioSize.Width, _radioSize.Height);
+ }
+ }
+
+ public override void Render(WidgetRenderArgs args)
+ {
+ GH_Canvas canvas = args.Canvas;
+ float zoom = canvas.Viewport.Zoom;
+ int num = 255;
+ if (zoom < 1f)
+ {
+ float num2 = (zoom - 0.5f) * 2f;
+ num = (int)((float)num * num2);
+ }
+ if (num < 0)
+ {
+ num = 0;
+ }
+ num = GH_Canvas.ZoomFadeLow;
+ SolidBrush solidBrush = null;
+ SolidBrush solidBrush2 = null;
+ Pen pen = null;
+ if (_enabled)
+ {
+ solidBrush = new SolidBrush(Color.FromArgb(num, 0, 0, 0));
+ solidBrush2 = new SolidBrush(Color.FromArgb(num, 255, 255, 255));
+ }
+ else
+ {
+ solidBrush = new SolidBrush(Color.FromArgb(num, 50, 50, 50));
+ solidBrush2 = new SolidBrush(Color.FromArgb(num, 150, 150, 150));
+ }
+ pen = new Pen(solidBrush, 1.5f);
+ Graphics graphics = canvas.Graphics;
+ if (RenderTag && Tag != null)
+ {
+ if (Align == Alignment.Horizontal)
+ {
+ PointF point = new PointF(base.CanvasPivot.X, base.CanvasPivot.Y);
+ graphics.DrawString(Tag, WidgetServer.Instance.TextFont, solidBrush, point);
+ }
+ else
+ {
+ StringFormat stringFormat = new StringFormat
+ {
+ Alignment = StringAlignment.Center
+ };
+ PointF point2 = new PointF(base.CanvasPivot.X + base.Width / 2f, base.CanvasPivot.Y);
+ graphics.DrawString(Tag, WidgetServer.Instance.TextFont, solidBrush, point2, stringFormat);
+ }
+ }
+ if (!_active)
+ {
+ graphics.FillEllipse(solidBrush2, _radioBounds);
+ graphics.DrawEllipse(pen, _radioBounds);
+ return;
+ }
+ int num3 = (int)((double)_padding / 2.0);
+ RectangleF rect = new RectangleF(_radioBounds.X + num3, _radioBounds.Y + num3, _radioBounds.Width - num3 * 2, _radioBounds.Height - num3 * 2);
+ graphics.FillEllipse(solidBrush2, _radioBounds);
+ graphics.FillEllipse(solidBrush, rect);
+ graphics.DrawEllipse(pen, _radioBounds);
+ }
+
+ public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (base.CanvasBounds.Contains(e.CanvasLocation))
+ {
+ _active = !_active;
+ }
+ if (this.ValueChanged != null)
+ {
+ this.ValueChanged(this, new EventArgs());
+ }
+ return GH_ObjectResponse.Release;
+ }
+
+ public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (_radioBounds.Contains((int)e.CanvasX, (int)e.CanvasY))
+ {
+ return GH_ObjectResponse.Handled;
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Capture;
+ }
+
+ public override bool Contains(PointF pt)
+ {
+ return base.CanvasBounds.Contains(pt);
+ }
+
+ public override GH_Attr_Widget IsTtipPoint(PointF pt)
+ {
+ if (base.CanvasBounds.Contains(pt))
+ {
+ return this;
+ }
+ return null;
+ }
+
+ public override void TooltipSetup(PointF canvasPoint, GH_TooltipDisplayEventArgs e)
+ {
+ e.Icon = null;
+ e.Title = _name + " (RadioButton)";
+ e.Text = _header;
+ if (_active)
+ {
+ e.Description = "ON";
+ }
+ else
+ {
+ e.Description = "OFF";
+ }
+ }
+}
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/MenuRadioButtonGroup.cs b/SandWorm/CustomComponent/MenuRadioButtonGroup.cs
new file mode 100644
index 0000000..84a5fb7
--- /dev/null
+++ b/SandWorm/CustomComponent/MenuRadioButtonGroup.cs
@@ -0,0 +1,461 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using GH_IO.Serialization;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+
+namespace SandWorm
+{
+
+ public class MenuRadioButtonGroup : GH_Attr_Widget
+ {
+ public enum LayoutDirection
+ {
+ Vertical,
+ Horizontal
+ }
+
+ private List _buttons;
+
+ private int minActive = 1;
+
+ private int maxActive = 1;
+
+ private List actives;
+
+ private MenuRadioButton _activeControl;
+
+ private float _space;
+
+ public LayoutDirection Direction
+ {
+ get;
+ set;
+ }
+
+ public override bool Enabled
+ {
+ get
+ {
+ return _enabled;
+ }
+ set
+ {
+ _enabled = value;
+ int count = _buttons.Count;
+ for (int i = 0; i < count; i++)
+ {
+ _buttons[i].Enabled = _enabled;
+ }
+ }
+ }
+
+ public int MaxActive
+ {
+ get
+ {
+ return maxActive;
+ }
+ set
+ {
+ if (value < 1)
+ {
+ maxActive = 1;
+ }
+ else
+ {
+ maxActive = value;
+ }
+ UpdateSettings();
+ }
+ }
+
+ public int MinActive
+ {
+ get
+ {
+ return minActive;
+ }
+ set
+ {
+ if (value < 0)
+ {
+ minActive = 0;
+ }
+ else
+ {
+ minActive = value;
+ }
+ UpdateSettings();
+ }
+ }
+
+ public event ValueChangeEventHandler ValueChanged;
+
+ public MenuRadioButtonGroup(int index, string id)
+ : base(index, id)
+ {
+ _buttons = new List();
+ actives = new List();
+ Direction = LayoutDirection.Vertical;
+ _space = 5f;
+ }
+ public MenuRadioButtonGroup(MenuStaticText inputText, int index, string id ="")
+ : base(index, id)
+ {
+ _buttons = new List();
+ actives = new List();
+ Direction = LayoutDirection.Vertical;
+ _space = 5f;
+ Header = inputText.Header;
+ Name = inputText.Text;
+ }
+
+ public override SizeF ComputeMinSize()
+ {
+ if (Direction == LayoutDirection.Vertical)
+ {
+ float num = 0f;
+ float num2 = 0f;
+ int num3 = 0;
+ foreach (MenuRadioButton button in _buttons)
+ {
+ if (num3++ > 0)
+ {
+ num2 += _space;
+ }
+ SizeF sizeF = button.ComputeMinSize();
+ num = Math.Max(sizeF.Width, num);
+ num2 += sizeF.Height;
+ }
+ return new SizeF(num, num2);
+ }
+ if (Direction == LayoutDirection.Horizontal)
+ {
+ float num4 = 0f;
+ float num5 = 0f;
+ foreach (MenuRadioButton button2 in _buttons)
+ {
+ SizeF sizeF2 = button2.ComputeMinSize();
+ num4 = Math.Max(sizeF2.Width, num4);
+ num5 = Math.Max(sizeF2.Height, num5);
+ }
+ return new SizeF(num4 * (float)_buttons.Count + _space * (float)_buttons.Count, num5);
+ }
+ throw new NotImplementedException("todo");
+ }
+
+ public override void Layout()
+ {
+ if (Direction == LayoutDirection.Vertical)
+ {
+ float num = base.CanvasPivot.Y;
+ float num2 = num;
+ int num3 = 0;
+ foreach (MenuRadioButton button in _buttons)
+ {
+ if (num3++ > 0)
+ {
+ num += _space;
+ }
+ PointF transform = new PointF(base.CanvasPivot.X, num);
+ button.UpdateBounds(transform, base.Width);
+ button.Style = base.Style;
+ button.Palette = base.Palette;
+ button.Layout();
+ num += button.Height;
+ }
+ base.Height = num - num2;
+ return;
+ }
+ if (Direction == LayoutDirection.Horizontal)
+ {
+ float num4 = base.CanvasPivot.X + _space / 2f;
+ int num5 = 0;
+ float num6 = 0f;
+ if (_buttons.Count > 0)
+ {
+ num6 = base.Width / (float)_buttons.Count - _space - 20f;
+ }
+ float num7 = 0f;
+ foreach (MenuRadioButton button2 in _buttons)
+ {
+ if (num5++ > 0)
+ {
+ num4 += _space + 20f;
+ }
+ PointF transform2 = new PointF(num4, base.CanvasPivot.Y - 3f);
+ button2.UpdateBounds(transform2, num6);
+ button2.Style = base.Style;
+ button2.Palette = base.Palette;
+ button2.Layout();
+ num4 += num6;
+ num7 = Math.Max(button2.Height, num7);
+ }
+ base.Height = num7;
+ return;
+ }
+ throw new NotImplementedException("todo");
+ }
+
+ public void AddButton(MenuRadioButton button)
+ {
+ if (button.Active && actives.Count < maxActive)
+ {
+ actives.Add(button);
+ }
+ button.Parent = this;
+ button.Enabled = _enabled;
+ _buttons.Add(button);
+ }
+
+ public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ int count = _buttons.Count;
+ for (int i = 0; i < count; i++)
+ {
+ if (!_buttons[i].Contains(e.CanvasLocation) || _buttons[i] != _activeControl)
+ {
+ continue;
+ }
+ if (_buttons[i].Active)
+ {
+ if (actives.Count - 1 >= minActive)
+ {
+ _buttons[i].Active = false;
+ actives.Remove(_buttons[i]);
+ }
+ }
+ else
+ {
+ if (actives.Count == maxActive)
+ {
+ actives[0].Active = false;
+ actives.RemoveAt(0);
+ }
+ _buttons[i].Active = true;
+ actives.Add(_buttons[i]);
+ }
+ if (this.ValueChanged != null)
+ {
+ this.ValueChanged(this, new EventArgs());
+ }
+ Update();
+ }
+ return GH_ObjectResponse.Release;
+ }
+
+ public override bool Write(GH_IWriter writer)
+ {
+ GH_IWriter gH_IWriter = writer.CreateChunk("RadioButtonGroup", Index);
+ int count = _buttons.Count;
+ gH_IWriter.SetInt32("Count", count);
+ GH_IWriter gH_IWriter2 = gH_IWriter.CreateChunk("Active");
+ for (int i = 0; i < count; i++)
+ {
+ gH_IWriter2.SetBoolean("button", i, _buttons[i].Active);
+ }
+ return true;
+ }
+
+ public override bool Read(GH_IReader reader)
+ {
+ string text = "RadioButtonGroup";
+ GH_IReader gH_IReader = reader.FindChunk(text, Index);
+ if (gH_IReader == null)
+ {
+ List chunks = reader.Chunks;
+ GH_IChunk gH_IChunk = null;
+ foreach (GH_IChunk chunk in reader.Chunks)
+ {
+ if (chunk.Name.Equals(text))
+ {
+ gH_IChunk = chunk;
+ break;
+ }
+ }
+ if (gH_IChunk == null)
+ {
+ throw new ArgumentException("fail");
+ }
+ if (chunks.Count != 1)
+ {
+ throw new ArgumentException("RadioButtonGroup could not be loaded");
+ }
+ gH_IReader = (GH_Chunk)gH_IChunk;
+ }
+ int count = _buttons.Count;
+ int @int = gH_IReader.GetInt32("Count");
+ GH_IReader gH_IReader2 = gH_IReader.FindChunk("Active");
+ actives.Clear();
+ for (int i = 0; i < count; i++)
+ {
+ bool boolean = gH_IReader2.GetBoolean("button", i);
+ _buttons[i].Active = boolean;
+ if (boolean)
+ {
+ actives.Add(_buttons[i]);
+ }
+ }
+ return true;
+ }
+
+ public override bool Contains(PointF pt)
+ {
+ int count = _buttons.Count;
+ for (int i = 0; i < count; i++)
+ {
+ if (_buttons[i].Contains(pt))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ int count = _buttons.Count;
+ for (int i = 0; i < count; i++)
+ {
+ if (_buttons[i].Contains(e.CanvasLocation))
+ {
+ GH_ObjectResponse gH_ObjectResponse = _buttons[i].RespondToMouseDown(sender, e);
+ if (gH_ObjectResponse != 0)
+ {
+ _activeControl = _buttons[i];
+ return gH_ObjectResponse;
+ }
+ }
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Capture;
+ }
+
+ public override GH_Attr_Widget IsTtipPoint(PointF pt)
+ {
+ int count = _buttons.Count;
+ for (int i = 0; i < count; i++)
+ {
+ GH_Attr_Widget gH_Attr_Widget = _buttons[i].IsTtipPoint(pt);
+ if (gH_Attr_Widget != null)
+ {
+ return gH_Attr_Widget;
+ }
+ }
+ return null;
+ }
+
+ public override void Render(WidgetRenderArgs args)
+ {
+ int count = _buttons.Count;
+ for (int i = 0; i < count; i++)
+ {
+ _buttons[i].Render(args);
+ }
+ }
+
+ public List GetActive()
+ {
+ return actives;
+ }
+
+ public List GetPattern()
+ {
+ List list = new List();
+ int count = _buttons.Count;
+ for (int i = 0; i < count; i++)
+ {
+ list.Add(_buttons[i].Active);
+ }
+ return list;
+ }
+
+ public List GetActiveInt()
+ {
+ List list = new List();
+ int count = _buttons.Count;
+ for (int i = 0; i < count; i++)
+ {
+ if (_buttons[i].Active)
+ {
+ list.Add(i);
+ }
+ }
+ return list;
+ }
+
+ public bool SetActive(int index)
+ {
+ if (index < _buttons.Count)
+ {
+ if (!_buttons[index].Active)
+ {
+ actives.Add(_buttons[index]);
+ _buttons[index].Active = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void UpdateSettings()
+ {
+ if (minActive > maxActive)
+ {
+ minActive = maxActive;
+ }
+ }
+
+ private void Update()
+ {
+ _ = _buttons.Count;
+ int count = actives.Count;
+ if (count > maxActive)
+ {
+ int num = count - maxActive;
+ for (int i = 0; i < num; i++)
+ {
+ MenuRadioButton menuRadioButton = actives[0];
+ actives.RemoveAt(0);
+ menuRadioButton.Active = false;
+ }
+ }
+ else
+ {
+ if (count >= minActive)
+ {
+ return;
+ }
+ int num2 = count - maxActive;
+ int num3 = 0;
+ for (int j = 0; j < num2; j++)
+ {
+ if (!actives.Contains(_buttons[num3]))
+ {
+ MenuRadioButton menuRadioButton2 = _buttons[num3];
+ menuRadioButton2.Active = true;
+ actives.Add(menuRadioButton2);
+ j--;
+ }
+ num3++;
+ }
+ }
+ }
+
+ public override string GetWidgetDescription()
+ {
+ string str = base.GetWidgetDescription() + "{\n";
+ foreach (MenuRadioButton button in _buttons)
+ {
+ str = str + button.GetWidgetDescription() + "\n";
+ }
+ return str + "}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/MenuSlider.cs b/SandWorm/CustomComponent/MenuSlider.cs
new file mode 100644
index 0000000..9f46781
--- /dev/null
+++ b/SandWorm/CustomComponent/MenuSlider.cs
@@ -0,0 +1,446 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using GH_IO.Serialization;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+
+namespace SandWorm
+{
+
+ public class MenuSlider : GH_Attr_Widget
+ {
+ private double _step;
+
+ private float _sliderWidth;
+
+ private double currentValue;
+
+ private double maxValue = 1.0;
+
+ private double minValue;
+
+ private int numDecimals = 2;
+
+ private System.Drawing.RectangleF _handleArea;
+
+ private System.Drawing.RectangleF _textBounds;
+
+ private float _handleDiameter = 8f;
+
+ private string _number_format = "{0:0.00}";
+
+
+ ///
+ /// Name is title of the tooltip
+ ///
+ public override string Name
+ {
+ get
+ {
+ return _name;
+ }
+ set
+ {
+ _name = value;
+ }
+ }
+
+
+
+ public string NumberFormat
+ {
+ get
+ {
+ return _number_format;
+ }
+ set
+ {
+ _number_format = value;
+ }
+ }
+
+ public double Step
+ {
+ get
+ {
+ return _step;
+ }
+ set
+ {
+ _step = value;
+ if (_step < 0.0)
+ {
+ _step = 0.0;
+ }
+ }
+ }
+
+ public int NumDecimals
+ {
+ get
+ {
+ return numDecimals;
+ }
+ set
+ {
+ numDecimals = Math.Min(0, Math.Max(12, value));
+ }
+ }
+
+ public double Value
+ {
+ get
+ {
+ return currentValue;
+ }
+ set
+ {
+ if (value > maxValue)
+ {
+ currentValue = maxValue;
+ }
+ else if (value < minValue)
+ {
+ currentValue = minValue;
+ }
+ else
+ {
+ currentValue = value;
+ }
+ }
+ }
+
+ public double MaxValue
+ {
+ get
+ {
+ return maxValue;
+ }
+ set
+ {
+ maxValue = value;
+ if (currentValue > maxValue)
+ {
+ currentValue = maxValue;
+ }
+ }
+ }
+
+ public double MinValue
+ {
+ get
+ {
+ return minValue;
+ }
+ set
+ {
+ minValue = value;
+ if (currentValue < minValue)
+ {
+ currentValue = minValue;
+ }
+ }
+ }
+
+ public event ValueChangeEventHandler ValueChanged;
+
+ public MenuSlider(int index, string id, double min, double max, double value, int numDecimals)
+ : base(index, id)
+ {
+ minValue = min;
+ maxValue = max;
+ currentValue = value;
+ this.numDecimals = numDecimals;
+ _step = 0.0;
+ FixValues();
+ }
+
+ public MenuSlider(MenuStaticText textInput, int index, double min, double max, double value, int numDecimals, string id = "")
+ : base(index, id)
+ {
+
+ minValue = min;
+ maxValue = max;
+ currentValue = value;
+ this.numDecimals = numDecimals;
+ _step = 0.0;
+ FixValues();
+
+ Name = textInput.Text;
+ Header = textInput.Header;
+ }
+
+
+
+ private void FixValues()
+ {
+ numDecimals = Math.Max(0, Math.Min(12, numDecimals));
+ minValue = TruncateValue(minValue, numDecimals);
+ maxValue = TruncateValue(maxValue, numDecimals);
+ currentValue = TruncateValue(currentValue, numDecimals);
+ if (minValue > maxValue)
+ {
+ minValue = maxValue;
+ }
+ if (currentValue < minValue)
+ {
+ currentValue = minValue;
+ }
+ if (currentValue > maxValue)
+ {
+ currentValue = maxValue;
+ }
+ if (_step < 0.0)
+ {
+ _step = 0.0;
+ }
+ }
+
+ private static double TruncateValue(double value, int numDecimals)
+ {
+ if (numDecimals < 0)
+ {
+ numDecimals = 0;
+ }
+ decimal d = (decimal)value;
+ decimal d2 = (decimal)Math.Pow(10.0, numDecimals);
+ return (double)(Math.Truncate(d * d2) / d2);
+ }
+
+ public override bool Write(GH_IWriter writer)
+ {
+ GH_IWriter gH_IWriter = writer.CreateChunk("Slider", Index);
+ gH_IWriter.SetDouble("MinValue", minValue);
+ gH_IWriter.SetDouble("MaxValue", maxValue);
+ gH_IWriter.SetDouble("CurrentValue", currentValue);
+ gH_IWriter.SetInt32("NumDecimals", numDecimals);
+ return true;
+ }
+
+ public override bool Read(GH_IReader reader)
+ {
+ GH_IReader gH_IReader = reader.FindChunk("Slider", Index);
+ if (gH_IReader != null)
+ {
+ minValue = gH_IReader.GetDouble("MinValue");
+ maxValue = gH_IReader.GetDouble("MaxValue");
+ currentValue = gH_IReader.GetDouble("CurrentValue");
+
+ if (!gH_IReader.TryGetInt32("NumDecimals", ref numDecimals))
+ {
+ numDecimals = 2;
+ }
+ }
+
+ FixValues();
+ return true;
+ }
+
+ public override System.Drawing.SizeF ComputeMinSize()
+ {
+ float height = Math.Max(TextRenderer.MeasureText("1", WidgetServer.Instance.SliderValueTagFont).Height, _handleDiameter);
+ return new System.Drawing.SizeF(_handleDiameter + 20f, height);
+ }
+
+ public override void Layout()
+ {
+ double num = 0.0;
+ double num2 = maxValue - minValue;
+ if (_step > 0.001)
+ {
+ currentValue = (double)(int)((currentValue + _step / 2.0) / _step) * _step;
+ }
+ num = ((!(num2 < 1E-10)) ? ((currentValue - minValue) / num2) : 0.5);
+ currentValue = TruncateValue(currentValue, numDecimals);
+ float num3 = (float)((double)_sliderWidth * num);
+ _handleArea = new System.Drawing.RectangleF(base.CanvasPivot.X + num3, base.CanvasPivot.Y + base.Height / 2f - _handleDiameter / 2f, _handleDiameter, _handleDiameter);
+ if (Value < (MaxValue + MinValue) / 2.0)
+ {
+ _textBounds = new System.Drawing.RectangleF(_handleArea.Right + 3f, base.CanvasPivot.Y, canvasBounds.Right - (_handleArea.Right + 3f), base.Height);
+ }
+ else
+ {
+ _textBounds = new System.Drawing.RectangleF(base.CanvasPivot.X, base.CanvasPivot.Y, _handleArea.Left - base.CanvasPivot.X - 3f, base.Height);
+ }
+ }
+
+ public override void Render(WidgetRenderArgs args)
+ {
+ GH_Canvas canvas = args.Canvas;
+ System.Drawing.Graphics graphics = canvas.Graphics;
+ float zoom = canvas.Viewport.Zoom;
+ int num = 255;
+ if (zoom < 1f)
+ {
+ float num2 = (zoom - 0.5f) * 2f;
+ num = (int)((float)num * num2);
+ }
+ if (num < 0)
+ {
+ num = 0;
+ }
+ num = GH_Canvas.ZoomFadeLow;
+ int alpha = (int)((double)num * 0.2);
+ System.Drawing.Pen pen = null;
+ System.Drawing.Pen pen2 = null;
+ System.Drawing.SolidBrush solidBrush = null;
+ System.Drawing.SolidBrush solidBrush2 = null;
+ if (_enabled)
+ {
+ pen = new System.Drawing.Pen(System.Drawing.Color.FromArgb(alpha, 115, 115, 115), 1f);
+ pen2 = new System.Drawing.Pen(System.Drawing.Color.FromArgb(num, 45, 45, 45), 2f);
+ solidBrush = new System.Drawing.SolidBrush(System.Drawing.Color.FromArgb(num, 255, 255, 255));
+ solidBrush2 = new System.Drawing.SolidBrush(System.Drawing.Color.FromArgb(45, 45, 45));
+ }
+ else
+ {
+ pen = new System.Drawing.Pen(System.Drawing.Color.FromArgb(alpha, 50, 50, 50), 1f);
+ pen2 = new System.Drawing.Pen(System.Drawing.Color.FromArgb(num, 50, 50, 50), 2f);
+ solidBrush = new System.Drawing.SolidBrush(System.Drawing.Color.FromArgb(num, 150, 150, 150));
+ solidBrush2 = new System.Drawing.SolidBrush(System.Drawing.Color.FromArgb(num, 50, 50, 50));
+ }
+ graphics.DrawLine(pen, base.CanvasPivot.X + _handleDiameter / 2f, base.CanvasPivot.Y + base.Height / 2f, base.CanvasPivot.X + base.Width - _handleDiameter / 2f, base.CanvasPivot.Y + base.Height / 2f);
+ graphics.DrawLine(pen, base.CanvasPivot.X + _handleDiameter / 2f, base.CanvasPivot.Y + base.Height / 2f - 3f, base.CanvasPivot.X + _handleDiameter / 2f, base.CanvasPivot.Y + base.Height / 2f + 3f);
+ graphics.DrawLine(pen, base.CanvasPivot.X + base.Width - _handleDiameter / 2f, base.CanvasPivot.Y + base.Height / 2f - 3f, base.CanvasPivot.X + base.Width - _handleDiameter / 2f, base.CanvasPivot.Y + base.Height / 2f + 3f);
+ graphics.FillEllipse(solidBrush, _handleArea);
+ graphics.DrawEllipse(pen2, _handleArea);
+ StringFormat stringFormat = new StringFormat
+ {
+ Trimming = StringTrimming.EllipsisCharacter,
+ LineAlignment = StringAlignment.Near
+ };
+ if (Value < (MaxValue + MinValue) / 2.0)
+ {
+ stringFormat.Alignment = StringAlignment.Near;
+ }
+ else
+ {
+ stringFormat.Alignment = StringAlignment.Far;
+ }
+ string s = currentValue.ToString();
+ graphics.DrawString(s, WidgetServer.Instance.SliderValueTagFont, solidBrush2, _textBounds, stringFormat);
+ }
+
+ public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Release;
+ }
+
+ public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (e.Button == MouseButtons.Left)
+ return GH_ObjectResponse.Handled;
+ else
+ return GH_ObjectResponse.Ignore;
+
+ }
+
+ public override GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ if (e.Button != MouseButtons.Left)
+ return GH_ObjectResponse.Capture;
+ System.Drawing.PointF canvasPivot = base.CanvasPivot;
+ if (e.CanvasLocation.X < canvasPivot.X + 5f)
+ {
+ currentValue = minValue;
+ }
+ else if (e.CanvasLocation.X > canvasPivot.X + 5f + _sliderWidth)
+ {
+ currentValue = maxValue;
+ }
+ else
+ {
+ double num = (double)(e.CanvasLocation.X - (canvasPivot.X + 5f)) / (double)_sliderWidth;
+ double num2 = (maxValue - minValue) * num;
+ currentValue = num2 + minValue;
+ }
+ Layout();
+ FireChangedEvent();
+ return GH_ObjectResponse.Capture;
+ }
+
+ public void FireChangedEvent()
+ {
+ if (this.ValueChanged != null)
+ {
+ this.ValueChanged(this, new EventArgs());
+ }
+ }
+
+ public override GH_ObjectResponse RespondToMouseDoubleClick(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ try
+ {
+ float scalingFactor = WidgetServer.GetScalingFactor();
+ SilderDialog sliderDialog = new SilderDialog(this);
+ System.Drawing.Rectangle bounds = System.Windows.Forms.Screen.FromControl(sender).Bounds;
+ System.Drawing.Point point = new System.Drawing.Point((int)((float)(bounds.Width / 2) / scalingFactor), (int)((float)(bounds.Height / 2) / scalingFactor));
+ sliderDialog.Location = new Eto.Drawing.Point(point.X - 200, point.Y - 200);
+ SliderDialogResult sliderDialogResult = sliderDialog.ShowModal();
+ if (sliderDialogResult.Status == Eto.Forms.DialogResult.Ok)
+ {
+ minValue = sliderDialogResult.MinValue;
+ maxValue = sliderDialogResult.MaxValue;
+ currentValue = sliderDialogResult.CurrentValue;
+ numDecimals = sliderDialogResult.NumDecimals;
+ FireChangedEvent();
+ }
+ }
+ catch (Exception)
+ {
+ }
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override void PostUpdateBounds(out float outHeight)
+ {
+ _sliderWidth = base.Width - 10f;
+ outHeight = ComputeMinSize().Height;
+ }
+
+ public override GH_Attr_Widget IsTtipPoint(System.Drawing.PointF pt)
+ {
+ if (new System.Drawing.RectangleF(transfromation.X, transfromation.Y, base.Width, 10f).Contains(pt))
+ {
+ return this;
+ }
+ return null;
+ }
+
+ public override void TooltipSetup(System.Drawing.PointF canvasPoint, GH_TooltipDisplayEventArgs e)
+ {
+ e.Icon = null;
+ e.Title = _name + " (Slider)";
+ e.Text = _header + "\n\nDouble-click on the knob to set the value range.";
+ e.Description = "Value: " + currentValue + "\nMinValue: " + minValue + "\nMaxValue: " + maxValue;
+ }
+
+ public override bool Contains(System.Drawing.PointF pt)
+ {
+ if (_handleArea.Contains(pt))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Sets the ref variable to the value of the slider. It does NOT update the document.
+ ///
+ /// double, int or enum
+ /// Can be any object that can be converted to double or int
+ public MenuSlider SetDefault(ref T reference) where T : struct, IConvertible
+ {
+ if (typeof(T).IsAssignableFrom(typeof(double)))
+ {
+ reference = (T)(object)Value;
+ }
+ else if (typeof(T).IsAssignableFrom(typeof(int)))
+ {
+ reference = (T)(object)System.Convert.ToInt32(Math.Round(Value));
+ }
+ return this; // returns allows fluent interface
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/MenuStaticText.cs b/SandWorm/CustomComponent/MenuStaticText.cs
new file mode 100644
index 0000000..19a5575
--- /dev/null
+++ b/SandWorm/CustomComponent/MenuStaticText.cs
@@ -0,0 +1,131 @@
+using System.Drawing;
+using System.Windows.Forms;
+using GH_IO.Serialization;
+using Grasshopper.GUI;
+using Grasshopper.GUI.Canvas;
+
+namespace SandWorm
+{
+ public class MenuStaticText : GH_Attr_Widget
+ {
+ private string _text;
+
+
+
+ ///
+ /// The text shown as a label
+ ///
+ public string Text
+ {
+ get
+ {
+ return _text;
+ }
+ set
+ {
+ _text = value;
+ }
+ }
+
+
+ public MenuStaticText()
+ : base(0, "")
+ {
+ }
+
+ public MenuStaticText(string label, string tooltip = "")
+ : base(0, "")
+ {
+ Text = label;
+ Header = tooltip;
+ }
+
+ public override bool Write(GH_IWriter writer)
+ {
+ return true;
+ }
+
+
+
+ // This allowed us to get the static text tooltip :-)
+ public override GH_Attr_Widget IsTtipPoint(System.Drawing.PointF pt)
+ {
+ if (new System.Drawing.RectangleF(transfromation.X, transfromation.Y, base.Width, base.Height).Contains(pt))
+ {
+ return this;
+
+ }
+ return null;
+ }
+
+
+
+ public override void PostUpdateBounds(out float outHeight)
+ {
+ outHeight = ComputeMinSize().Height;
+ }
+
+ public override SizeF ComputeMinSize()
+ {
+ if (Text == null)
+ {
+ return default;
+ }
+ Size size = TextRenderer.MeasureText(Text, WidgetServer.Instance.TextFont);
+ return new SizeF(size.Width, size.Height);
+ }
+
+
+ public override void TooltipSetup(PointF canvasPoint, GH_TooltipDisplayEventArgs e)
+ {
+ e.Icon = null;
+ e.Title = Text.Replace("\n", "");
+ e.Text = _header;
+ if (_header != null)
+ {
+ e.Text += "\n";
+ }
+
+ e.Description = _description;
+ }
+
+ public override void Render(WidgetRenderArgs args)
+ {
+ GH_Canvas canvas = args.Canvas;
+ if (Text != null)
+ {
+ Graphics graphics = canvas.Graphics;
+ float zoom = canvas.Viewport.Zoom;
+ int num = 255;
+ if (zoom < 1f)
+ {
+ float num2 = (zoom - 0.5f) * 2f;
+ num = (int)((float)num * num2);
+ }
+ if (num < 0)
+ {
+ num = 0;
+ }
+ num = GH_Canvas.ZoomFadeLow;
+ SolidBrush brush = new SolidBrush(Color.FromArgb(num, 45, 45, 45));
+ PointF point = new PointF(base.CanvasPivot.X, base.CanvasPivot.Y);
+ graphics.DrawString(_text, WidgetServer.Instance.MenuHeaderFont, brush, point);
+ }
+ }
+
+ public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Release;
+ }
+
+ public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Ignore;
+ }
+
+ public override GH_ObjectResponse RespondToMouseMove(GH_Canvas sender, GH_CanvasMouseEvent e)
+ {
+ return GH_ObjectResponse.Capture;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/RunTimeComponentData.cs b/SandWorm/CustomComponent/RunTimeComponentData.cs
new file mode 100644
index 0000000..42d8bb1
--- /dev/null
+++ b/SandWorm/CustomComponent/RunTimeComponentData.cs
@@ -0,0 +1,205 @@
+using System.Collections.Generic;
+using System.Drawing;
+using Grasshopper.Kernel;
+using Rhino;
+
+namespace SandWorm
+{
+public class RuntimeComponentData
+{
+ private GH_SwitcherComponent component;
+
+ private string cachedName;
+
+ private string cachedNickname;
+
+ private string cachedDescription;
+
+ private Bitmap cachedIcon;
+
+ private List stcInputs;
+
+ private List stcOutputs;
+
+ private List dynInputs;
+
+ private List dynOutputs;
+
+ public string CachedName => cachedName;
+
+ public string CachedNickname => cachedNickname;
+
+ public string CachedDescription => cachedDescription;
+
+ public Bitmap CachedIcon => cachedIcon;
+
+ public List StaticInputs => stcInputs;
+
+ public List StaticOutputs => stcOutputs;
+
+ public List DynamicInputs => dynInputs;
+
+ public List DynamicOutputs => dynOutputs;
+
+ public RuntimeComponentData(GH_SwitcherComponent component)
+ {
+ this.component = component;
+ cachedName = component.Name;
+ cachedNickname = component.NickName;
+ cachedDescription = component.Description;
+ cachedIcon = component.Icon_24x24;
+ dynInputs = new List();
+ dynOutputs = new List();
+ stcInputs = new List();
+ stcOutputs = new List();
+ foreach (IGH_Param item in component.Params.Input)
+ {
+ stcInputs.Add(item);
+ }
+ foreach (IGH_Param item2 in component.Params.Output)
+ {
+ stcOutputs.Add(item2);
+ }
+ }
+
+ private void UnregisterParameter(IGH_Param param, bool input, bool isolate)
+ {
+ if (input)
+ {
+ component.Params.UnregisterInputParameter(param, isolate);
+ }
+ else
+ {
+ component.Params.UnregisterOutputParameter(param, isolate);
+ }
+ if (param.Attributes != null)
+ {
+ if (param.Attributes.Parent != null)
+ {
+ RhinoApp.WriteLine("This should not happen");
+ }
+ param.Attributes.Parent = component.Attributes;
+ }
+ }
+
+ public void PrepareWrite(EvaluationUnit unit)
+ {
+ if (unit == null)
+ {
+ return;
+ }
+ foreach (ExtendedPlug input in unit.Inputs)
+ {
+ UnregisterParameter(input.Parameter, input: true, isolate: false);
+ }
+ foreach (ExtendedPlug output in unit.Outputs)
+ {
+ UnregisterParameter(output.Parameter, input: false, isolate: false);
+ }
+ }
+
+ public void RestoreWrite(EvaluationUnit unit)
+ {
+ if (unit == null)
+ {
+ return;
+ }
+ foreach (ExtendedPlug input in unit.Inputs)
+ {
+ component.Params.RegisterInputParam(input.Parameter);
+ }
+ foreach (ExtendedPlug output in unit.Outputs)
+ {
+ component.Params.RegisterOutputParam(output.Parameter);
+ }
+ }
+
+ public void UnregisterUnit(EvaluationUnit unit)
+ {
+ stcInputs.Clear();
+ stcOutputs.Clear();
+ dynInputs.Clear();
+ dynOutputs.Clear();
+ foreach (ExtendedPlug input in unit.Inputs)
+ {
+ UnregisterParameter(input.Parameter, input: true, isolate: true);
+ }
+ foreach (ExtendedPlug output in unit.Outputs)
+ {
+ UnregisterParameter(output.Parameter, input: false, isolate: true);
+ }
+ foreach (IGH_Param item in component.Params.Input)
+ {
+ stcInputs.Add(item);
+ }
+ foreach (IGH_Param item2 in component.Params.Output)
+ {
+ stcOutputs.Add(item2);
+ }
+ }
+
+ public void RegisterUnit(EvaluationUnit unit)
+ {
+ stcInputs.Clear();
+ stcOutputs.Clear();
+ dynInputs.Clear();
+ dynOutputs.Clear();
+ foreach (IGH_Param item in component.Params.Input)
+ {
+ stcInputs.Add(item);
+ }
+ foreach (IGH_Param item2 in component.Params.Output)
+ {
+ stcOutputs.Add(item2);
+ }
+ foreach (ExtendedPlug input in unit.Inputs)
+ {
+ component.Params.RegisterInputParam(input.Parameter);
+ if (input.IsMenu)
+ {
+ dynInputs.Add(input.Parameter);
+ }
+ else
+ {
+ stcInputs.Add(input.Parameter);
+ }
+ }
+ foreach (ExtendedPlug output in unit.Outputs)
+ {
+ component.Params.RegisterOutputParam(output.Parameter);
+ if (output.IsMenu)
+ {
+ dynOutputs.Add(output.Parameter);
+ }
+ else
+ {
+ stcOutputs.Add(output.Parameter);
+ }
+ }
+ }
+
+ public List GetComponentInputs()
+ {
+ List list = new List(stcInputs);
+ list.AddRange(dynInputs);
+ return list;
+ }
+
+ public List GetComponentInputSection()
+ {
+ return stcInputs;
+ }
+
+ public List GetComponentOutputs()
+ {
+ List list = new List(stcOutputs);
+ list.AddRange(dynOutputs);
+ return list;
+ }
+
+ public List GetComponentOutputSection()
+ {
+ return stcOutputs;
+ }
+}
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/SliderDialog.cs b/SandWorm/CustomComponent/SliderDialog.cs
new file mode 100644
index 0000000..56f00ae
--- /dev/null
+++ b/SandWorm/CustomComponent/SliderDialog.cs
@@ -0,0 +1,176 @@
+using System;
+using Eto.Drawing;
+using Eto.Forms;
+
+namespace SandWorm
+{
+
+ public class SilderDialog : Dialog
+ {
+ private NumericStepper precision;
+
+ private NumericStepper numLower;
+
+ private NumericStepper numUpper;
+
+ private NumericStepper numValue;
+
+ private MenuSlider _slider;
+
+ public SilderDialog(MenuSlider slider)
+ {
+ base.Resizable = true;
+ base.Title = "Slider Control";
+ _slider = slider;
+ InitializeDialog();
+ numValue.Focus();
+ }
+
+ private SliderDialogResult CreateResult(DialogResult result)
+ {
+ return new SliderDialogResult(result, numLower.Value, numUpper.Value, numValue.Value, (int)precision.Value);
+ }
+
+ private static void UpdateStepperPrecision(NumericStepper stepper, int numDecimals)
+ {
+ if (numDecimals < 0)
+ {
+ numDecimals = 0;
+ }
+ double increment = 1.0 / Math.Pow(10.0, numDecimals);
+ stepper.DecimalPlaces = numDecimals;
+ stepper.Increment = increment;
+ decimal d = (decimal)stepper.Value;
+ decimal d2 = (decimal)Math.Pow(10.0, numDecimals);
+ d = Math.Truncate(d * d2) / d2;
+ stepper.Value = (double)d;
+ }
+
+ private void InitializeDialog()
+ {
+ Label label = new Label
+ {
+ Text = "Precision(Number of Decimals)"
+ };
+ precision = new NumericStepper
+ {
+ Increment = 1.0,
+ Value = _slider.NumDecimals,
+ DecimalPlaces = 0,
+ MinValue = 0.0,
+ MaxValue = 12.0
+ };
+ precision.ValueChanged += delegate
+ {
+ int numDecimals = (int)precision.Value;
+ UpdateStepperPrecision(numLower, numDecimals);
+ UpdateStepperPrecision(numUpper, numDecimals);
+ UpdateStepperPrecision(numValue, numDecimals);
+ };
+ base.DefaultButton = new Button
+ {
+ Text = "OK"
+ };
+ base.DefaultButton.Click += delegate
+ {
+ Close(CreateResult(DialogResult.Ok));
+ };
+ base.AbortButton = new Button
+ {
+ Text = "Cancel"
+ };
+ base.AbortButton.Click += delegate
+ {
+ Close(CreateResult(DialogResult.Cancel));
+ };
+ Label label2 = new Label { Text = "Min Value" };
+ Label label3 = new Label { Text = "Max Value" };
+ Label label4 = new Label { Text = "Current Value" };
+ double increment = 1.0 / Math.Pow(10.0, _slider.NumDecimals);
+ numLower = new NumericStepper
+ {
+ Increment = increment,
+ ToolTip = "Minimum slider value",
+ DecimalPlaces = _slider.NumDecimals
+ };
+ numUpper = new NumericStepper
+ {
+ Increment = increment,
+ ToolTip = "Maximum slider value",
+ DecimalPlaces = _slider.NumDecimals
+ };
+ numValue = new NumericStepper
+ {
+ Increment = increment,
+ ToolTip = "Current slider value",
+ DecimalPlaces = _slider.NumDecimals
+ };
+ numLower.ValueChanged += delegate
+ {
+ double value3 = numLower.Value;
+ if (value3 > numUpper.Value)
+ {
+ numUpper.Value = value3;
+ }
+ if (value3 > numValue.Value)
+ {
+ numValue.Value = value3;
+ }
+ };
+ numUpper.ValueChanged += delegate
+ {
+ double value2 = numUpper.Value;
+ if (value2 < numLower.Value)
+ {
+ numLower.Value = value2;
+ }
+ if (value2 < numValue.Value)
+ {
+ numValue.Value = value2;
+ }
+ };
+ numValue.ValueChanged += delegate
+ {
+ double value = numValue.Value;
+ if (value < numLower.Value)
+ {
+ numLower.Value = value;
+ }
+ if (value > numUpper.Value)
+ {
+ numUpper.Value = value;
+ }
+ };
+ numLower.Value = _slider.MinValue;
+ numUpper.Value = _slider.MaxValue;
+ numValue.Value = _slider.Value;
+ TableLayout tableLayout = new TableLayout
+ {
+ Padding = new Padding(5, 10, 5, 5),
+ Spacing = new Size(5, 5)
+ };
+ tableLayout.Rows.Add(new TableRow(new TableCell(label), new TableCell(precision, scaleWidth: true)));
+ tableLayout.Rows.Add(new TableRow(label2, new TableCell(numLower, scaleWidth: true)));
+ tableLayout.Rows.Add(new TableRow(label3, new TableCell(numUpper, scaleWidth: true)));
+ tableLayout.Rows.Add(new TableRow(label4, new TableCell(numValue, scaleWidth: true)));
+ TableLayout control = tableLayout;
+ tableLayout = new TableLayout
+ {
+ Padding = new Padding(5, 10, 5, 5),
+ Spacing = new Size(5, 5)
+ };
+ tableLayout.Rows.Add(new TableRow(null, base.DefaultButton, base.AbortButton, null));
+ TableLayout control2 = tableLayout;
+ base.Content = new TableLayout
+ {
+ Padding = new Padding(5),
+ Spacing = new Size(5, 5),
+ Rows =
+ {
+ new TableRow(control),
+ new TableRow(control2)
+ }
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/SliderDialogResults.cs b/SandWorm/CustomComponent/SliderDialogResults.cs
new file mode 100644
index 0000000..c180634
--- /dev/null
+++ b/SandWorm/CustomComponent/SliderDialogResults.cs
@@ -0,0 +1,46 @@
+using Eto.Forms;
+
+namespace SandWorm
+{
+public sealed class SliderDialogResult
+{
+ public DialogResult Status
+ {
+ get;
+ private set;
+ }
+
+ public double MinValue
+ {
+ get;
+ private set;
+ }
+
+ public double MaxValue
+ {
+ get;
+ private set;
+ }
+
+ public double CurrentValue
+ {
+ get;
+ private set;
+ }
+
+ public int NumDecimals
+ {
+ get;
+ private set;
+ }
+
+ public SliderDialogResult(DialogResult result, double min, double max, double current, int numDecimals)
+ {
+ Status = result;
+ MinValue = min;
+ MaxValue = max;
+ CurrentValue = current;
+ NumDecimals = numDecimals;
+ }
+}
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/StandardFont.cs b/SandWorm/CustomComponent/StandardFont.cs
new file mode 100644
index 0000000..521cd60
--- /dev/null
+++ b/SandWorm/CustomComponent/StandardFont.cs
@@ -0,0 +1,18 @@
+using System.Drawing;
+using Grasshopper.Kernel;
+
+namespace SandWorm
+{
+ public class StandardFont
+ {
+ public static Font Font()
+ {
+ return GH_FontServer.StandardAdjusted;
+ }
+
+ public static Font LargeFont()
+ {
+ return GH_FontServer.LargeAdjusted;
+ }
+ }
+}
diff --git a/SandWorm/CustomComponent/SubComponent.cs b/SandWorm/CustomComponent/SubComponent.cs
new file mode 100644
index 0000000..d2e9fbc
--- /dev/null
+++ b/SandWorm/CustomComponent/SubComponent.cs
@@ -0,0 +1,15 @@
+using Grasshopper.Kernel;
+
+namespace SandWorm
+{
+ public abstract class SubComponent
+ {
+ public abstract string Name();
+ public abstract string Display_name();
+ public abstract void RegisterEvaluationUnits(EvaluationUnitManager mngr);
+ public abstract void SolveInstance(IGH_DataAccess DA, out string msg, out GH_RuntimeMessageLevel level);
+ public virtual void OnComponentLoaded()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/ValueChangeEventHandler.cs b/SandWorm/CustomComponent/ValueChangeEventHandler.cs
new file mode 100644
index 0000000..0b91b05
--- /dev/null
+++ b/SandWorm/CustomComponent/ValueChangeEventHandler.cs
@@ -0,0 +1,6 @@
+using System;
+
+namespace SandWorm
+{
+ public delegate void ValueChangeEventHandler(object sender, EventArgs e);
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/VariableComponent.cs b/SandWorm/CustomComponent/VariableComponent.cs
new file mode 100644
index 0000000..ba28783
--- /dev/null
+++ b/SandWorm/CustomComponent/VariableComponent.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using Grasshopper.Kernel;
+using Grasshopper.Kernel.Parameters;
+
+namespace SandWorm
+{
+ public class VariableComponent
+ {
+ ///
+ /// Add input parameters to the GH component. You might be able to get the input based on the 'name'.
+ ///
+ /// this
+ /// the sender MenuDropDownMenu
+ /// which integer items of the MenuDropDown will activate this input?
+ /// Name of the input
+ /// Nickname of the input
+ /// Description of the input
+ public static void AddOrRemoveVariableParameters(GH_Component component, MenuDropDown menu, List values, string name, string nickName, string description)
+ {
+ if (values.Contains(menu.Value))
+ {
+ foreach (int value in values)
+ {
+ foreach (var p in component.Params.Input)
+ {
+ if (p.Name == name) return;
+ }
+ component.Params.RegisterInputParam(new Param_Number
+ {
+ Name = name,
+ NickName = nickName,
+ Description = description,
+ Access = GH_ParamAccess.list,
+ Optional = true
+ });
+ component.Params.OnParametersChanged();
+ return;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < component.Params.Input.Count; i++)
+ {
+ if (component.Params.Input[i].Name == name)
+ {
+ component.Params.Input[i].RemoveAllSources();
+ component.Params.UnregisterInputParameter(component.Params.Input[i]);
+ }
+ }
+ component.Params.OnParametersChanged();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/WidgetChannel.cs b/SandWorm/CustomComponent/WidgetChannel.cs
new file mode 100644
index 0000000..8f8c35b
--- /dev/null
+++ b/SandWorm/CustomComponent/WidgetChannel.cs
@@ -0,0 +1,8 @@
+namespace SandWorm
+{
+ public enum WidgetChannel
+ {
+ Object,
+ Overlay,
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/WidgetLayoutData.cs b/SandWorm/CustomComponent/WidgetLayoutData.cs
new file mode 100644
index 0000000..106c810
--- /dev/null
+++ b/SandWorm/CustomComponent/WidgetLayoutData.cs
@@ -0,0 +1,19 @@
+using Grasshopper.GUI.Canvas;
+
+namespace SandWorm
+{
+ public abstract class WidgetLayoutData
+ {
+ public GH_PaletteStyle Style
+ {
+ get;
+ private set;
+ }
+
+ protected GH_Palette Palette
+ {
+ get;
+ private set;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/WidgetRenderArgs.cs b/SandWorm/CustomComponent/WidgetRenderArgs.cs
new file mode 100644
index 0000000..8ebe9b0
--- /dev/null
+++ b/SandWorm/CustomComponent/WidgetRenderArgs.cs
@@ -0,0 +1,25 @@
+using Grasshopper.GUI.Canvas;
+
+namespace SandWorm
+{
+ public class WidgetRenderArgs
+ {
+ public GH_Canvas Canvas
+ {
+ get;
+ private set;
+ }
+
+ public WidgetChannel Channel
+ {
+ get;
+ private set;
+ }
+
+ public WidgetRenderArgs(GH_Canvas canvas, WidgetChannel channel)
+ {
+ Canvas = canvas;
+ Channel = channel;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/CustomComponent/WidgetServer.cs b/SandWorm/CustomComponent/WidgetServer.cs
new file mode 100644
index 0000000..a7c0410
--- /dev/null
+++ b/SandWorm/CustomComponent/WidgetServer.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Drawing;
+using Grasshopper.Kernel;
+using Eto.Forms;
+
+namespace SandWorm
+{
+ public sealed class WidgetServer
+ {
+ private static WidgetServer _instance;
+
+ public Font TextFont
+ {
+ get;
+ private set;
+ }
+
+ public Font DropdownFont
+ {
+ get;
+ private set;
+ }
+
+ public Font MenuHeaderFont
+ {
+ get;
+ private set;
+ }
+
+ public Font SliderValueTagFont
+ {
+ get;
+ private set;
+ }
+
+ public Size RadioButtonSize
+ {
+ get;
+ private set;
+ }
+
+ public int RadioButtonPadding
+ {
+ get;
+ private set;
+ }
+
+ public Size CheckBoxSize
+ {
+ get;
+ private set;
+ }
+
+ public int CheckBoxPadding
+ {
+ get;
+ private set;
+ }
+
+ public static WidgetServer Instance
+ {
+ get
+ {
+ if (_instance == null)
+ {
+ _instance = new WidgetServer();
+ }
+ return _instance;
+ }
+ }
+
+ public static float GetScalingFactor()
+ {
+ float num = Screen.PrimaryScreen.LogicalPixelSize;
+ if ((double)num > 0.95 && (double)num < 1.05)
+ {
+ num = 1f; // Windows scaling % as int; e.g. 200% = 2.0
+ }
+ return num;
+ }
+
+ public static int ScaleFontSize(int font_size)
+ {
+ // E.g. 8em at 100% scale; 4em at 200% scale
+ return (int)Math.Round((double)font_size * (double)GH_FontServer.StandardAdjusted.Height / (double)GH_FontServer.Standard.Height);
+ }
+
+ private WidgetServer()
+ {
+ string name = "Arial";
+ int num = ScaleFontSize(8);
+ TextFont = new Font(new FontFamily(name), num, FontStyle.Regular);
+ string name2 = "Arial";
+ int num2 = ScaleFontSize(8);
+ DropdownFont = new Font(new FontFamily(name2), num2, FontStyle.Italic);
+ string name3 = "Arial";
+ int num3 = ScaleFontSize(8);
+ MenuHeaderFont = new Font(new FontFamily(name3), num3, FontStyle.Bold);
+ string name4 = "Arial";
+ int num4 = ScaleFontSize(10);
+ SliderValueTagFont = new Font(new FontFamily(name4), num4, FontStyle.Italic);
+ int width = 8;
+ int height = 8;
+ RadioButtonSize = new Size(width, height);
+ RadioButtonPadding = 4;
+ int width2 = 8;
+ int height2 = 8;
+ CheckBoxSize = new Size(width2, height2);
+ CheckBoxPadding = 4;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/FodyWeavers.xml b/SandWorm/FodyWeavers.xml
index b231262..366d551 100644
--- a/SandWorm/FodyWeavers.xml
+++ b/SandWorm/FodyWeavers.xml
@@ -1,4 +1,5 @@
-
+
\ No newline at end of file
diff --git a/SandWorm/FodyWeavers.xsd b/SandWorm/FodyWeavers.xsd
index 44a5374..05e92c1 100644
--- a/SandWorm/FodyWeavers.xsd
+++ b/SandWorm/FodyWeavers.xsd
@@ -17,6 +17,16 @@
A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
+
+
+ A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
+
+
+
+
+ A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
+
+
A list of unmanaged 32 bit assembly names to include, delimited with line breaks.
@@ -43,6 +53,16 @@
Controls if .pdbs for reference assemblies are also embedded.
+
+
+ Controls if runtime assemblies are also embedded.
+
+
+
+
+ Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.
+
+
Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.
@@ -73,6 +93,16 @@
A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.
+
+
+ A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
+
+
+
+
+ A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.
+
+
A list of unmanaged 32 bit assembly names to include, delimited with |.
diff --git a/SandWorm/KinectController.cs b/SandWorm/KinectController.cs
deleted file mode 100644
index b3024f0..0000000
--- a/SandWorm/KinectController.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-using System;
-using System.Windows.Media;
-using Microsoft.Kinect;
-
-
-namespace SandWorm
-{
- static class KinectController
- {
- public static KinectSensor sensor = null;
- public static int depthHeight = 0;
- public static int depthWidth = 0;
- public static int colorHeight = 0;
- public static int colorWidth = 0;
- public static MultiSourceFrameReader multiFrameReader = null;
- public static FrameDescription depthFrameDescription = null;
- public static FrameDescription colorFrameDescription = null;
- public static int refc = 0;
- public static ushort[] depthFrameData = null;
- public static byte[] colorFrameData = null;
- public static int bytesForPixelColor = (PixelFormats.Bgr32.BitsPerPixel + 7) / 8;
-
- public static void AddRef()
- {
- if (sensor == null)
- {
- Initialize();
- }
- if (sensor != null)
- {
- refc++;
- }
- }
-
- public static void RemoveRef()
- {
- refc--;
- if ((sensor != null) && (refc == 0))
- {
- multiFrameReader.MultiSourceFrameArrived -= Reader_FrameArrived;
- sensor.Close();
- sensor = null;
- }
- }
-
- public static void Initialize()
- {
- sensor = KinectSensor.GetDefault();
- // TODO: switch based on component type?
- multiFrameReader = sensor.OpenMultiSourceFrameReader(FrameSourceTypes.Depth | FrameSourceTypes.Color);
- multiFrameReader.MultiSourceFrameArrived += new EventHandler(KinectController.Reader_FrameArrived);
-
- sensor.Open();
- }
-
- private static void Reader_FrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
- {
- if (e.FrameReference != null)
- {
- MultiSourceFrame multiFrame = e.FrameReference.AcquireFrame();
-
- if (multiFrame.DepthFrameReference != null)
- {
- try
- {
- using (DepthFrame depthFrame = multiFrame.DepthFrameReference.AcquireFrame())
- {
- if (depthFrame != null)
- {
- using (KinectBuffer buffer = depthFrame.LockImageBuffer())
- {
- depthFrameDescription = depthFrame.FrameDescription;
- depthWidth = depthFrameDescription.Width;
- depthHeight = depthFrameDescription.Height;
- depthFrameData = new ushort[depthWidth * depthHeight];
- depthFrame.CopyFrameDataToArray(depthFrameData);
- }
- }
- }
- }
- catch (Exception) { return; }
- }
-
- if (multiFrame.ColorFrameReference != null)
- {
- try
- {
- using (ColorFrame colorFrame = multiFrame.ColorFrameReference.AcquireFrame())
- {
- if (colorFrame != null)
- {
- colorFrameDescription = colorFrame.FrameDescription;
- colorWidth = colorFrameDescription.Width;
- colorHeight = colorFrameDescription.Height;
- colorFrameData = new byte[colorWidth * colorHeight * bytesForPixelColor]; // 4 == bytes per color
-
- using (KinectBuffer buffer = colorFrame.LockRawImageBuffer())
- {
- if (colorFrame.RawColorImageFormat == ColorImageFormat.Bgra)
- {
- colorFrame.CopyRawFrameDataToArray(colorFrameData);
- }
- else
- {
- colorFrame.CopyConvertedFrameDataToArray(colorFrameData, ColorImageFormat.Bgra);
- }
- }
- }
- }
- }
- catch (Exception) { return; }
- }
- }
- }
-
- public static KinectSensor Sensor
- {
- get
- {
- if (sensor == null)
- {
- Initialize();
- }
- return sensor;
- }
- set
- {
- sensor = value;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/SandWorm/Properties/AssemblyInfo.cs b/SandWorm/Properties/AssemblyInfo.cs
index aba7ce8..1994c24 100644
--- a/SandWorm/Properties/AssemblyInfo.cs
+++ b/SandWorm/Properties/AssemblyInfo.cs
@@ -10,7 +10,7 @@
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SandWorm")]
-[assembly: AssemblyCopyright("Copyright © 2019")]
+[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyVersion("2.0.1")]
[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SandWorm/Properties/Resources.Designer.cs b/SandWorm/Properties/Resources.Designer.cs
index f427b9f..e5a622b 100644
--- a/SandWorm/Properties/Resources.Designer.cs
+++ b/SandWorm/Properties/Resources.Designer.cs
@@ -60,6 +60,16 @@ internal Resources() {
}
}
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Icons_Main {
+ get {
+ object obj = ResourceManager.GetObject("Icons_Main", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
diff --git a/SandWorm/Properties/Resources.resx b/SandWorm/Properties/Resources.resx
index 04fc575..c0fa9d2 100644
--- a/SandWorm/Properties/Resources.resx
+++ b/SandWorm/Properties/Resources.resx
@@ -118,6 +118,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ ..\Resources\Icons_Main.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
icons_marker_areas.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
diff --git a/SandWorm/Resources/Icons_Main.png b/SandWorm/Resources/Icons_Main.png
new file mode 100644
index 0000000..860bb42
Binary files /dev/null and b/SandWorm/Resources/Icons_Main.png differ
diff --git a/SandWorm/Resources/costura64/depthengine_2_0.dll b/SandWorm/Resources/costura64/depthengine_2_0.dll
new file mode 100644
index 0000000..6db4243
Binary files /dev/null and b/SandWorm/Resources/costura64/depthengine_2_0.dll differ
diff --git a/SandWorm/Resources/costura64/k4a.dll b/SandWorm/Resources/costura64/k4a.dll
new file mode 100644
index 0000000..d545608
Binary files /dev/null and b/SandWorm/Resources/costura64/k4a.dll differ
diff --git a/SandWorm/SandWorm.csproj b/SandWorm/SandWorm.csproj
index 23546fa..aee0222 100644
--- a/SandWorm/SandWorm.csproj
+++ b/SandWorm/SandWorm.csproj
@@ -1,7 +1,7 @@
-
-
+
+
Debug
AnyCPU
@@ -12,7 +12,7 @@
Properties
SandWorm
SandWorm
- v4.6.1
+ v4.7.2
512
false
@@ -46,6 +46,7 @@
prompt
MinimumRecommendedRules.ruleset
false
+ true
bin\x64\Release\
@@ -55,72 +56,156 @@
x64
prompt
MinimumRecommendedRules.ruleset
+ true
+
+
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE
+ true
+ full
+ x86
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
+ false
+
+
+ bin\x86\Release\
+ TRACE
+ true
+ true
+ pdbonly
+ x86
+ 7.3
+ prompt
+ MinimumRecommendedRules.ruleset
-
- ..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll
+
+ ..\packages\Costura.Fody.5.7.0\lib\netstandard1.0\Costura.dll
- ..\packages\RhinoCommon.6.17.19235.15041\lib\net45\Eto.dll
+ ..\packages\RhinoCommon.6.20.19322.20361\lib\net45\Eto.dll
+
+
+ ..\packages\Grasshopper.6.20.19322.20361\lib\net45\GH_IO.dll
+
+
+ ..\packages\Grasshopper.6.20.19322.20361\lib\net45\Grasshopper.dll
-
- ..\packages\Grasshopper.6.17.19235.15041\lib\net45\GH_IO.dll
+
+ ..\packages\ILGPU.1.0.0-rc1\lib\net471\ILGPU.dll
-
- ..\packages\Grasshopper.6.17.19235.15041\lib\net45\Grasshopper.dll
+
+ ..\packages\ILGPU.Algorithms.1.0.0-rc1\lib\net471\ILGPU.Algorithms.dll
-
- ..\packages\K4AdotNet.1.3.0\lib\net461\K4AdotNet.dll
+
+ ..\packages\Microsoft.Azure.Kinect.Sensor.1.4.1\lib\netstandard2.0\Microsoft.Azure.Kinect.Sensor.dll
..\packages\Microsoft.Kinect.2.0.1410.19000\lib\net45\Microsoft.Kinect.dll
- ..\packages\OpenCvSharp4.4.1.1.20191110\lib\net461\OpenCvSharp.dll
-
-
- ..\packages\OpenCvSharp4.4.1.1.20191110\lib\net461\OpenCvSharp.Blob.dll
+ ..\packages\OpenCvSharp4.4.5.3.20210817\lib\net461\OpenCvSharp.dll
- ..\packages\OpenCvSharp4.4.1.1.20191110\lib\net461\OpenCvSharp.Extensions.dll
-
-
- ..\packages\OpenCvSharp4.4.1.1.20191110\lib\net461\OpenCvSharp.UserInterface.dll
+ ..\packages\OpenCvSharp4.4.5.3.20210817\lib\net461\OpenCvSharp.Extensions.dll
-
- ..\packages\RhinoCommon.6.17.19235.15041\lib\net45\Rhino.UI.dll
+
+ ..\packages\RhinoCommon.6.20.19322.20361\lib\net45\Rhino.UI.dll
-
- ..\packages\RhinoCommon.6.17.19235.15041\lib\net45\RhinoCommon.dll
+
+ ..\packages\RhinoCommon.6.20.19322.20361\lib\net45\RhinoCommon.dll
+
+ ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll
+
+
+ ..\packages\System.Collections.Immutable.6.0.0-preview.7.21377.19\lib\net461\System.Collections.Immutable.dll
+
-
- ..\packages\System.Runtime.CompilerServices.Unsafe.4.6.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
+
+ ..\packages\System.Drawing.Common.5.0.2\lib\net461\System.Drawing.Common.dll
+
+
+ ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll
+
+
+
+ ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
+
+
+ ..\packages\System.Reflection.Metadata.6.0.0-preview.7.21377.19\lib\net461\System.Reflection.Metadata.dll
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0-preview.7.21377.19\lib\net461\System.Runtime.CompilerServices.Unsafe.dll
+
+
+ ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll
+
+
+
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
True
True
@@ -128,10 +213,15 @@
-
+
+
+
+
+
+
@@ -139,13 +229,11 @@
-
-
-
ResXFileCodeGenerator
Resources.Designer.cs
+ Designer
@@ -186,8 +274,8 @@
en-US
-
- C:\Program Files\Rhino 6\System\Rhino.exe
+
+ C:\Program Files\Rhino 7\System\Rhino.exe
Program
@@ -196,23 +284,27 @@
- Copy "$(TargetPath)" "$(TargetDir)$(ProjectName).gha"
+ Copy "$(TargetPath)" "$(TargetDir)$(ProjectName).gha"
Copy "$(TargetDir)\dll\x64\OpenCvSharpExtern.dll" "$(TargetDir)OpenCvSharpExtern.dll"
-
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SandWorm/SetupOptions.cs b/SandWorm/SetupOptions.cs
deleted file mode 100644
index be2cd42..0000000
--- a/SandWorm/SetupOptions.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SandWorm
-{
- public class SetupOptions
- {
- public SetupOptions()
- {
- }
-
- private double _sensorElevation;
- private int _leftColumns;
- private int _rightColumns;
- private int _topRows;
- private int _bottomRows;
- private int _tickRate;
- private int _keepFrames;
- private double[] _elevationArray; // Store all deltas between desired and measured distance values from the sensor to the table for each pixel.
-
- public double SensorElevation
- {
- get { return _sensorElevation; }
- set { _sensorElevation = value; }
- }
- public int LeftColumns
- {
- get { return _leftColumns; }
- set { _leftColumns = value; }
- }
-
- public int RightColumns
- {
- get { return _rightColumns; }
- set { _rightColumns = value; }
- }
-
- public int TopRows
- {
- get { return _topRows; }
- set { _topRows = value; }
- }
-
- public int BottomRows
- {
- get { return _bottomRows; }
- set { _bottomRows = value; }
- }
-
- public int TickRate
- {
- get { return _tickRate; }
- set { _tickRate = value; }
- }
-
- public int KeepFrames
- {
- get { return _keepFrames; }
- set { _keepFrames = value; }
- }
- public double[] ElevationArray
- {
- get { return _elevationArray; }
- set { _elevationArray = value; }
- }
-
- }
-}
diff --git a/SandWorm/Utilities/Core.cs b/SandWorm/Utilities/Core.cs
new file mode 100644
index 0000000..19837ef
--- /dev/null
+++ b/SandWorm/Utilities/Core.cs
@@ -0,0 +1,392 @@
+using System;
+using System.Threading.Tasks;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using Microsoft.Azure.Kinect.Sensor;
+using Rhino.Geometry;
+using SandWorm.Analytics;
+
+namespace SandWorm
+{
+ public static class Core
+ {
+ public static Mesh CreateQuadMesh(ref Mesh mesh, Point3d[] vertices, ref Color[] colors, ref Vector3?[] booleanMatrix,
+ Structs.KinectTypes kinectType, int xStride, int yStride)
+ {
+ int xd = xStride; // The x-dimension of the data
+ int yd = yStride; // They y-dimension of the data
+
+ if (mesh == null)
+ {
+ mesh = new Mesh();
+ mesh.Vertices.Capacity = vertices.Length; // Don't resize array
+ mesh.Vertices.UseDoublePrecisionVertices = true;
+ mesh.Vertices.AddVertices(vertices);
+
+ switch (kinectType)
+ {
+ case Structs.KinectTypes.KinectAzureNear:
+ case Structs.KinectTypes.KinectAzureWide:
+ for (int y = 1; y < yd - 1; y++) // Iterate over y dimension
+ for (int x = 1; x < xd - 1; x++) // Iterate over x dimension
+ {
+ int i = y * xd + x;
+ int j = (y - 1) * xd + x;
+
+ // This check is necessary for Kinect Azure WFOV
+ if (booleanMatrix[i] != null && booleanMatrix[i - 1] != null && booleanMatrix[j] != null && booleanMatrix[j - 1] != null)
+ mesh.Faces.AddFace(j - 1, j, i, i - 1);
+ }
+ break;
+
+ case Structs.KinectTypes.KinectForWindows:
+ for (int y = 1; y < yd - 1; y++) // Iterate over y dimension
+ for (int x = 1; x < xd - 1; x++) // Iterate over x dimension
+ {
+ int i = y * xd + x;
+ int j = (y - 1) * xd + x;
+ mesh.Faces.AddFace(j - 1, j, i, i - 1);
+ }
+ break;
+ }
+
+
+ }
+ else
+ {
+ unsafe
+ {
+ using (var meshAccess = mesh.GetUnsafeLock(true))
+ {
+ Point3d* points = meshAccess.VertexPoint3dArray(out int arrayLength);
+ for (int i = 0; i < arrayLength; i++)
+ {
+ points->Z = vertices[i].Z;
+ points++;
+ }
+ mesh.ReleaseUnsafeLock(meshAccess);
+ }
+ }
+ }
+
+ if (colors != null && colors.Length > 0) // Colors only provided if the mesh style permits
+ mesh.VertexColors.SetColors(colors);
+ else
+ mesh.VertexColors.Clear();
+
+ return mesh;
+ }
+
+
+ public static void GetTrimmedDimensions(Structs.KinectTypes kinectType, ref int trimmedWidth, ref int trimmedHeight, int[] runningSum,
+ double topRows, double bottomRows, double leftColumns, double rightColumns)
+ {
+ int _x;
+ int _y;
+ switch (kinectType)
+ {
+ case Structs.KinectTypes.KinectForWindows:
+ _x = KinectForWindows.depthWidth;
+ _y = KinectForWindows.depthHeight;
+ break;
+ case Structs.KinectTypes.KinectAzureNear:
+ _x = KinectAzureController.depthWidthNear;
+ _y = KinectAzureController.depthHeightNear;
+ break;
+ case Structs.KinectTypes.KinectAzureWide:
+ _x = KinectAzureController.depthWidthWide;
+ _y = KinectAzureController.depthHeightWide;
+ break;
+ default:
+ throw new ArgumentException("Invalid Kinect Type", "original");
+ }
+
+ runningSum = Enumerable.Range(1, _x * _y).Select(i => new int()).ToArray();
+
+ trimmedWidth = (int)(_x - leftColumns - rightColumns);
+ trimmedHeight = (int)(_y - topRows - bottomRows);
+ }
+
+ public static void SetupCorrectiveLookupTables(Vector2[] idealXYCoordinates, double[] verticalTiltCorrectionLookupTable,
+ Vector3?[] booleanMatrix, Vector3?[] trimmedBooleanMatrix,
+ double leftColumns, double rightColumns, double topRows, double bottomRows, int height, int width)
+ {
+ ref Vector3? bv0 = ref booleanMatrix[0];
+ ref Vector3? bd0 = ref trimmedBooleanMatrix[0];
+
+ int _yStride = height - (int)bottomRows;
+ int _xStride = width - (int)leftColumns;
+
+ for (int rows = (int)topRows, j = 0; rows < _yStride; rows++)
+ {
+ for (int columns = (int)rightColumns; columns < _xStride; columns++, j++)
+ {
+ int i = rows * width + columns;
+ verticalTiltCorrectionLookupTable[j] = idealXYCoordinates[i].Y * KinectAzureController.sin6;
+
+ Unsafe.Add(ref bd0, j) = Unsafe.Add(ref bv0, i);
+ }
+ }
+ }
+
+ public static void TrimXYLookupTable(Vector2[] sourceXY, Vector2[] destinationXY,
+ double leftColumns, double rightColumns, double topRows, double bottomRows, int height, int width)
+ {
+ ref Vector2 rv0 = ref sourceXY[0];
+ ref Vector2 rd0 = ref destinationXY[0];
+
+ int _yStride = height - (int)bottomRows;
+ int _xStride = width - (int)leftColumns;
+ float _units = (float)SandWormComponent.unitsMultiplier;
+
+ for (int rows = (int)topRows, j = 0; rows < _yStride; rows++)
+ {
+ for (int columns = (int)rightColumns; columns < _xStride; columns++, j++)
+ {
+ int i = rows * width + columns;
+ Unsafe.Add(ref rd0, j).X = Unsafe.Add(ref rv0, i).X * _units;
+ Unsafe.Add(ref rd0, j).Y = Unsafe.Add(ref rv0, i).Y * _units;
+ }
+ }
+ }
+
+
+ public static void SetupRenderBuffer(int[] depthFrameDataInt, Structs.KinectTypes kinectType, ref BGRA[] rgbArray, int analysisType,
+ double leftColumns, double rightColumns, double topRows, double bottomRows, double sensorElevation, ref int activeHeight, ref int activeWidth,
+ double averageFrames, int[] runningSum, LinkedList renderBuffer)
+ {
+
+ ushort[] depthFrameData;
+
+ if (kinectType == Structs.KinectTypes.KinectForWindows)
+ {
+ KinectForWindows.SetupSensor();
+ depthFrameData = KinectForWindows.depthFrameData;
+ activeHeight = KinectForWindows.depthHeight;
+ activeWidth = KinectForWindows.depthWidth;
+ }
+ else
+ {
+ KinectAzureController.SetupSensor(kinectType, sensorElevation);
+ KinectAzureController.CaptureFrame(ref rgbArray, analysisType);
+ depthFrameData = KinectAzureController.depthFrameData;
+ activeHeight = KinectAzureController.depthHeight;
+ activeWidth = KinectAzureController.depthWidth;
+ }
+
+ // Trim the depth array and cast ushort values to int
+ CopyAsIntArray(depthFrameData, depthFrameDataInt,
+ leftColumns, rightColumns, topRows, bottomRows,
+ activeHeight, activeWidth);
+
+ // Reset everything when changing the amount of frames to average across
+ if (renderBuffer.Count > averageFrames)
+ {
+ renderBuffer.Clear();
+ Array.Clear(runningSum, 0, runningSum.Length);
+ renderBuffer.AddLast(depthFrameDataInt);
+ }
+ else
+ {
+ renderBuffer.AddLast(depthFrameDataInt);
+ }
+
+ }
+
+ public static void AverageAndBlurPixels(int[] depthFrameDataInt, ref double[] averagedDepthFrameData, int[] runningSum, LinkedList renderBuffer,
+ double sensorElevation, double averageFrames,
+ double blurRadius, int trimmedWidth, int trimmedHeight)
+ {
+ // Average across multiple frames
+ for (var pixel = 0; pixel < depthFrameDataInt.Length; pixel++)
+ {
+ if (depthFrameDataInt[pixel] > 200) // We have a valid pixel.
+ {
+ runningSum[pixel] += depthFrameDataInt[pixel];
+ }
+ else
+ {
+ if (pixel > 0) // Pixel is invalid and we have a neighbor to steal information from
+ {
+ //D1 Method
+ runningSum[pixel] += depthFrameDataInt[pixel - 1];
+
+ // Replace the zero value from the depth array with the one from the neighboring pixel
+ renderBuffer.Last.Value[pixel] = depthFrameDataInt[pixel - 1];
+ }
+ else // Pixel is invalid and it is the first one in the list. (No neighbor on the left hand side, so we set it to the lowest point on the table)
+ {
+ runningSum[pixel] += (int)sensorElevation;
+ renderBuffer.Last.Value[pixel] = (int)sensorElevation;
+ }
+ }
+
+ averagedDepthFrameData[pixel] = runningSum[pixel] / renderBuffer.Count; // Calculate average values
+
+ if (renderBuffer.Count >= averageFrames)
+ runningSum[pixel] -= renderBuffer.First.Value[pixel]; // Subtract the oldest value from the sum
+ }
+
+
+ if (blurRadius > 1) // Apply gaussian blur
+ {
+ var gaussianBlurProcessor = new GaussianBlurProcessor((int)blurRadius, trimmedWidth, trimmedHeight);
+ gaussianBlurProcessor.Apply(averagedDepthFrameData);
+ }
+ }
+
+ public static void GeneratePointCloud(double[] averagedDepthFrameData, Vector2[] trimmedXYLookupTable, double[] verticalTiltCorrectionLookupTable, Structs.KinectTypes kinectType,
+ Point3d[] allPoints, LinkedList renderBuffer, int trimmedWidth, int trimmedHeight, double sensorElevation, double averageFrames)
+ {
+ Point3d tempPoint = new Point3d();
+
+ switch (kinectType)
+ {
+ case Structs.KinectTypes.KinectAzureNear:
+ case Structs.KinectTypes.KinectAzureWide:
+ for (int rows = 0, i = 0; rows < trimmedHeight; rows++)
+ for (int columns = 0; columns < trimmedWidth; columns++, i++)
+ {
+ tempPoint.X = trimmedXYLookupTable[i].X * -1;
+ tempPoint.Y = trimmedXYLookupTable[i].Y;
+
+ // Correct for Kinect Azure's tilt of the depth camera
+ double correctedElevation = averagedDepthFrameData[i] - verticalTiltCorrectionLookupTable[i];
+ tempPoint.Z = (correctedElevation - sensorElevation) * -SandWormComponent.unitsMultiplier;
+ averagedDepthFrameData[i] = correctedElevation;
+
+ allPoints[i] = tempPoint;
+ }
+ break;
+
+ case Structs.KinectTypes.KinectForWindows:
+ for (int rows = 0, i = 0; rows < trimmedHeight; rows++)
+ for (int columns = 0; columns < trimmedWidth; columns++, i++)
+ {
+ tempPoint.X = trimmedXYLookupTable[i].X;
+ tempPoint.Y = trimmedXYLookupTable[i].Y * -1;
+ tempPoint.Z = (averagedDepthFrameData[i] - sensorElevation) * -SandWormComponent.unitsMultiplier;
+ allPoints[i] = tempPoint;
+ }
+ break;
+
+ }
+
+
+
+ // Keep only the desired amount of frames in the buffer
+ while (renderBuffer.Count >= averageFrames) renderBuffer.RemoveFirst();
+ }
+
+
+ public static void GenerateMeshColors(ref Color[] vertexColors, Structs.AnalysisTypes analysisType, double[] averagedDepthFrameData,
+ Vector2[]xyLookuptable, Color[] pixelColors, double gradientRange, Structs.ColorPalettes colorPalette, List customColors,
+ double?[] baseMeshElevationPoints, Point3d[] allPoints, ref List stats,
+ double sensorElevation, int trimmedWidth, int trimmedHeight)
+ {
+ switch (analysisType)
+ {
+ case Structs.AnalysisTypes.None:
+ vertexColors = new None().GetColorCloudForAnalysis();
+ break;
+
+ case Structs.AnalysisTypes.Camera:
+ vertexColors = pixelColors;
+ break;
+
+ case Structs.AnalysisTypes.Elevation:
+ vertexColors = new Elevation().GetColorCloudForAnalysis(averagedDepthFrameData, sensorElevation, gradientRange, colorPalette, customColors);
+ break;
+
+ case Structs.AnalysisTypes.Slope:
+ vertexColors = new Slope().GetColorCloudForAnalysis(averagedDepthFrameData,
+ trimmedWidth, trimmedHeight, gradientRange, xyLookuptable);
+ break;
+
+ case Structs.AnalysisTypes.Aspect:
+ vertexColors = new Aspect().GetColorCloudForAnalysis(averagedDepthFrameData,
+ trimmedWidth, trimmedHeight, gradientRange);
+ break;
+
+ case Structs.AnalysisTypes.CutFill:
+ vertexColors = new CutFill().GetColorCloudForAnalysis(allPoints, baseMeshElevationPoints, gradientRange, colorPalette, customColors, ref stats);
+ break;
+ }
+ }
+
+ public static void CopyAsIntArray(ushort[] source, int[] destination,
+ double leftColumns, double rightColumns, double topRows, double bottomRows, int height, int width)
+ {
+ if (source == null)
+ return; // Triggers on initial setup
+
+ ref ushort ru0 = ref source[0];
+ ref int ri0 = ref destination[0];
+
+ for (int rows = (int)topRows, j = 0; rows < height - bottomRows; rows++)
+ {
+ for (int columns = (int)rightColumns; columns < width - leftColumns; columns++, j++)
+ {
+ int i = rows * width + columns;
+ Unsafe.Add(ref ri0, j) = Unsafe.Add(ref ru0, i);
+ }
+ }
+ }
+
+ public static void TrimColorArray(BGRA[] source, ref Color[] destination, Structs.KinectTypes kinectType,
+ double leftColumns, double rightColumns, double topRows, double bottomRows, int height, int width)
+ {
+ int _yStride = height - (int)bottomRows;
+ int _xStride = width - (int)leftColumns;
+
+ switch (kinectType)
+ {
+ case Structs.KinectTypes.KinectAzureNear:
+ case Structs.KinectTypes.KinectAzureWide:
+
+ for (int rows = (int)topRows, j = 0; rows < _yStride; rows++)
+ {
+ for (int columns = (int)rightColumns; columns < _xStride; columns++, j++)
+ {
+ int i = rows * width + columns;
+ destination[j] = Color.FromArgb(source[i].R, source[i].G, source[i].B);
+ }
+ }
+ break;
+
+ case Structs.KinectTypes.KinectForWindows:
+ if (KinectForWindows.rgbColorData == null)
+ break;
+ for (int rows = (int)topRows, j = 0; rows < _yStride; rows++)
+ {
+ for (int columns = (int)rightColumns; columns < _xStride; columns++, j++)
+ {
+ int i = rows * width + columns;
+ destination[j] = KinectForWindows.rgbColorData[i];
+ }
+ }
+ break;
+ }
+ }
+
+ public static double Calibrate(Structs.KinectTypes kinectType)
+ {
+ switch(kinectType)
+ {
+ case Structs.KinectTypes.KinectAzureNear:
+ case Structs.KinectTypes.KinectAzureWide:
+ return KinectAzureController.Calibrate();
+
+ case Structs.KinectTypes.KinectForWindows:
+ return KinectForWindows.Calibrate();
+
+ default:
+ return 0;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/Utilities/GeneralHelpers.cs b/SandWorm/Utilities/GeneralHelpers.cs
new file mode 100644
index 0000000..4b99764
--- /dev/null
+++ b/SandWorm/Utilities/GeneralHelpers.cs
@@ -0,0 +1,250 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace SandWorm
+{
+ public static class GeneralHelpers
+ {
+ public static void HideParameterGeometry(Grasshopper.Kernel.IGH_Param parameter)
+ {
+ // Casting to IGH_PreviewObject allows access to the 'Hidden' property
+ if (parameter.Recipients.Count > 0)
+ ((Grasshopper.Kernel.IGH_PreviewObject)parameter).Hidden = true;
+ else
+ ((Grasshopper.Kernel.IGH_PreviewObject)parameter).Hidden = false;
+ }
+ public static void SetupLogging(ref Stopwatch timer, ref List output)
+ {
+ timer = Stopwatch.StartNew(); // Setup timer used for debugging
+ output = new List(); // For the debugging log lines
+ }
+ public static void LogTiming(ref List output, Stopwatch timer, string eventDescription)
+ {
+ var logInfo = eventDescription + ": ";
+ timer.Stop();
+ output.Add(logInfo.PadRight(28, ' ') + timer.ElapsedMilliseconds.ToString() + " ms");
+ timer.Restart();
+ }
+
+ public static double ConvertDrawingUnits(Rhino.UnitSystem units)
+ {
+ double unitsMultiplier = 1.0;
+
+ switch (units.ToString())
+ {
+ case "Kilometers":
+ unitsMultiplier = 0.0001;
+ break;
+
+ case "Meters":
+ unitsMultiplier = 0.001;
+ break;
+
+ case "Decimeters":
+ unitsMultiplier = 0.01;
+ break;
+
+ case "Centimeters":
+ unitsMultiplier = 0.1;
+ break;
+
+ case "Millimeters":
+ unitsMultiplier = 1.0;
+ break;
+
+ case "Inches":
+ unitsMultiplier = 0.0393701;
+ break;
+
+ case "Feet":
+ unitsMultiplier = 0.0328084;
+ break;
+ }
+ return unitsMultiplier;
+ }
+
+ public static int ConvertFPStoMilliseconds(int fps)
+ {
+ switch (fps)
+ {
+ default:
+ case 0: // Max
+ return 33;
+
+ case 1: // 15 FPS
+ return 66;
+
+ case 2: // 5 FPS
+ return 200;
+
+ case 3: // 1 FPS
+ return 1000;
+
+ case 4: // 0.2 FPS
+ return 5000;
+ }
+ }
+
+ public static void SwapLeftRight(Structs.KinectTypes sensorType, double leftColumns, double rightColumns, ref double _left, ref double _right)
+ {
+ switch (sensorType)
+ {
+ case Structs.KinectTypes.KinectForWindows:
+ _left = rightColumns;
+ _right = leftColumns;
+ break;
+
+ case Structs.KinectTypes.KinectAzureNear:
+ case Structs.KinectTypes.KinectAzureWide:
+ _left = leftColumns;
+ _right = rightColumns;
+ break;
+ }
+ }
+
+ public static void CreateLabels(Rhino.Geometry.Point3d[] pointArray, ref List labels,
+ Structs.AnalysisTypes analysisType, double?[] baseMeshElevationPoints,
+ int xStride, int yStride, int spacing)
+ {
+ Rhino.Display.Text3d _text;
+ double _distanceToTerrain = 5 * SandWormComponent.unitsMultiplier;
+
+ int maxSize = 10;
+ double _size = spacing / 5;
+ if (_size > maxSize)
+ _size = maxSize;
+
+ _size *= SandWormComponent.unitsMultiplier;
+
+ int roundingFactor = CalculateRoundingFactor(SandWormComponent.unitsMultiplier);
+
+ for (int y = 0; y < yStride; y += spacing) // Iterate over y dimension
+ for (int x = spacing; x < xStride; x += spacing) // Iterate over x dimension
+ {
+ int i = y * xStride + x;
+ Rhino.Geometry.Point3d _point = new Rhino.Geometry.Point3d(pointArray[i].X, pointArray[i].Y, pointArray[i].Z + _distanceToTerrain);
+ Rhino.Geometry.Plane _plane = new Rhino.Geometry.Plane(_point, new Rhino.Geometry.Vector3d(0, 0, 1));
+ double _value = 0;
+
+ switch (analysisType)
+ {
+ case Structs.AnalysisTypes.Elevation:
+ _value = Math.Round(pointArray[i].Z, roundingFactor);
+ break;
+
+ case Structs.AnalysisTypes.CutFill:
+ if (baseMeshElevationPoints[i] == null)
+ continue;
+
+ _value = Math.Round(pointArray[i].Z - (double)baseMeshElevationPoints[i], roundingFactor);
+ break;
+ }
+
+ _text = new Rhino.Display.Text3d($".{_value}", _plane, _size);
+ labels.Add(_text);
+ }
+ }
+
+ public static int CalculateRoundingFactor(double unitsMultiplier)
+ {
+ switch (unitsMultiplier)
+ {
+ case 1: // Millimeters
+ return 0;
+ case 0.1: // Centimeters
+ case 0.0393701: // Inches
+ return 1;
+ case 0.01: // Decimeters
+ case 0.0328084: // Feet
+ return 2;
+ case 0.001: // Meters
+ return 3;
+ case 0.0001: // Kilometers
+ return 4;
+ default:
+ return 0;
+ }
+ }
+
+ public static Grasshopper.Kernel.Types.GH_Line[] ConvertLineToGHLine(List rhinoLines)
+ {
+ Grasshopper.Kernel.Types.GH_Line[] ghLines = new Grasshopper.Kernel.Types.GH_Line[rhinoLines.Count];
+
+ System.Threading.Tasks.Parallel.For(0, rhinoLines.Count, i =>
+ { ghLines[i] = new Grasshopper.Kernel.Types.GH_Line(rhinoLines[i]); });
+
+ return ghLines;
+ }
+
+ // Multiply two int[] arrays using SIMD instructions
+ public static int[] SimdVectorProd(int[] a, int[] b)
+ {
+ if (a.Length != b.Length) throw new ArgumentException();
+ if (a.Length == 0) return Array.Empty();
+
+ int[] result = new int[a.Length];
+
+ // Get a reference to the first value in all 3 arrays
+ ref int ra = ref a[0];
+ ref int rb = ref b[0];
+ ref int rr = ref result[0];
+ int length = a.Length;
+ int i = 0;
+
+
+ /* Calculate the maximum offset we can work on with SIMD instructions.
+ * Eg. if each SIMD register can hold 4 int values, and our input
+ * arrays have 10 values, we can use SIMD instructions to sum the
+ * first two groups of 4 integers, leaving the last 2 out. */
+ int end = length - Vector.Count;
+
+ for (; i <= end; i += Vector.Count)
+ {
+ // Get the reference into a and b at the current position
+ ref int rai = ref Unsafe.Add(ref ra, i);
+ ref int rbi = ref Unsafe.Add(ref rb, i);
+
+ /* Reinterpret those references as Vector, which effectively
+ * means reading a Vector value starting from the memory
+ * locations those two references are pointing to.
+ * The JIT compiler will make sure that our Vector
+ * variables are stored in exactly one SIMD register each.
+ * Once we have them, we can multiply those together, which will
+ * use a single special SIMD instruction in assembly. */
+
+ // va = { a[i], a[i + 1], ..., a[i + Vector.Count - 1] }
+ Vector va = Unsafe.As>(ref rai);
+
+ // vb = { b[i], b[i + 1], ..., b[i + Vector.Count - 1] }
+ Vector vb = Unsafe.As>(ref rbi);
+
+ /* vr =
+ * {
+ * a[i] * b[i],
+ * a[i + 1] * b[i + 1],
+ * ...,
+ * a[i + Vector.Count - 1] * b[i + Vector.Count - 1]
+ * } */
+ Vector vr = va * vb;
+
+ // Get the reference into the target array
+ ref int rri = ref Unsafe.Add(ref rr, i);
+
+ // Store the resulting vector at the right position
+ Unsafe.As>(ref rri) = vr;
+ }
+
+
+ // Multiply the remaining values
+ for (; i < a.Length; i++)
+ {
+ result[i] = a[i] * b[i];
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/SandWorm/Utilities/KinectAzureController.cs b/SandWorm/Utilities/KinectAzureController.cs
new file mode 100644
index 0000000..d37484f
--- /dev/null
+++ b/SandWorm/Utilities/KinectAzureController.cs
@@ -0,0 +1,205 @@
+using System;
+using System.Numerics;
+using Microsoft.Azure.Kinect.Sensor;
+
+
+namespace SandWorm
+{
+
+ static class KinectAzureController
+ {
+ // Shared across devices
+ public static int depthHeight = 0;
+ public static int depthWidth = 0;
+ public static int colorHeight = 0;
+ public static int colorWidth = 0;
+ public static ushort[] depthFrameData = null;
+ public static byte[] colorFrameData = null;
+ public static Image colorForDepthImage = null;
+ public static Vector2[] idealXYCoordinates;
+
+ // Kinect for Azure specific
+ public static Device sensor;
+ private static DeviceConfiguration deviceConfig;
+ private static Calibration calibration;
+ private static Transformation transformation;
+
+ public const int depthWidthNear = 640; // Assuming low FPS mode
+ public const int depthHeightNear = 576;
+
+ public const int depthWidthWide = 1024; // Assuming low FPS mode
+ public const int depthHeightWide = 1024;
+
+ public static double previousSensorElevation;
+ public static Structs.KinectTypes previousKinectType;
+
+ public static Vector3?[] undistortMatrix;
+ public static double[] verticalTiltCorrectionMatrix;
+
+ public const double sin6 = 0.10452846326;
+
+
+ public static DepthMode GetDepthMode(Structs.KinectTypes type)
+ {
+ switch (type)
+ {
+ case Structs.KinectTypes.KinectAzureNear:
+ depthWidth = depthWidthNear;
+ depthHeight = depthHeightNear;
+ return DepthMode.NFOV_Unbinned;
+
+ case Structs.KinectTypes.KinectAzureWide:
+ depthWidth = depthWidthWide;
+ depthHeight = depthHeightWide;
+ return DepthMode.WFOV_Unbinned;
+
+ default:
+ throw new ArgumentException("Invalid Kinect Type provided", "original"); ;
+ }
+ }
+
+ public static double Calibrate()
+ {
+ int minX = 3 * (depthWidth / 2) - (3 * 10); // Multiply by 3 for each of the X,Y,Z coordinates
+ int maxX = 3 * (depthWidth / 2) + (3 * 10); // Multiply by 3 for each of the X,Y,Z coordinates
+ int minY = (depthHeight / 2) - 10;
+ int maxY = (depthHeight / 2) + 10;
+
+ double averagedSensorElevation = 0.0;
+ int counter = 0;
+
+ using (Capture capture = sensor.GetCapture())
+ {
+ if (capture.Depth == null)
+ return 0; // No depth data obtained
+
+ var pointCloud = transformation.DepthImageToPointCloud(capture.Depth);
+ var pointCloudBuffer = System.Runtime.InteropServices.MemoryMarshal.Cast(pointCloud.Memory.Span); // Cast byte values to short
+
+ for (int y = minY; y < maxY; y++) // Iterate over y dimension
+ {
+ for (int x = minX + 2; x < maxX; x += 3, counter++) // Iterate over x dimension. Increment by 3 to skip over the X,Y coordinates
+ {
+ int i = y * depthWidth * 3 + x;
+ averagedSensorElevation += pointCloudBuffer[i];
+ }
+ }
+ }
+ return Math.Round(averagedSensorElevation /= counter);
+ }
+
+ public static void RemoveRef()
+ {
+ sensor.Dispose();
+ sensor = null;
+ }
+
+ private static void CreateCameraConfig(Structs.KinectTypes fieldOfViewMode)
+ {
+ deviceConfig = new DeviceConfiguration
+ {
+ CameraFPS = FPS.FPS15,
+ DepthMode = GetDepthMode(fieldOfViewMode),
+ ColorFormat = ImageFormat.ColorBGRA32,
+ ColorResolution = ColorResolution.R1536p,
+ SynchronizedImagesOnly = true
+ };
+
+ if (fieldOfViewMode == Structs.KinectTypes.KinectAzureNear) // We can have 30 FPS in the narrow field of view
+ deviceConfig.CameraFPS = FPS.FPS30;
+ }
+
+ // Capture a single frame
+ public static void CaptureFrame(ref BGRA[] colorArray, int analysisType)
+ {
+ using (Capture capture = sensor.GetCapture())
+ {
+ if (capture.Depth != null)
+ depthFrameData = capture.Depth.GetPixels().ToArray();
+
+ if ((Structs.AnalysisTypes) analysisType == Structs.AnalysisTypes.Camera && capture.Color != null)
+ {
+ Image rgb = null;
+ if (deviceConfig.DepthMode == DepthMode.NFOV_Unbinned)
+ rgb = new Image(ImageFormat.ColorBGRA32, depthWidthNear, depthHeightNear, 2560);
+ else
+ rgb = new Image(ImageFormat.ColorBGRA32, depthWidthWide, depthHeightWide, 4096);
+
+ transformation.ColorImageToDepthCamera(capture, rgb);
+ colorArray = rgb.GetPixels().ToArray();
+ }
+ }
+ }
+
+
+ public static void SetupSensor(Structs.KinectTypes fieldOfViewMode, double sensorElevation)
+ {
+ if (sensor == null)
+ {
+ CreateCameraConfig(fieldOfViewMode); // Apply user options from Sandworm Options
+
+ try
+ {
+ sensor = Device.Open();
+ sensor.StartCameras(deviceConfig);
+ Rhino.RhinoApp.WriteLine("Start Kinect Cameras");
+
+ #region Speed up resetting procedure
+ // Calibration, transformation and lookup tables take approx 2 seconds to calculate (or 4 for WFOV). Only do this when necessary
+
+ if (previousKinectType == fieldOfViewMode && previousSensorElevation == sensorElevation)
+ return;
+
+ previousSensorElevation = sensorElevation;
+ previousKinectType = fieldOfViewMode;
+ #endregion
+
+ calibration = sensor.GetCalibration(deviceConfig.DepthMode, deviceConfig.ColorResolution);
+ transformation = calibration.CreateTransformation();
+ Rhino.RhinoApp.WriteLine("Create Transformation Matrices");
+
+ Vector2 depthPixel = new Vector2();
+ Vector3? translationVector;
+
+ // Lookup tables to correct for depth camera distortion in XY plane and its vertical tilt
+ undistortMatrix = new Vector3?[depthHeight * depthWidth];
+ verticalTiltCorrectionMatrix = new double[depthHeight * depthWidth];
+
+ for (int y = 0, i = 0; y < depthHeight - 0; y++)
+ {
+ depthPixel.Y = (float)y;
+ for (int x = 0; x < depthWidth - 0; x++, i++)
+ {
+ depthPixel.X = (float)x;
+
+ translationVector = calibration.TransformTo3D(depthPixel, 1f, CalibrationDeviceType.Depth, CalibrationDeviceType.Depth);
+ undistortMatrix[i] = translationVector;
+
+ if (translationVector != null)
+ verticalTiltCorrectionMatrix[i] = translationVector.Value.Y * sin6;
+ }
+ }
+ Rhino.RhinoApp.WriteLine("Correct for Vertical Tilt");
+
+ // Create synthetic depth values emulating our sensor elevation and obtain corresponding idealized XY coordinates
+ double syntheticDepthValue;
+ idealXYCoordinates = new Vector2[depthWidth * depthHeight];
+ for (int i = 0; i < depthWidth * depthHeight; i++)
+ {
+ if (undistortMatrix[i] != null)
+ {
+ syntheticDepthValue = sensorElevation / (1 - verticalTiltCorrectionMatrix[i]);
+ idealXYCoordinates[i] = new Vector2((float)Math.Round(syntheticDepthValue * undistortMatrix[i].Value.X, 1), (float)Math.Round(syntheticDepthValue * undistortMatrix[i].Value.Y, 1));
+ }
+ else
+ idealXYCoordinates[i] = new Vector2();
+ }
+ Rhino.RhinoApp.WriteLine("Calculate XY Coordinates");
+ }
+ catch (Exception)
+ {
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/Utilities/KinectForWindows.cs b/SandWorm/Utilities/KinectForWindows.cs
new file mode 100644
index 0000000..7b4bdd0
--- /dev/null
+++ b/SandWorm/Utilities/KinectForWindows.cs
@@ -0,0 +1,225 @@
+using System;
+using System.Numerics;
+using System.Windows.Media;
+using Microsoft.Kinect;
+
+
+namespace SandWorm
+{
+ static class KinectForWindows
+ {
+ // Shared across devices
+ public static int depthHeight = 424;
+ public static int depthWidth = 512;
+ public static int colorHeight = 0;
+ public static int colorWidth = 0;
+ public static ushort[] depthFrameData = null;
+ private static byte[] colorFrameData = null;
+ public static System.Drawing.Color[] rgbColorData = null;
+ public static Vector2[] idealXYCoordinates;
+
+ // Kinect for Windows specific
+ public static KinectSensor sensor = null;
+ public static MultiSourceFrameReader multiFrameReader = null;
+ public static FrameDescription depthFrameDescription = null;
+ public static FrameDescription colorFrameDescription = null;
+ public static int refc = 0;
+ public static int bytesForPixelColor = (PixelFormats.Bgr32.BitsPerPixel + 7) / 8;
+
+ public const double kinect2FOVForX = 70.6;
+ public const double kinect2FOVForY = 60.0;
+
+ public static void AddRef()
+ {
+ if (sensor == null)
+ {
+ Initialize();
+ }
+ if (sensor != null)
+ {
+ refc++;
+ }
+ }
+
+ public static void SetupSensor()
+ {
+ if (sensor == null)
+ {
+ AddRef();
+ }
+ }
+
+ public static void RemoveRef()
+ {
+ refc--;
+ if ((sensor != null) && (refc <= 0))
+ {
+ multiFrameReader.MultiSourceFrameArrived -= Reader_FrameArrived;
+ sensor.Close();
+ sensor = null;
+ }
+ }
+
+ public static void Initialize()
+ {
+ sensor = KinectSensor.GetDefault();
+ multiFrameReader = sensor.OpenMultiSourceFrameReader(FrameSourceTypes.Depth | FrameSourceTypes.Color);
+ multiFrameReader.MultiSourceFrameArrived += new EventHandler(Reader_FrameArrived);
+ sensor.Open();
+
+ ComputeXYCoordinates();
+ }
+
+ private static void ComputeXYCoordinates()
+ {
+ PointF[] intrinsicCoordinates = sensor.CoordinateMapper.GetDepthFrameToCameraSpaceTable();
+ idealXYCoordinates = new Vector2[depthWidth * depthHeight];
+ Vector2 _point = new Vector2();
+
+ for (int i = 0; i < intrinsicCoordinates.Length; i++)
+ {
+ _point.X = intrinsicCoordinates[i].X * (float)SandWormComponentUI._sensorElevation.Value;
+ _point.Y = intrinsicCoordinates[i].Y * (float)SandWormComponentUI._sensorElevation.Value;
+ idealXYCoordinates[i] = _point;
+ }
+ }
+
+ private static void Reader_FrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
+ {
+ if (e.FrameReference != null)
+ {
+ MultiSourceFrame multiFrame = e.FrameReference.AcquireFrame();
+
+ if (multiFrame.DepthFrameReference != null)
+ {
+ try
+ {
+ using (DepthFrame depthFrame = multiFrame.DepthFrameReference.AcquireFrame())
+ {
+ if (depthFrame != null)
+ {
+ using (KinectBuffer buffer = depthFrame.LockImageBuffer())
+ {
+ depthFrameDescription = depthFrame.FrameDescription;
+ depthWidth = depthFrameDescription.Width;
+ depthHeight = depthFrameDescription.Height;
+ depthFrameData = new ushort[depthWidth * depthHeight];
+ depthFrame.CopyFrameDataToArray(depthFrameData);
+ }
+ }
+ }
+ }
+ catch (Exception) { return; }
+ }
+
+ if ((Structs.AnalysisTypes)SandWormComponentUI._analysisType.Value == Structs.AnalysisTypes.Camera && multiFrame.ColorFrameReference != null)
+ {
+ try
+ {
+ using (ColorFrame colorFrame = multiFrame.ColorFrameReference.AcquireFrame())
+ {
+ if (colorFrame == null)
+ return;
+
+ colorFrameDescription = colorFrame.FrameDescription;
+ colorWidth = colorFrameDescription.Width;
+ colorHeight = colorFrameDescription.Height;
+ colorFrameData = new byte[colorWidth * colorHeight * bytesForPixelColor]; // 4 == bytes per color
+
+ using (KinectBuffer buffer = colorFrame.LockRawImageBuffer())
+ {
+ if (colorFrame.RawColorImageFormat == ColorImageFormat.Bgra)
+ {
+ colorFrame.CopyRawFrameDataToArray(colorFrameData);
+ }
+ else
+ {
+ colorFrame.CopyConvertedFrameDataToArray(colorFrameData, ColorImageFormat.Bgra);
+ }
+ }
+ ColorSpacePoint[] _colorPoints = new ColorSpacePoint[depthWidth * depthHeight];
+ sensor.CoordinateMapper.MapDepthFrameToColorSpace(depthFrameData, _colorPoints);
+
+ rgbColorData = new System.Drawing.Color[depthWidth * depthHeight];
+
+ for (int y = 0, j = 0; y < depthHeight; y++)
+ {
+ for (int x = 0; x < depthWidth; x++, j++)
+ {
+ int depthIndex = (y * depthWidth) + x;
+ ColorSpacePoint colorPoint = _colorPoints[depthIndex];
+
+ int colorX = (int)Math.Floor(colorPoint.X + 0.5);
+ int colorY = (int)Math.Floor(colorPoint.Y + 0.5);
+
+ if ((colorX >= 0) && (colorX < colorWidth) && (colorY >= 0) && (colorY < colorHeight))
+ {
+ int colorIndex = ((colorY * colorWidth) + colorX) * 4;
+ rgbColorData[j] = System.Drawing.Color.FromArgb(colorFrameData[colorIndex - 2], colorFrameData[colorIndex + 1], colorFrameData[colorIndex]);
+ }
+ else
+ {
+ rgbColorData[j] = new System.Drawing.Color();
+ }
+ }
+ }
+ }
+ }
+ catch (Exception) { return; }
+ }
+ }
+ }
+
+ public static KinectSensor Sensor
+ {
+ get
+ {
+ if (sensor == null)
+ {
+ Initialize();
+ }
+ return sensor;
+ }
+ set
+ {
+ sensor = value;
+ }
+ }
+
+ public static double Calibrate()
+ {
+ int minX = (depthWidth / 2) - 10;
+ int maxX = (depthWidth / 2) + 10;
+ int minY = (depthHeight / 2) - 10;
+ int maxY = (depthHeight / 2) + 10;
+
+ double averagedSensorElevation = 0.0;
+ int counter = 0;
+
+ using (DepthFrame depthFrame = multiFrameReader.AcquireLatestFrame().DepthFrameReference.AcquireFrame())
+ {
+ if (depthFrame == null)
+ return 0;
+
+ using (KinectBuffer buffer = depthFrame.LockImageBuffer())
+ {
+ depthFrameDescription = depthFrame.FrameDescription;
+ depthWidth = depthFrameDescription.Width;
+ depthHeight = depthFrameDescription.Height;
+ depthFrameData = new ushort[depthWidth * depthHeight];
+ depthFrame.CopyFrameDataToArray(depthFrameData);
+ }
+
+ for (int y = minY; y < maxY; y++) // Iterate over y dimension
+ {
+ for (int x = minX; x < maxX; x++, counter++) // Iterate over x dimension.
+ {
+ int i = y * depthWidth + x;
+ averagedSensorElevation += depthFrameData[i];
+ }
+ }
+ }
+ return Math.Round(averagedSensorElevation /= counter);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SandWorm/Utilities/Structs.cs b/SandWorm/Utilities/Structs.cs
new file mode 100644
index 0000000..91ba0c1
--- /dev/null
+++ b/SandWorm/Utilities/Structs.cs
@@ -0,0 +1,43 @@
+
+namespace SandWorm
+{
+ public static class Structs
+ {
+ public enum KinectTypes
+ {
+ KinectAzureNear,
+ KinectAzureWide,
+ KinectForWindows
+ }
+
+ public enum OutputTypes
+ {
+ Mesh,
+ PointCloud
+ }
+
+ public enum AnalysisTypes
+ {
+ None,
+ Camera,
+ Elevation,
+ Slope,
+ Aspect,
+ CutFill
+ }
+
+ public enum ColorPalettes
+ {
+ Custom,
+ CutFill,
+ Chile,
+ Desert,
+ Dune,
+ Europe,
+ Greyscale,
+ Ocean,
+ Turbo,
+ Viridis
+ }
+ }
+}
diff --git a/SandWorm/app.config b/SandWorm/app.config
new file mode 100644
index 0000000..04d8f80
--- /dev/null
+++ b/SandWorm/app.config
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SandWorm/packages.config b/SandWorm/packages.config
index fb8f6c6..ebb6aee 100644
--- a/SandWorm/packages.config
+++ b/SandWorm/packages.config
@@ -1,12 +1,23 @@
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SandwormBenchmarks/SandwormBenchmarks.csproj b/SandwormBenchmarks/SandwormBenchmarks.csproj
index 6e2498c..e80225d 100644
--- a/SandwormBenchmarks/SandwormBenchmarks.csproj
+++ b/SandwormBenchmarks/SandwormBenchmarks.csproj
@@ -39,7 +39,7 @@
..\packages\BenchmarkDotNet.0.11.3\lib\net46\BenchmarkDotNet.dll
- ..\packages\CommandLineParser.2.6.0\lib\net45\CommandLine.dll
+ ..\packages\CommandLineParser.2.6.0\lib\net461\CommandLine.dll
..\packages\Grasshopper.6.17.19235.15041\lib\net45\Grasshopper.dll
@@ -53,8 +53,8 @@
..\packages\Microsoft.DotNet.PlatformAbstractions.3.0.1\lib\net45\Microsoft.DotNet.PlatformAbstractions.dll
-
- ..\packages\Microsoft.Win32.Registry.4.6.0\lib\net46\Microsoft.Win32.Registry.dll
+
+ ..\packages\Microsoft.Win32.Registry.4.6.0\lib\net461\Microsoft.Win32.Registry.dll
..\packages\RhinoCommon.6.17.19235.15041\lib\net45\Rhino.UI.dll
@@ -67,79 +67,125 @@
..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll
- ..\packages\System.Buffers.4.5.0\lib\netstandard1.1\System.Buffers.dll
+ ..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll
- ..\packages\System.Collections.Immutable.1.6.0\lib\netstandard1.3\System.Collections.Immutable.dll
+ ..\packages\System.Collections.Immutable.1.6.0\lib\netstandard2.0\System.Collections.Immutable.dll
+
..\packages\System.Console.4.3.1\lib\net46\System.Console.dll
+ True
+ True
..\packages\System.Diagnostics.FileVersionInfo.4.3.0\lib\net46\System.Diagnostics.FileVersionInfo.dll
+ True
+ True
..\packages\System.Diagnostics.StackTrace.4.3.0\lib\net46\System.Diagnostics.StackTrace.dll
+ True
+ True
+
..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll
+ True
+ True
..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll
+ True
+ True
..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll
+ True
+ True
+
- ..\packages\System.Memory.4.5.3\lib\netstandard1.1\System.Memory.dll
+ ..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll
+
+
+
+ ..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll
- ..\packages\System.Reflection.Metadata.1.7.0\lib\portable-net45+win8\System.Reflection.Metadata.dll
+ ..\packages\System.Reflection.Metadata.1.7.0\lib\netstandard2.0\System.Reflection.Metadata.dll
- ..\packages\System.Runtime.CompilerServices.Unsafe.4.6.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll
+ ..\packages\System.Runtime.CompilerServices.Unsafe.4.6.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
+ True
+ True
+
+
+ ..\packages\System.Security.AccessControl.4.6.0\lib\net461\System.Security.AccessControl.dll
-
- ..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net46\System.Security.Cryptography.Algorithms.dll
+
+ ..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net461\System.Security.Cryptography.Algorithms.dll
+ True
+ True
..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll
+ True
+ True
..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll
+ True
+ True
-
- ..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net46\System.Security.Cryptography.X509Certificates.dll
+
+ ..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll
+ True
+ True
+
+
+ ..\packages\System.Security.Principal.Windows.4.6.0\lib\net461\System.Security.Principal.Windows.dll
-
- ..\packages\System.Text.Encoding.CodePages.4.6.0\lib\net46\System.Text.Encoding.CodePages.dll
+
+ ..\packages\System.Text.Encoding.CodePages.4.6.0\lib\net461\System.Text.Encoding.CodePages.dll
- ..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll
+ ..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll
..\packages\System.Threading.Thread.4.3.0\lib\net46\System.Threading.Thread.dll
+ True
+ True
- ..\packages\System.ValueTuple.4.5.0\lib\netstandard1.0\System.ValueTuple.dll
+ ..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll
-
+
+
..\packages\System.Xml.ReaderWriter.4.3.1\lib\net46\System.Xml.ReaderWriter.dll
+ True
+ True
..\packages\System.Xml.XmlDocument.4.3.0\lib\net46\System.Xml.XmlDocument.dll
+ True
+ True
..\packages\System.Xml.XPath.4.3.0\lib\net46\System.Xml.XPath.dll
+ True
+ True
..\packages\System.Xml.XPath.XDocument.4.3.0\lib\net46\System.Xml.XPath.XDocument.dll
+ True
+ True
..\packages\System.Xml.XPath.XmlDocument.4.3.0\lib\net46\System.Xml.XPath.XmlDocument.dll
diff --git a/SandwormBenchmarks/app.config b/SandwormBenchmarks/app.config
index 2bd9245..2ad3ecd 100644
--- a/SandwormBenchmarks/app.config
+++ b/SandwormBenchmarks/app.config
@@ -1,71 +1,83 @@
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
diff --git a/SandwormBenchmarks/packages.config b/SandwormBenchmarks/packages.config
index a81aadc..035d5d8 100644
--- a/SandwormBenchmarks/packages.config
+++ b/SandwormBenchmarks/packages.config
@@ -1,59 +1,62 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/Icons_Main.png b/assets/Icons_Main.png
new file mode 100644
index 0000000..860bb42
Binary files /dev/null and b/assets/Icons_Main.png differ
diff --git a/assets/icons_1.ai b/assets/icons_1.ai
new file mode 100644
index 0000000..94127be
--- /dev/null
+++ b/assets/icons_1.ai
@@ -0,0 +1,6637 @@
+%PDF-1.5
%����
+1 0 obj
<>/OCGs[14 0 R 80 0 R 114 0 R]>>/Pages 3 0 R/Type/Catalog>>
endobj
2 0 obj
<>stream
+
+
+
+
+ application/pdf
+
+
+ Icons
+
+
+ 2021-07-16T10:05:55+02:00
+ 2021-07-16T10:05:55+02:00
+ 2021-07-16T10:03:51+02:00
+ Adobe Illustrator CC 23.0 (Windows)
+
+
+
+ 256
+ 160
+ JPEG
+ /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA
AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK
DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f
Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAoAEAAwER
AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA
AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB
UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE
1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ
qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy
obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp
0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo
+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7
FXYq7FXYq7FXYq7FXYq7FXYq7FXYq0zKqlmIVVFWY7AAdziryLz5/wA5Q/ll5WaW1tLltf1OOqm3
0+jQqw7Pcn93/wAByI8MVTa18/6v5w/JG884eWaaXrMtjdz2kXwXJhntWcen8acXLelQck71pir4
l1r80/zI1vkup+ZtSuI2PIwfWZEir4+khVPwwoYzNPPO/OaRpX6cnJY+PU4qtVmRgykqymoYbEHF
U+0bz/550RgdJ8wahYgfsQ3MqIfmgbiencYq9Q8qf85dfmjpDImsfVtftRQMLiNYJuI/llgCCvu6
Nitvf/y9/wCcnfy382tFZ3c50DVpNha37KIWY9o7kUjP+z4knoMCXruKuxV2KuxV2KuxV2KuxV2K
uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVhf5m/m15R/LzSvretT872VSbHS4SDcTkbbKfspXq7bD
3O2KvjD80Pz689ef5pILq4OnaET+70e0YrEV7es+zTN/rfD4KMKHm+KvrD/nC3zek2ma75PuGq9s
41KzQ71ilAinHyVlj/4LAl8+/mx5Ofyf+YeuaBw4W9tcs9l720372DcbbRuAffChiOKuxV2KuxV2
KvX/AMof+ckPNvkaSHTdSZ9a8sKQpspWrNAvSttI3Sn++2+Hw41ritvtLyf5y8uecNDg1vQLxbux
m2JGzxuPtRyod0de4Pz6EHAlOsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVee/n
P+b+k/lt5c+tyKt1rd5yj0nTiac3A3kkpuIo6/F49B1qFXwV5n8z655n1u51vXLt7zUbtuUsr9AO
yIvRUUbKo2AwoSvFXYqzL8n/ADufJX5iaPrzsVs4pfR1AAVrazfu5dtqlVPIe4GKvff+cyPIqXml
6T57sFEn1YLY6jIhBBglJe2kqOwdmWtf2lwJL5QwodirsVdirsVdirM/yt/NPzH+XnmGPUtLlMll
KyDU9NY0iuYlO6nrxcAng9KqfaoKr778k+dNB85+XLXX9Dn9ayuRRkNBJFIv24pVBPF17j6RUEHA
lPcVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVU7m5t7W2lubiRYreBGlmlc0VUQcmYnwA
GKvzo/Nj8w7/AM++dr/XrhmFqWMOmWzE0htIyfTQDsT9pv8AKJwoYfirsVdirsVfaH5AeY9L/M38
nLzyTrrerdadAdMu1Jq7Wrqfqs617x8eIPZkB74Evkbzb5Z1Pyv5k1Hy/qacL3Tpmhk2IDAbpItf
2XQhl9jhQlGKuxV2KuxV2KuxV6j+QX5wXX5eealW7dn8s6myxarb7kRnolyg/mjr8X8y1HWlFX3v
BPDPDHPBIssEqh4pUIZGRhVWVhsQR0OBK/FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqxb8
0dE17Xfy917RtAdI9Wv7R4LcysUVg9BInIdC8fJQelTvtir86dX0fVNG1K40zVbWSy1C1bhcW0yl
XRuu4PtuD3woQeKuxV2KuxVnP5MfmNN5A8+2OtEsdNkP1XVol3L2spHMgVFWQgOvuMVe8/8AOW35
bwatotn+Y2hqs5t444tUkhoyy2klDBcAr9rgW4k/ykdlwJfJuFDsVdirsVdirsVdir7E/wCcRfzR
bWfL83knU5uWoaKnqaYzGrSWRNCn/PBmA/1WAH2cCX0PirsVdirsVdirsVdirsVdirsVdirsVdir
sVdirsVeAfn9+feufl95+8v6fpKxXVpHbPda1YSUpMk8nCNOYBaJ0ELMpH824I2xVM9V0L8q/wDn
Ifykuo2EwtNctE9NboKovbNzuIriOo9SImpG9DvxYGuKvkn8wvy181+QtabS9fteHKptbyOrW9wg
P24noK+6mjDuMKGLYq7FXYq7FX1x/wA4rfmPZeZfK93+W3mIrcSWsDrYRzdLjT5Bxkg9zFy/4A7b
LgS+fvzh/La9/L7ztd6LJyfT5P8ASNJuTv6lq5PCpoPjSnB/ceFMKGEYq7FXYq7FXYq7FWV/lV5t
m8pfmFoWuoxWO2ukW6A/at5v3U6/8i3NPfFX6QYEuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Ku
xV+eP56+Zv8AEf5seY9QRw9vHdGztiPs+laAQKV9m9Pl9OFBYz5V82eYfKmtQazoF7JY38B2kQ/C
61BKSKfhdGpurbYq+ufIf5w/l3+c2h/4R862UFtrU4C/UpTSKeSlBJZyk8kk/wAmvLwLCuBLxf8A
OX/nGrzH5IafV9EEmseVwSzSqtbm1Xr+/RRug/34op4hcKHi+KuxV2Kpn5Z8x6r5b1+x13SZfR1D
T5VmgfqCR1Vh3VhVWHcHFX2V500PQvz8/J621rRVVNct1aawVmHOG7QAT2cjbfC9KAmn7D9MCXxN
cW89tPJb3EbRTwu0csTgqyupoysD0IIwoU8VdirsVdirsVXRRvLIkUa8pHIVFHUkmgGKv1JwJY95
z/MHyd5MsBe+ZNTisI3r6MTEtNKR1EUSBnf6BQd8VeU6J/zlx5P1vzppnl+w0m7Sz1K4S0Gp3Txx
cJJTxQ+ivq1UuQKlxirKPzk/O3/lWN1pP1vQ31LTtUWUC7huBG0ckJXkhjaNgaq4IPMd/DFVHyd/
zk1+U3mZ0gOpNo17IQq22qKIASfCYF4evi4Ptir1OOSOSNZI2DxuAyOpBBBFQQR1BxVdirsVdirs
VdirsVdirsVdiqS+dvMMflvyhrOvSMF/RtnNcJy6GREJjX/ZPQYq/NCSR5JGkkYs7ks7HqSTUnCh
biraOyMHQlWUgqwNCCOhBxV9G/k5/wA5XX+liDQvPxfUNM2ji1sAvcwqdqTqKmZB/MPj/wBbAlmX
5kf842eTfPenf4r/AC4u7a0u7tTMsMLBtPuyTvx419B67Gg412Kg1OKvlPzJ5Y1/y1q02ka7Yy6f
qEB+OCYUqOzKwqrqezKSD2woU9B0DWfMGrW2kaNaSXupXbcILaIVZj1J32AA3JOwG5xV795f/wCc
KvNN1bpLrvmC00uR15GC3he8ZSf2WJe3Wo78SR88C09Y/Jr8kPNX5Y65ci38xQ6t5c1FP9Ns5Ld7
eRZkB9OWICSZeX7LfEKr7gYped/85X/kvIsz/mDoFuWSQhdftYl3VqUW7AXseknvRu7HFXy7hQ7F
XYq7FXYq9f8A+cavytu/OPnm21W5hP8Ah7QZUubyVh8Es0Z5w24rsSWozj+X5jFQ+rPzq/Nex/Lj
yk2pFVn1e8Jg0izbo81Kl3AIPpxjdqew74EvgjzN5n17zPrNxrOu3kl9qNy3KSaQ9B2VFFFRV6BV
FBhQl0M0sE0c0LFJYmDxuuxVlNQR8jir7Q/NuKL8z/8AnG+38zWqepe2tvDrCqi/ZltwY71PGiKZ
f+BGBL4swoZ9+XX53fmB5DljTSr83GlKayaRdky2zDvxUnlET4oR71xV9fflT/zkF5K/MBI7NJP0
V5hpV9IuWFXI6/V5aKso9qBv8mm+BL0/FXYq7FXYq7FXYq7FXYq8S/5y68yfov8AKk6bG4WbXLyG
2KftGGIm4cj25RIp+eKviHCh2KuxV2Ksy/Lj82POf5faj9Z0G7/0WRgbvTJqvazgfzJUUb/LUhve
mKvqjQfP35OfnvoyaFr9olprgUmPT7llS4jcjd7K5HHn8hQmnxJTAlO/yZ/IbTfy01TW72O7GpPf
mOKwuZE4TQ2y1Z42p8NWelSvXiNh0xV6tirsVdiryv8AMP8A5xu/Lbzk0l2LU6Lq71Jv9PCxh2Pe
WGnpv7kAMf5sVeAeaf8AnDz8ydNkZtCuLTXrap9MK4tJyB05JORGK+0pwop59dfkj+bltKYpPKWp
sw2JigaVdv8AKj5r+OK0vsPyL/N++nWCHynqKOxABni+rpU+LzGNB9JxWnrPkD/nDbXbqaO68736
afaAgtp1kwmuHAP2Wlp6UfzXngTT6l8t+WtC8taPBo+h2cdjp1sKRQRjuerMTVmY92Y1OKviL/nJ
/wA6z+ZPzV1C0Dk2Ggf7jbWPsHj3uGoO5m5D5AYUF5JirsVfW/8Azh15mttW8oa95K1DjMlpIZ4o
HJPO1vFKTJT+VXXf/XwJD5j87+WZ/K/m/WPL89S+mXUsCuRTnGrfu337OlGHzwoSTFV0ckkUiyRs
UkQhkdSQwYGoII6EYq+lvyZ/5ywurIwaF+YMj3VpURwa+AWmjHT/AEpRvIv+WBy8eXXAl9W2V9ZX
9pDeWU8d1aXCiSC4hYPG6NuGVlqCDiqtirsVdirsVdirsVfHv/OaHmcXnnLR/LsUgaLSbNriZR2n
u2+y3jSOFCP9bFBfO2FXYq7FXYq7FV0cjxuskbFHQhkdTQgjcEEYq+y/+cTPzO8yebNJ1jR/MN81
/c6N9XayuJt5mhmEgYPJ1fg0Y+Jt9+uBL37FXYq7FXYq7FXYq7FXYq7FX5f6xqEmpate6jKS0t7c
S3DsepaVy5J+k4UITFXYq9O/5xw83nyz+bWjySOUs9UY6Xd70BW6IEZJO1FnEbH5YqGdf85l+ThY
ecNM80QJSHWrcwXTDf8A0m0ooJ8OULIB/qnApfO+FXYq7FXsv/ONv5n+dNE87aR5WsZjd6HrF2kN
xp0xLJEHP7yeHujKtWNNmpv4gKH3IJIzIYwwMigMyVFQGJAJHgeJxSuxV2KuxV2KuxV4f+YH5Kfl
z+cVu/mzy3qyRazOqr+k7Z/Xt5TEgREuISaoyqoX4eLDuDir5W/MH8o/PXkK6Mevacy2ZbjBqcFZ
bSTegpKB8JNPsvRvbChhuKuxV2KuxV2Kvef+cNtVFr+Z97Yufh1DTJkQf8WRSxSD/hA+Kh9pYEux
V2KuxV2KuxV2KuxV2Kvy0wodirsVXRSSRSJLGxSRCGRhsQQagjFX2j+Z8a/mn/zjVb+Y4I/U1G3t
otWCovSe15RXqqBXYD1qfIYEvizCh2KuxV7Z/wA4iaEdR/NtL4gcNHsbi6qf5pALZQPek5P0YqE0
/wCchvzP8zaF+fM175d1F7S50S0trEGM1jdWX6y8cqH4JF5T7hh28RgS9m/J/wD5yW8r+dxDpWsm
PRfM70Rbd2pbXLHb/R5G6MT/ALrY8vAtir2XFXYq7FXmf/ORHn2Pyf8AljqUkUvDVNVU6dpqinLn
OpEkg3r+7i5Gv83HxxV8O+UfPHmvyfqQ1Hy5qU2n3OwkEZrHIoNeMsbVR19mGFD6a8g/85c+Wdat
ho/5haclk0y+lNfRIZ7KUHY+tAQ7oD7cx8hgSjPN/wDzi1+XPnGxOu+QNTj01rkF4RA4utNlPtxJ
aPfrxYgfy4q+cfPf5OfmH5Hdm13SpBYqaLqdv++tGrWn71R8BNPsuFb2woYVirsVdir1L/nGO+Fp
+dnl4saRz/WoG6neS0lCDb/L44qH3zgS7FXYq7FXYq7FXYq7FXYq/MnzXpkuleaNY0yUcZbG9ubZ
x4NDKyH/AIjhQlWKuxV2KvrT/nDXzXDf+Xdd8l3oWQWr/XLeNxUPb3I9KdKdOKuoP+zwJD5t/MPy
pP5S87az5dlBH6PunjhLVBaBjzgff+eJlb6cKGO4q7FX1b/zhLogWx80a68e8sttYwTeHpq8sqj5
+pGTgSHzt+ZWtjXPzB8x6sprHeajcyQ/8YvVYRj6EAwoY3ir2n8sP+cpPPHlFItO1kHzFokdFSO4
crdRIBQCKejVA/lcHwBXFbfSfk//AJyR/KbzMsUa6uNKvpSF+pakPq7BjSgEprCdzTZ8CWT+ffzL
8neRNPjvfMl99VFwH+qQKjSTTtGAWWNFB3+IbmgFdzir4Z/OP82tV/MnzP8ApG4Q2ul2imHStP5c
hFGTVmY9DJIQCx+Q7YUMCxV2Ksg8oef/ADj5OvfrflzVZ9PdiDLEjVhkp09SJuUb/wCyXFX0X5G/
5zJsbmJbDz5pHp8wUk1GwHOJlIp+9tnJYf5RVmr/AC4E2ynUfyZ/IH81LWXUvKl3BZ3zAu9xpDqn
Fm6GeycUTf8AyEY+OKvGfOv/ADiV+ZWhGWfRfS8x2CVIa2PpXPEH9q3kO59kdsKKeOanpOqaVeSW
WqWc1jeRGkltcxtFIp90cA4qy78jZvS/N7ym/MpXUYUqDT7Z4U28eVMVD9EsCXYq7FXYq7FXYq7F
XYq7FXwL/wA5L+Xjov5x64ApWHUTHqEJIpX6wgMh/wCRwcYUF5dirsVdir2L/nFWDzT/AMrYs7vR
rV57GGKSLW5AeMaWsykfGx2r6iqyr1JX54qH0H+b/wDzjVpH5h+Yv8QR6u+kag1ukEyrAJ45GiLc
JGBeM14kLsegGBL5s/Mf/nHL8xPJFvLqEsCatosW76hY8n9NfGaIgSIPFqFR/NhRTy3FX2l/zj0s
nlf/AJxxvdfFEkdNT1dTQVH1dGjUkHb/AI9a/LAl8XEkkkmpO5JwoaxV2KtqzKwZSVZTUEbEEYq+
tf8AnJpF80/kb5V84p8cyNaXMjqNhHf2/wC8Bp0/ehB+GBL5JwodirsVdirsVRGn6jqGnXcd5p9z
LZ3cR5RXEDtFIp8VdCGGKvY/Jf8Azln+ZugiODVmh8xWKUBW7Hp3PEHotxGASfeRXxW3sOm/85Jf
kd55tE0/zlpwsWII9HVLZbu3DN19OZFcqf8AKKpgSm+hfkH+S995g0vzh5NuzGdNuobyNNOu1urR
2iYOFcP6zLWnQOKeGKvaMVdirsVdirsVdirsVdirsVfL/wDzmr5TZ7fy/wCbIYyfSL6ZeuNwFas1
v79fV+8YqXyphQ7FXYq+6/8AnFXyxaaN+UdhfIg+ua3LNe3UlByIEhhiXl1KiOMEDsScCXsGKuIB
BBFQdiDir5u/Pb/nF+01SK48y+RLdbfVRWS90SMBYrgdWe3GwSTvw6N2oftKpx5ygbyn/wA4jpYy
KYLptIsoJYHHFxNeyRGdCrGtVMr1+WKvi3Ch2KuxV2Kvrv8ALlR52/5xK1bRCC1xpkF7bLT4i01o
4v7dadvtRrgS+RMKHYq7FXYq7FXYq7FXYq9Y/wCcXLSS5/OrQwC3pQpdTTKpKghLaThyoRsJCpxU
PvPAl2KuxV2KuxV2KuxV2KuxVJfOHk/QfN/l+50DXbf6xp11xLqrFHVkYOjo67qysP4HYkYq8A8z
f84U6TKXl8s+YZrbulrqESzL7j1ovSIH+wOKvMtd/wCcTPzh01n+qWlprESjkJLO5RSR/q3P1dq+
wHyrhRTBNV/Kn8zNKBa/8r6pDGv2pfqsrxj/AGaKy/jir7g/ICOSP8nfLEcqNHIls6vG6lWBE0gI
IahwJeg4q7FXYq8x/wCci/JPmLzh+Wd1pmgKJb6GeK7NqTRp44QxaNO3OpBUHrSmKvgSSOSKRo5F
KSISrowIYMDQgg9CMKFuKuxV2Kvp3/nCfzCBf+ZfLUnxCeGHUYFJ2HpN6M23+V6sf3YEh4F+YHlx
vLXnfXNCIomn3s0MJpSsQcmJqf5UZU4UMfxV2KuxV2KuxV2KuxV9Hf8AOFfl97jzbr2vsgMNhZJa
Ix7SXUgf4fcJbkH5++BQ+vcUuxV2KuxV2KuxV2KuxV2KuxV8df8AOQsPnj8tPzEk1fyvq19peieY
q3kUdtNIsAugf9IjaOvpklz6lCP2sVSLQ/8AnLj83tOot5NZawgAH+mWwRqD/Ktjb7/OuFFs30r/
AJzcu1ULq3lSOVu8tpdtGP8AkXJFJ/xPAm3vP5Ufmbp35jeV21+xtJLJI7mS0mtpWV2WSNUf7S9Q
VkGKsyxV2KuxV2Kvnn/nIr/nHVPMSXHm/wAoW4XzAoMmpabGKC9A3MkYH+7/ABH7f+t9pV8dujo7
I6lXUlWVhQgjYgg4UNYq7FXo/wDzjz5nHl783vL9zI3G2vJjp9xU0HG7UxLUnssjI30YqGZ/85i+
V2078x7XXUQiDXbNC8hGxuLSkLgH2i9L78Cl4LhV2KuxV2KuxV2KuxV96/8AOM3keTyr+Vli11H6
eo60x1O6UgBlWZQIEJ67RKrUPQk4EvVsVdirsVdirsVdirsVdirsVdirCPzj/Li2/MDyNe6I3FNQ
T/SdKuG/3XdRg8KnsrglG9jXtir88r+wvNPvrixvYWt7y1kaG4gcUZJEPFlI8QRhQoYq+sv+cJNV
L6R5p0ln2t7i1u44ye86PG7Af88Fr9GBIfTOKuxV2KuxV2KvBvz8/wCcb7Xzh63mTyskdr5nALXV
rskN9TepPRJv8ro37Xjir411DT77Tr2ewv7eS1vbZzHcW0ylJEdTQqytQgjChD4qvgmlgmjnhYpL
EweNx1VlNQR8jir6+/5yEt4PPv5A6N52tVVriyW21B+P7KXKiG5iH+pKy8v9TAl8fYUOxV2KuxV2
KuxV6n/zj3+U0/n/AM5xPdxH/DmkslxqshB4yUNY7YHxlI+LwWvemKvvgAAAAUA2AGBLsVdirsVd
irsVdirsVdirsVdirsVfNv8AzlH+RUurpL578s2xfUoUH6bsYhVp40FBcRqOrooo47qK9QaqvkbC
h9If84Tmb/FXmQCvofUYefhz9b4Pw5YEh9d4q7FXYq7FXYq7FXm/5tfkV5R/MW1M1yv6O1+NeNtr
ECgvQdEmT4fVT2JBHYjFXxf+Y35SedvIF+YNdsibNyRbapBWS1mH+TJQcW/yXAb2woYZir69/wCc
W9UtPOH5R695C1JuS2frW1NiVtNRRypUHusvqH22wJfJ2saVeaRq17pV6hjvLCeS2uEPaSFyjD71
woQeKuxV2KuxVn/5T/kx5r/MbVFjsIzaaNE1L7WJVPoxgblU6epJ4Kv00G+Kvu7yR5J0DyX5cttA
0OD0bO3+JnbeSWVqc5ZW25O1N/uFAAMCU+xV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV4t+Y//ADit
5E826g+qadNJ5e1KZuVybVFkt5WJJZzASnFz4owHtXfFWWflD+T2g/lno9zZ6fcSXt7fukmoX8wC
GQxgiNVRa8ETk1BUmpO+Ks9xV2KuxV2KuxV2KuxVQvrGxv7SWzvreK7s514TW06LJG6+DIwKsPni
rwjz7/ziB5K1lpLvyvdSeX7xqt9WINxZk06BGIkjqfByB2XFUh/JD8nvzS/LH8zEmv7SK98u6lDL
ZXt/ZTLIiV/ewyNE/pzD44wteFByOKsA/wCcs/I0ujfmU+t2sBFhr0C3TMingtxEBHOCQKVNFc/6
2KC8PwqmWm+WvMeqSLHpulXl9I9AiW1vLMxr0oEVq9cVZ/5b/wCcafzh11lI0RtMgY0M+pOtsF9z
GeU33R4rT3HyD/zhz5Y0ySK98337a1cJ8R0+3DQWgavRmr6sg/4D3BwJfQOnadp+m2UNjp1tFZ2V
uvCC2gRY40XrRUUADFURirsVdirsVdirsVdirsVdirsVdir/AP/Z
+
+
+
+ uuid:be42ae0d-9048-4379-9890-d1ad00257a7e
+ xmp.did:a7600040-1c06-e244-a1a2-ae7f205b1f92
+ uuid:5D20892493BFDB11914A8590D31508C8
+ proof:pdf
+
+ xmp.iid:cf5f886f-f7b0-9348-a4a3-c238d26d55d7
+ xmp.did:cf5f886f-f7b0-9348-a4a3-c238d26d55d7
+ uuid:5D20892493BFDB11914A8590D31508C8
+ proof:pdf
+
+
+
+
+ saved
+ xmp.iid:cc8fd989-c2ed-a247-be5a-9efa8651e362
+ 2020-09-18T14:08:10+02:00
+ Adobe Illustrator CC 23.0 (Windows)
+ /
+
+
+ saved
+ xmp.iid:a7600040-1c06-e244-a1a2-ae7f205b1f92
+ 2021-07-16T10:03:51+02:00
+ Adobe Illustrator CC 23.0 (Windows)
+ /
+
+
+
+ Print
+ Document
+ Adobe PDF library 15.00
+ 1
+ False
+ False
+
+ 24.000000
+ 24.000000
+ Points
+
+
+
+ Cyan
+ Magenta
+ Yellow
+ Black
+
+
+
+
+
+ Default Swatch Group
+ 0
+
+
+
+ White
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 0.000000
+
+
+ Black
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 100.000000
+
+
+ CMYK Red
+ CMYK
+ PROCESS
+ 0.000000
+ 100.000000
+ 100.000000
+ 0.000000
+
+
+ CMYK Yellow
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 100.000000
+ 0.000000
+
+
+ CMYK Green
+ CMYK
+ PROCESS
+ 100.000000
+ 0.000000
+ 100.000000
+ 0.000000
+
+
+ CMYK Cyan
+ CMYK
+ PROCESS
+ 100.000000
+ 0.000000
+ 0.000000
+ 0.000000
+
+
+ CMYK Blue
+ CMYK
+ PROCESS
+ 100.000000
+ 100.000000
+ 0.000000
+ 0.000000
+
+
+ CMYK Magenta
+ CMYK
+ PROCESS
+ 0.000000
+ 100.000000
+ 0.000000
+ 0.000000
+
+
+ C=15 M=100 Y=90 K=10
+ CMYK
+ PROCESS
+ 15.000000
+ 100.000000
+ 90.000000
+ 10.000000
+
+
+ C=0 M=90 Y=85 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 90.000000
+ 85.000000
+ 0.000000
+
+
+ C=0 M=80 Y=95 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 80.000000
+ 95.000000
+ 0.000000
+
+
+ C=0 M=50 Y=100 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 50.000000
+ 100.000000
+ 0.000000
+
+
+ C=0 M=35 Y=85 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 35.000000
+ 85.000000
+ 0.000000
+
+
+ C=5 M=0 Y=90 K=0
+ CMYK
+ PROCESS
+ 5.000000
+ 0.000000
+ 90.000000
+ 0.000000
+
+
+ C=20 M=0 Y=100 K=0
+ CMYK
+ PROCESS
+ 20.000000
+ 0.000000
+ 100.000000
+ 0.000000
+
+
+ C=50 M=0 Y=100 K=0
+ CMYK
+ PROCESS
+ 50.000000
+ 0.000000
+ 100.000000
+ 0.000000
+
+
+ C=75 M=0 Y=100 K=0
+ CMYK
+ PROCESS
+ 75.000000
+ 0.000000
+ 100.000000
+ 0.000000
+
+
+ C=85 M=10 Y=100 K=10
+ CMYK
+ PROCESS
+ 85.000000
+ 10.000000
+ 100.000000
+ 10.000000
+
+
+ C=90 M=30 Y=95 K=30
+ CMYK
+ PROCESS
+ 90.000000
+ 30.000000
+ 95.000000
+ 30.000000
+
+
+ C=75 M=0 Y=75 K=0
+ CMYK
+ PROCESS
+ 75.000000
+ 0.000000
+ 75.000000
+ 0.000000
+
+
+ C=80 M=10 Y=45 K=0
+ CMYK
+ PROCESS
+ 80.000000
+ 10.000000
+ 45.000000
+ 0.000000
+
+
+ C=70 M=15 Y=0 K=0
+ CMYK
+ PROCESS
+ 70.000000
+ 15.000000
+ 0.000000
+ 0.000000
+
+
+ C=85 M=50 Y=0 K=0
+ CMYK
+ PROCESS
+ 85.000000
+ 50.000000
+ 0.000000
+ 0.000000
+
+
+ C=100 M=95 Y=5 K=0
+ CMYK
+ PROCESS
+ 100.000000
+ 95.000000
+ 5.000000
+ 0.000000
+
+
+ C=100 M=100 Y=25 K=25
+ CMYK
+ PROCESS
+ 100.000000
+ 100.000000
+ 25.000000
+ 25.000000
+
+
+ C=75 M=100 Y=0 K=0
+ CMYK
+ PROCESS
+ 75.000000
+ 100.000000
+ 0.000000
+ 0.000000
+
+
+ C=50 M=100 Y=0 K=0
+ CMYK
+ PROCESS
+ 50.000000
+ 100.000000
+ 0.000000
+ 0.000000
+
+
+ C=35 M=100 Y=35 K=10
+ CMYK
+ PROCESS
+ 35.000000
+ 100.000000
+ 35.000000
+ 10.000000
+
+
+ C=10 M=100 Y=50 K=0
+ CMYK
+ PROCESS
+ 10.000000
+ 100.000000
+ 50.000000
+ 0.000000
+
+
+ C=0 M=95 Y=20 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 95.000000
+ 20.000000
+ 0.000000
+
+
+ C=25 M=25 Y=40 K=0
+ CMYK
+ PROCESS
+ 25.000000
+ 25.000000
+ 40.000000
+ 0.000000
+
+
+ C=40 M=45 Y=50 K=5
+ CMYK
+ PROCESS
+ 40.000000
+ 45.000000
+ 50.000000
+ 5.000000
+
+
+ C=50 M=50 Y=60 K=25
+ CMYK
+ PROCESS
+ 50.000000
+ 50.000000
+ 60.000000
+ 25.000000
+
+
+ C=55 M=60 Y=65 K=40
+ CMYK
+ PROCESS
+ 55.000000
+ 60.000000
+ 65.000000
+ 40.000000
+
+
+ C=25 M=40 Y=65 K=0
+ CMYK
+ PROCESS
+ 25.000000
+ 40.000000
+ 65.000000
+ 0.000000
+
+
+ C=30 M=50 Y=75 K=10
+ CMYK
+ PROCESS
+ 30.000000
+ 50.000000
+ 75.000000
+ 10.000000
+
+
+ C=35 M=60 Y=80 K=25
+ CMYK
+ PROCESS
+ 35.000000
+ 60.000000
+ 80.000000
+ 25.000000
+
+
+ C=40 M=65 Y=90 K=35
+ CMYK
+ PROCESS
+ 40.000000
+ 65.000000
+ 90.000000
+ 35.000000
+
+
+ C=40 M=70 Y=100 K=50
+ CMYK
+ PROCESS
+ 40.000000
+ 70.000000
+ 100.000000
+ 50.000000
+
+
+ C=50 M=70 Y=80 K=70
+ CMYK
+ PROCESS
+ 50.000000
+ 70.000000
+ 80.000000
+ 70.000000
+
+
+
+
+
+ Grays
+ 1
+
+
+
+ C=0 M=0 Y=0 K=100
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 100.000000
+
+
+ C=0 M=0 Y=0 K=90
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 89.999400
+
+
+ C=0 M=0 Y=0 K=80
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 79.998800
+
+
+ C=0 M=0 Y=0 K=70
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 69.999700
+
+
+ C=0 M=0 Y=0 K=60
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 59.999100
+
+
+ C=0 M=0 Y=0 K=50
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 50.000000
+
+
+ C=0 M=0 Y=0 K=40
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 39.999400
+
+
+ C=0 M=0 Y=0 K=30
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 29.998800
+
+
+ C=0 M=0 Y=0 K=20
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 19.999700
+
+
+ C=0 M=0 Y=0 K=10
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 9.999100
+
+
+ C=0 M=0 Y=0 K=5
+ CMYK
+ PROCESS
+ 0.000000
+ 0.000000
+ 0.000000
+ 4.998800
+
+
+
+
+
+ Brights
+ 1
+
+
+
+ C=0 M=100 Y=100 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 100.000000
+ 100.000000
+ 0.000000
+
+
+ C=0 M=75 Y=100 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 75.000000
+ 100.000000
+ 0.000000
+
+
+ C=0 M=10 Y=95 K=0
+ CMYK
+ PROCESS
+ 0.000000
+ 10.000000
+ 95.000000
+ 0.000000
+
+
+ C=85 M=10 Y=100 K=0
+ CMYK
+ PROCESS
+ 85.000000
+ 10.000000
+ 100.000000
+ 0.000000
+
+
+ C=100 M=90 Y=0 K=0
+ CMYK
+ PROCESS
+ 100.000000
+ 90.000000
+ 0.000000
+ 0.000000
+
+
+ C=60 M=90 Y=0 K=0
+ CMYK
+ PROCESS
+ 60.000000
+ 90.000000
+ 0.003100
+ 0.003100
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+endstream
endobj
3 0 obj
<>
endobj
16 0 obj
<>
endobj
5 0 obj
<>/Resources<>/ExtGState<>/Properties<>>>/Thumb 120 0 R/TrimBox[0.0 0.0 24.0 24.0]/Type/Page>>
endobj
116 0 obj
<>stream
+H�LUKn%7��)t�'�)�����,r���l<��������v�آ�b�(?��ܞ�>����s�^����{��?߯���������om����z�������N�����ڤ
�H�|��v���庍���Wh�e��tY�V:
]��t]�����uId����}�
!v��D�FðN�g
���t�f�y߰g��+DZ
+���PFν��!�:�ׯ�{΅�LC]��w�]�D��xl��*W�s�� �w8������!���xI�q2�W
+
+֪\3�U�djӞ�I_�Y-��7�X$3�Td��t��,�n�UZ{yu��`m$"7^|失�H�H�
>1y�nLTL �B�录�D�Pu�.�p`M���]�RUQ��-$c�( `���g�R >��Bd؇�Z���v��VG��.�ʢ|���@�|�y�Ԟ,���s3��<�%Ȯ�b3!����1�a)��3�;vN�Sp2u��ʸ#*I�!�^v��pu�;�c�(,�Jӏ�m+z�.�(g�P�4e�!d�8-�g��[�J��3
++W��RD�P}��m�7W�=JM[r����mOJ=6Q �D �����_�5��4'�@�I81t��M ���[�6t�P���`��8� }�� A6��0��_�RJ��{�KJ��jr��4O�@\�����Z���z�)���x&���6V
���.��.��Bh�Lt
��,��W��e�L�.�a���c�H�B�f�н�n��3��R��ڳ��QoJ2J=+/?7��N<��<��x��V��C�_?�oח��?���W� ��?/
+endstream
endobj
120 0 obj
<>stream
+8;Xq'^rQKWf)Yp]!!m`KYQ~>
+endstream
endobj
121 0 obj
[/Indexed/DeviceRGB 255 122 0 R]
endobj
122 0 obj
<>stream
+8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0
+b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup`
+E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn
+6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O(
+l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~>
+endstream
endobj
114 0 obj
<>
endobj
123 0 obj
[/View/Design]
endobj
124 0 obj
<>>>
endobj
119 0 obj
<>
endobj
118 0 obj
[/ICCBased 125 0 R]
endobj
125 0 obj
<>stream
+H���uT�K�tK���KJI,��t(�4�K���%ҹ�H�4J#�Ғ�(H
+w���q�y�y��~�3��̙g�<3�� �Y9El
+ @� ]��!O��-@� � �\����+BVKK
��:�OX�~�����WCa���iHKL ���0�q�Y��� `�5 �c����k��
+ X�] �x=����8���� �X ��Ŀ�>�.�f���#aP������n� �D^{y8����� �d�pH�� st��:Y����c xc
I�V�?S��!�:��_�����9[�YbQ�P�~�+rA
+S�h�����Hh���t^��
�'0�߅�kY�X��Y9�Yq�q�p��l�'���W����z�E����E$�%D>,��^|t*K)�%/�`���\�ҫ����:���&D����[�7��dplDa5�|�mb���4�,�y�y��{�e��5�
������������3���⚅,t+w��h�l�������A
��
� �mk
+��xYU���H�&%��Ȥ
+�q��O'M�z�3�K�T�@v[NUnn^\�o�]�a�b�Tr��t�l��mE]e~U�+�j�א�Z�:�z��a�q�i�����5����};�C�S��������[�\_�ۆw�����C�a��Q�1������;>�L$Lz}4��:%8M7�l̎��Χ/�}�XT^�]�X>\�Ym[���n�!�ycsk��kƶ�ʷ;��v{���p����I�s���0���X������ݯ�3�s��&�$��W�WW�*��)���!�$�$�%�!e$c��HNOAKIMEq�������������ƕ;KL�w�@��Y�X;ؚ��8^�+�Dsp����f���K�O��TC�P�p�J%���D=��+�+���O%$*������������8�I�Z�\�Z�^�U�K�_wL�������"d�x�����]�}����� �����>�9�=�;��s���_G�8/�̹N!G�z�[<�=��2�|B}����P�Q�z�l�H0Wc(E�e�n�|�Pds::5&8�9�y�F�T"o��d�䳔���i�/Z�K^&gd:fg����Q���l�
��kJХ�e����J�*�+��篍k�j5���U���[�ZU���h��0�|�e�m������6�]B�@�`�P�p�H�����������?QM1Msψ�*�iϛ.��Z[JY�Z)X-]��R�Ѻپ���w�������?��@��?��5� ǖ'v�N��g����
+��W������3�g����L�C#u!��M�M�M�E�vAms˔F�V�N���A���̝GL�w�A�̬,���l�l�ؿs�ݛ����n��ͽ����+���!B�²"�� 'R&k?���3�?�4+:�6o������T�\�ұڿ6���������V����ʝ�o���F?L����T;�:�>�::>�:�;��eq�vx^sa���wݥ��ʕ��'�_� E�F�O\D�K�LtAnF�F)F�|����ԭ6�\�`�@z?�m+F�;�L�wiA h�y���͖������)���M���g�w�~_
�@���ZH_XA,�"F)�%�/�*9���a�Z:�Q���,\�B^_AU2�
+�*����'[j��o5[����uR1u��h`f�m$1�xJgBdr�l�t�l�y�y�E�e$�fe�g-g#`�dGbwj��0���TOC9;��� �ܨ��ݿx�z6z�x��8��I����P��=A���!.��a�Axۑ ��ϊ����}�b�G�-����ޒē����x�`�G�/����Ԝ��������q�_�O�?�0�"��������۬խЮ˯ǰı²����µŶȷӹۺ������� �0�@�R�f�zƏǦȾ������*�G�cЀџҿ����'�L�sٛ������F�s�����M����6����+����1����M����Z�����:�{����� � � ��T�?�~ò��~i��~L}��~cbA�~�Da�d�����~t�y�~W����~O��>~\���/~���|�~���`���C ��x������}%��H}�1�X}%�z��}K���}��
{N}<_�~7��A�~��-ψ��|����|��Dz|+��E|[���s|���z}��^�}w�O@�}��-�~ċ�� {G�u��{D�z�{]�Ĭ�{��f�{�Zx�|[��]�|ϕM?�}R��<}Ǝ���z��]�Yz��Hħz����|z�=��{L�Nw�{���\�|=��>�|��v|ېI�8z/�r�
z�;�bz'�s�Mzd�6��zɬqv�{D�[�{��0>|;�|���y�����y��a�Iy��?y�a��zY��vzݮ�[{^��=c{Ф�I{R��*y�߄��yf���Uy`��Vy���y���uKz��Zi{��<�{z�%�zȎ���~+�~� }��͇}W���0}3���}HtЄ�}�Zk��}�=���~���z��ɇ��� ���}�����!�~����Єd�*s}��Y���<9������w����p���S�w�u�����������u�VrU���W��؈|;,�뇔��{���R�s��Ѳ��������;����:�8����q)�P�CV���:4�.��8�����Ȅ���2������?�U�����p����V�u��9S��������c��b�փ����R�����.�ՁN��n�� ��U��38���8���A�/����ͬ�������δz��6�߆�ө�n1����T\�e��7݀t�XT����)�$̯̕�6�;���e�C��ʷˆ �i��mw�3��Sƀ�V7M�
+�\��l�G�N��ػ�ځ��N�ā�a�5�t��N��zl�ߴ�S<��H6���*���<�a|��k�z|C�V��|�Ǎ|����|Ik���|�Ru��}26��'~2����F�-����B���*���o������������j�����Q^���@5z�I�`����ٳ� �$ĝ�"�����c�ߘ���&�U����ij���|PJ�ˇv4v�y���V���G����.�2�{������������萾hS�掼O[���3���(����J��ьx�&�$�Ԟs�҉h�
+~�2��gK�>�-N}�o��2ن��
N�%��է�������� ���>�w���֣A}⇤�\fX����M��ݘ�2,������K��Ԑ�3�����g���°����[��}
+�0�e��6��M�_��1���?���1ӣ���Ǿ���I��^�����I|B�̯d��ܪwL���e1$���:�������r�W������]�
��1���S{��z�|di����L
���g0��\��U��������{[���G{!����{ �ޔ`{&y�E{xbi�e{�Jr��|�/c�5}~
+~���:����f�#���������M�Kx+�C��a|�u��I~����.y�W�� ώ���әߎ���%�¡唘����[��w!�^�T`����^H��*��-����� 5�G��Ȩ�瘎��=�Π����4���r��v����_�ҍRG��f��,ދ̋������|�,���ƕ����{������Ҙ�t�ٕ�^��1��F�ő�,;�'��<�!���_���������Y�l�~�J���J��t�S�#]����'FA�4��+����;ۊL�P�f�&��ɔ��ޝ������ը�s@�夏]�0�E�����+:���ևSS 𧠨��jVPp,<XQ)�XVl�(Js�&�J!����K{�@B��E�.�e���WqQ䆓���~3��7���F�HI^�\�������ӡ���+j&Y���b�dML���{#�����)F���A�;q��T0��_�
^��9Iݤ�xֺ�ƢYFh�9Յ�hX;J���l�����n+j�ġ�(�m,ּJ�����(Va�V/���aؑ�@�Q~.�8_]���7]���������g�|�F�)-�/��MH�|����P��8�%�oFx��C��<��R�l�A�^����Q�l��z�rJAU�U����d*����N�$i�
#�v
b7F�*4ނ���
+
���pv�i�x֖�����I��o�����J۱�#�
+���W��h�ԉ�l�"�kxwVjG�Dz���*��ߜx�W���:Ʃd��P���$�����j�4W�M��[��,]�ʫ$���W��$I�&��N���}�7¨
s������1@�9�TɎ.�E��zN�Df��*uwr ���� �*�#����,�h��"�8=���N�
�%�=(�p���Qu�9s�Lӓ(�&��w�ھ�?��ER�AHi.R�~ Ԅ�6�����:�*tД3*���۱�x#�m?r~3��2�+�w����p���6�yXa�TO�#��
+U�¼<�_)�I�(��}��W2S�X U��w����
M��f�_T�)�����?k��YYF��K8��u;J$��þ*1T� >,��#h�%T�,�Qۥ{[��s�:��9�&^!Փ�a��@�!��"y �
+��.��Jl6m�Hj�u�,b�U6�+s���hܸd�-ʥ�}�wi� �-s�un=0�Ľ�i-_�*)U��_ˈ����b$��na+;ϧT�;p�pA7����C�4��.*�Iߥ�a�8��M��m�.���ACi���7�\j|fi������ԫ)��]ޭ�j����ʄ���U�]�3(í��wh�J��c�h-�4x7���h*P0�H됎L����랇ڡu��Â������,�{�Bz}��8�v�g�g�Ҳ�d[�!XTZ�Z.��vl�A���g��
+{;S�m�`v��ؿ`~�?g�a.�
+3��{����L�^�������W�����Y�e�4��]�L�