From f57cebcfc178796f49df3d17489ef8d2be86ca8b Mon Sep 17 00:00:00 2001 From: Philip Belesky Date: Tue, 17 Sep 2019 18:51:19 +1000 Subject: [PATCH 1/9] Remove TestA/TestB code from Tests Keeping this in means we will need to keep it updated with the equivalent methods in the component, which is unnecessary churn. Better to use these methods adhoc and not in git --- SandwormBenchmarks/MockMesh.cs | 1 - SandwormBenchmarks/TestMeshColoring.cs | 46 +++----------------------- 2 files changed, 4 insertions(+), 43 deletions(-) diff --git a/SandwormBenchmarks/MockMesh.cs b/SandwormBenchmarks/MockMesh.cs index 08671c5..f841251 100644 --- a/SandwormBenchmarks/MockMesh.cs +++ b/SandwormBenchmarks/MockMesh.cs @@ -29,7 +29,6 @@ static MockMesh() { averagedDepthFrameData[i] = randNum.Next(800, 1000); } - Analysis.AnalysisManager.ComputeLookupTables(sensorElevation, waterLevel); } } } diff --git a/SandwormBenchmarks/TestMeshColoring.cs b/SandwormBenchmarks/TestMeshColoring.cs index 3c37381..3d8c3ad 100644 --- a/SandwormBenchmarks/TestMeshColoring.cs +++ b/SandwormBenchmarks/TestMeshColoring.cs @@ -9,16 +9,16 @@ namespace SandwormBenchmarks { public class RunColorBenchmarks { - public static void Main(string[] args) + public static void Main() { - var summary = BenchmarkRunner.Run(); + BenchmarkRunner.Run(); MeshColorBenchmarks.TestA(); MeshColorBenchmarks.TestB(); // Prevent console from auto exiting Console.Write("Finished Benchmarks. Press any key to exit."); - var wait = Console.ReadLine(); + Console.ReadLine(); } } public class MeshColorBenchmarks : MockMesh @@ -26,49 +26,11 @@ public class MeshColorBenchmarks : MockMesh [Benchmark(Baseline = true)] public static void TestA() { - var pointCloud = new Point3d[trimmedWidth * trimmedHeight]; - vertexColors = new Color[trimmedWidth * trimmedHeight]; - var arrayIndex = 0; - for (var rows = 0; rows < trimmedHeight; rows++) - for (var columns = 0; columns < trimmedWidth; columns++) - { - tempPoint.X = columns * -unitsMultiplier * depthPixelSize.x; - tempPoint.Y = rows * -unitsMultiplier * depthPixelSize.y; - - depthPoint = averagedDepthFrameData[arrayIndex]; - tempPoint.Z = (depthPoint - sensorElevation) * -unitsMultiplier; - - pointCloud[arrayIndex] = tempPoint; - - if (vertexColors.Length > 0) // Proxy for whether a mesh-coloring visualisation has been enabled - vertexColors[arrayIndex] = Analysis.AnalysisManager.GetPixelColor((int) depthPoint); - - arrayIndex++; - } } [Benchmark] public static void TestB() { - var pointCloud = new Point3d[trimmedWidth * trimmedHeight]; - vertexColors = new Color[trimmedWidth * trimmedHeight]; - var arrayIndex = 0; - for (var rows = 0; rows < trimmedHeight; rows++) - for (var columns = 0; columns < trimmedWidth; columns++) - { - tempPoint.X = columns * -unitsMultiplier * depthPixelSize.x; - tempPoint.Y = rows * -unitsMultiplier * depthPixelSize.y; - - depthPoint = averagedDepthFrameData[arrayIndex]; - tempPoint.Z = (depthPoint - sensorElevation) * -unitsMultiplier; - - pointCloud[arrayIndex] = tempPoint; - - if (vertexColors.Length > 0) // Proxy for whether a mesh-coloring visualisation has been enabled - vertexColors[arrayIndex] = Analysis.AnalysisManager.GetPixelColor((int) depthPoint); - - arrayIndex++; - } } } -} \ No newline at end of file +} From 95fd4b11f836f4dab8a9b7fcd54e0ff208385f5c Mon Sep 17 00:00:00 2001 From: Philip Belesky Date: Tue, 17 Sep 2019 18:53:24 +1000 Subject: [PATCH 2/9] Implement first-stage of optimisations for the pixel color analysis; closes #29 This includes earlier optimisations that were not present (e.g. the color table as a dictionary) and reworks the way analysis is applied so that #33 can be implemented. Future tweaks will be benchmarked and sent in PRs --- SandWorm/Analysis.cs | 264 ++++++++++++++++------------------ SandWorm/SandWormComponent.cs | 61 ++++---- 2 files changed, 156 insertions(+), 169 deletions(-) diff --git a/SandWorm/Analysis.cs b/SandWorm/Analysis.cs index 4f765af..c66b1ca 100644 --- a/SandWorm/Analysis.cs +++ b/SandWorm/Analysis.cs @@ -1,10 +1,10 @@ using System; -using System.Drawing; using System.Collections.Generic; +using System.Drawing; +using System.Windows.Forms; +using System.Windows.Media.Media3D; using Rhino.Display; using Rhino.Geometry; -using Grasshopper.Kernel; -using System.Windows.Forms; namespace SandWorm { @@ -12,71 +12,55 @@ public static class Analysis { public static class AnalysisManager { - /// Stories copies of each analysis option and intefaces their use with components. + /// Stories copies of each analysis option and interfaces their use with components. public static List options; - public static List enabledOptions; - public static List enabledMeshVisualisationOptions; - static AnalysisManager() + static AnalysisManager() // Note that the order of items here determines their menu order { - // Note their order in array determines their priority; i.e. which color 'wins' - // Also needs to manually arrange the exclusive options together - options = new List { - new Analysis.Water(), new Analysis.Contours(), - new Analysis.None(), - new Analysis.Elevation(), new Analysis.Slope(), new Analysis.Aspect(), + options = new List + { + new Water(), new Contours(), + new None(), + new Elevation(), new Slope(), new Aspect() }; // Default to showing elevation analysis options[3].IsEnabled = true; - SetEnabledLookups(); } - private static void SetEnabledLookups() + public static List GetEnabledAnalyses() => options.FindAll(x => x.IsEnabled); + + public static MeshColorAnalysis GetEnabledMeshColoring() { - // Store the enabled analysis options to prevent recaclulating them during GetPixelColor() calls - enabledOptions = options.FindAll(x => x.IsEnabled); - enabledMeshVisualisationOptions = new List(); - foreach (MeshAnalysis enabledOption in enabledOptions) // Store analysis types that can color pixels + foreach (var enabledOption in GetEnabledAnalyses()) { - Type optionType = enabledOption.GetType(); - bool optionTest = optionType.IsSubclassOf(typeof(MeshAnalysisWithMeshGradient)); - if (enabledOption.GetType().IsSubclassOf(typeof(MeshAnalysisWithMeshGradient))) - enabledMeshVisualisationOptions.Add(enabledOption as MeshAnalysisWithMeshGradient); + var optionType = enabledOption.GetType(); + var optionTest = optionType.IsSubclassOf(typeof(MeshColorAnalysis)); + if (enabledOption.GetType().IsSubclassOf(typeof(MeshColorAnalysis))) + return enabledOption as MeshColorAnalysis; } + return null; // Shouldn't happen; a mesh color (even no color) is always set } public static void SetEnabledOptions(ToolStripMenuItem selectedMenuItem) { - MeshAnalysis selectedOption = options.Find(x => x.MenuItem == selectedMenuItem); + var selectedOption = options.Find(x => x.MenuItem == selectedMenuItem); if (selectedOption.IsExclusive) - foreach (MeshAnalysis exclusiveOption in options.FindAll(x => x.IsExclusive)) - exclusiveOption.IsEnabled = selectedOption == exclusiveOption; // Toggle selected item; untoggle other exclusive items + 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 - SetEnabledLookups(); } - public static void ComputeLookupTables(double sensorElevation, double waterLevel) + public static void ComputeLookupTables(double sensorElevation) { - foreach (MeshAnalysisWithMeshGradient option in enabledMeshVisualisationOptions) - option.ComputeLookupTableForAnalysis((int)sensorElevation, (int)waterLevel); - } - - public static Color GetPixelColor(int depthPoint) // Get color for pixel given enabled options - { - foreach (Analysis.MeshAnalysisWithMeshGradient option in enabledMeshVisualisationOptions) - { - Color? pixelColor = option.GetPixelColorForAnalysis(depthPoint); - if (pixelColor.HasValue) - return pixelColor.Value; - } - return Color.Transparent; // Fallback - shouldn't happen + GetEnabledMeshColoring().ComputeLookupTableForAnalysis(sensorElevation); } } - public class VisualisationRangeWithColor + public class VisualisationRangeWithColor { - /// Describes a numeric range (e.g. elevation/slope values) and color range to visualise it. + /// Describes a numeric range (e.g. elevation or slope values) and color range to visualise it. public int ValueStart { get; set; } public int ValueEnd { get; set; } public ColorHSL ColorStart { get; set; } @@ -84,176 +68,182 @@ public class VisualisationRangeWithColor public ColorHSL InterpolateColor(double progress) // Progress is assumed to be a % value of 0.0 - 1.0 { - return new ColorHSL( - ColorStart.H + ((ColorEnd.H - ColorStart.H) * progress), - ColorStart.S + ((ColorEnd.S - ColorStart.S) * progress), - ColorStart.L + ((ColorEnd.L - ColorStart.L) * progress) + return new ColorHSL( + ColorStart.H + (ColorEnd.H - ColorStart.H) * progress, + ColorStart.S + (ColorEnd.S - ColorStart.S) * progress, + ColorStart.L + (ColorEnd.L - ColorStart.L) * progress ); } } - public abstract class MeshAnalysis + public abstract class MeshAnalysis { - /// Inherited by all possible analysis options (even if not coloring the mesh). - public string Name { get; } // Name used in the toggle menu - public bool IsEnabled = false; // Whether to apply the analysis - public bool IsExclusive { get; set; } // Whether the analysis can be applied independent of other options - public ToolStripMenuItem MenuItem { get; set; } - public Dictionary lookupTable; // Dictionary of integers that map to color values + /// Some form of analysis that applies, or derives from, the mesh. + + public bool IsEnabled; // Whether to apply the analysis public MeshAnalysis(string menuName, bool exclusive) { Name = menuName; - IsExclusive = exclusive; - } + IsExclusive = exclusive; // Any analysis that applies to the mesh as a whole is mutually exclusive + } + + /// Inherited by all possible analysis options (even if not coloring the mesh). + public string Name { get; } // Name used in the toggle menu + + public bool IsExclusive { get; set; } // Whether the analysis can be applied independent of other options + public ToolStripMenuItem MenuItem { get; set; } + } + + public abstract class MeshGeometryAnalysis : MeshAnalysis + { + /// A form of analysis that outputs geometry (i.e. contours) based on the mesh + public MeshGeometryAnalysis(string menuName) : base(menuName, false) { } // Note: not mutually exclusive } - public abstract class MeshAnalysisWithMeshGradient: MeshAnalysis + public abstract class MeshColorAnalysis : MeshAnalysis { - /// Inherited by analysis options that color the entire mesh (and are thus mutually exclusive). - public abstract Color? GetPixelColorForAnalysis(int elevation); + /// A form of analysis that colors the vertices of the entire mesh + public Color[] lookupTable; // Dictionary of integers that map to color values + + public MeshColorAnalysis(string menuName) : base(menuName, true) { } // Note: is mutually exclusive + + public abstract int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts); - public MeshAnalysisWithMeshGradient(string menuName, bool exclusive) : base(menuName, exclusive) { } + public abstract void ComputeLookupTableForAnalysis(double sensorElevation); - public abstract void ComputeLookupTableForAnalysis(int sensorElevation, int waterLevel); public void ComputeLinearRanges(params VisualisationRangeWithColor[] lookUpRanges) { - int lookupTableMaximumSize = 0; - foreach (VisualisationRangeWithColor range in lookUpRanges) - { - lookupTableMaximumSize += range.ValueEnd - range.ValueStart; - } - - if (lookupTableMaximumSize == 0) - return; // Can occur e.g. if the waterLevel is greater than the sensor height - else - lookupTable = new Dictionary(lookupTableMaximumSize); // Init dict with needed size + var lookupTableMaximumSize = 1; + foreach (var range in lookUpRanges) lookupTableMaximumSize += range.ValueEnd - range.ValueStart; + lookupTable = new Color[lookupTableMaximumSize]; // Populate dict values by interpolating colors within each of the lookup ranges - foreach (VisualisationRangeWithColor range in lookUpRanges) - { - for (int i = range.ValueStart; i < range.ValueEnd; i++) + foreach (var range in lookUpRanges) + for (var i = range.ValueStart; i < range.ValueEnd; i++) { - double progress = ((double)i - range.ValueStart) / (range.ValueEnd - range.ValueStart); + var progress = ((double)i - range.ValueStart) / (range.ValueEnd - range.ValueStart); lookupTable[i] = range.InterpolateColor(progress); } - } } } - public class None : MeshAnalysis - { - public None() : base("No Visualisation", true) { } - } - - public class Water : MeshAnalysisWithMeshGradient + public class None : MeshColorAnalysis { - public Water() : base("Show Water Level", false) { } + public None() : base("No Visualisation") { } - public override Color? GetPixelColorForAnalysis(int elevation) + public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts) { - if (lookupTable.ContainsKey(elevation)) - return lookupTable[elevation]; // If the elevation is within the water level - else - return null; + return 0; // Should never be called (see below) } - public override void ComputeLookupTableForAnalysis(int sensorElevation, int waterLevel) + public override void ComputeLookupTableForAnalysis(double sensorElevation) { - VisualisationRangeWithColor waterRange = new VisualisationRangeWithColor - { - // From the sensor's perspective water is between specified level and max height (i.e. upside down) - ValueStart = sensorElevation - waterLevel, - ValueEnd = sensorElevation, - ColorStart = new ColorHSL(0.55, 0.85, 0.25), - ColorEnd = new ColorHSL(0.61, 0.65, 0.65) - }; - ComputeLinearRanges(new VisualisationRangeWithColor[] { waterRange }); + lookupTable = new Color[0]; // Empty color table allows pixel loop to skip lookup } } - public class Elevation : MeshAnalysisWithMeshGradient + public class Elevation : MeshColorAnalysis { - public Elevation() : base("Visualise Elevation", true) { } + public Elevation() : base("Visualise Elevation") { } - public override Color? GetPixelColorForAnalysis(int elevation) + public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts) { - if (lookupTable.ContainsKey(elevation)) - return lookupTable[elevation]; - else - return null; + return (int)vertex.Z; } - public override void ComputeLookupTableForAnalysis(int sensorElevation, int waterLevel) + public override void ComputeLookupTableForAnalysis(double sensorElevation) { - VisualisationRangeWithColor elevationRange = new VisualisationRangeWithColor + var normalElevationRange = new VisualisationRangeWithColor { - ValueStart = sensorElevation - 750, // TODO: don't assume maximum value here - ValueEnd = sensorElevation, - ColorStart = new ColorHSL(0.00, 0.25, 0.05), - ColorEnd = new ColorHSL(0.50, 0.85, 0.75) - }; - ComputeLinearRanges(new VisualisationRangeWithColor[] { elevationRange }); + ValueStart = 0, + ValueEnd = (int)sensorElevation - 201, + ColorStart = new ColorHSL(0.20, 0.35, 0.02), + ColorEnd = new ColorHSL(0.50, 0.85, 0.85) + }; // A clear gradient for pixels inside the expected normal model height + var extraElevationRange = new VisualisationRangeWithColor + { + ValueStart = (int)sensorElevation - 200, + ValueEnd = (int)sensorElevation + 1, + ColorStart = new ColorHSL(1.00, 0.85, 0.76), + ColorEnd = new ColorHSL(0.50, 0.85, 0.99) + }; // A fallback gradiend for those outside (TODO: set sensible colors here) + ComputeLinearRanges(normalElevationRange, extraElevationRange); } } - - class Slope : MeshAnalysisWithMeshGradient - { - public Slope() : base("Visualise Slope", true) { } - public override Color? GetPixelColorForAnalysis(int slopeValue) - { - return Color.Red; // TODO: implement slope analysis + private class Slope : MeshColorAnalysis + { + public Slope() : base("Visualise Slope") { } + + public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] neighbours) + { + return 22; // TODO: benchmark different methods for passing pixels before enabling a real calculation + // Loop over the neighbouring pixels; calculate slopes relative to vertex + //double slopeSum = 0; + //for (int i = 0; i < neighbours.Length; i++) + //{ + // double rise = vertex.Z - neighbours[i].Z; + // double run = Math.Sqrt(Math.Pow(vertex.X - neighbours[i].X, 2) + Math.Pow(vertex.Y - neighbours[i].Y, 2)); + // slopeSum += rise / run; + //} + //double slopeAverage = Math.Abs(slopeSum / neighbours.Length); + //double slopeAsPercent = slopeAverage * 100; // Array is keyed as 0 - 100 + //return (int)slopeAsPercent; // Cast to int as its cross-referenced to the lookup } - public override void ComputeLookupTableForAnalysis(int sensorElevation, int waterLevel) + public override void ComputeLookupTableForAnalysis(double sensorElevation) { - VisualisationRangeWithColor slopeRange = new VisualisationRangeWithColor + var slopeRange = new VisualisationRangeWithColor { - ValueStart = 0, - ValueEnd = 90, + ValueStart = 0, + ValueEnd = 100, ColorStart = new ColorHSL(1.0, 1.0, 1.0), // White ColorEnd = new ColorHSL(1.0, 1.0, 0.3) // Dark Red }; - ComputeLinearRanges(new VisualisationRangeWithColor[] { slopeRange }); + ComputeLinearRanges(slopeRange); } } - class Aspect : MeshAnalysisWithMeshGradient + private class Aspect : MeshColorAnalysis { - public Aspect() : base("Visualise Aspect", true) { } + public Aspect() : base("Visualise Aspect") { } - public override Color? GetPixelColorForAnalysis(int aspectValue) + public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts) { - return Color.Yellow; // TODO: implement aspect analysis + return 44; } - public override void ComputeLookupTableForAnalysis(int sensorElevation, int waterLevel) + public override void ComputeLookupTableForAnalysis(double sensorElevation) { - VisualisationRangeWithColor rightAspect = new VisualisationRangeWithColor + var rightAspect = new VisualisationRangeWithColor { - ValueStart = 0, + ValueStart = 0, ValueEnd = 180, ColorStart = new ColorHSL(1.0, 1.0, 1.0), // White ColorEnd = new ColorHSL(1.0, 1.0, 0.3) // Dark Red }; - VisualisationRangeWithColor leftAspect = new VisualisationRangeWithColor + var leftAspect = new VisualisationRangeWithColor { ValueStart = 180, // For the other side of the aspect we loop back to the 0 value ValueEnd = 359, ColorStart = new ColorHSL(1.0, 1.0, 0.3), // Dark Red ColorEnd = new ColorHSL(1.0, 1.0, 1.0) // White }; - ComputeLinearRanges(new VisualisationRangeWithColor[] { rightAspect, leftAspect }); + ComputeLinearRanges(rightAspect, leftAspect); } } - public class Contours : MeshAnalysis + public class Contours : MeshGeometryAnalysis { - public Contours() : base("Show Contour Lines", false) { } + public Contours() : base("Show Contour Lines") { } } + public class Water : MeshGeometryAnalysis + { + public Water() : base("Show Water Level") { } + } } -} +} \ No newline at end of file diff --git a/SandWorm/SandWormComponent.cs b/SandWorm/SandWormComponent.cs index d361707..3f74a1c 100644 --- a/SandWorm/SandWormComponent.cs +++ b/SandWorm/SandWormComponent.cs @@ -7,6 +7,7 @@ using Microsoft.Kinect; using System.Windows.Forms; using System.Linq; + // comment // In order to load the result of this wizard, you will also need to // add the output bin/ folder of this project to the list of loaded @@ -72,7 +73,6 @@ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pM 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("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[0].Optional = true; pManager[1].Optional = true; pManager[2].Optional = true; @@ -82,7 +82,6 @@ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pM pManager[6].Optional = true; pManager[7].Optional = true; pManager[8].Optional = true; - } /// @@ -110,7 +109,7 @@ protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown men private void SetMeshVisualisation(object sender, EventArgs e) { Analysis.AnalysisManager.SetEnabledOptions((ToolStripMenuItem)sender); - Analysis.AnalysisManager.ComputeLookupTables(sensorElevation, waterLevel); + Analysis.AnalysisManager.ComputeLookupTables(sensorElevation); quadMesh.VertexColors.Clear(); // Must flush mesh colors to properly updated display ExpireSolution(true); } @@ -168,7 +167,6 @@ protected override void SolveInstance(IGH_DataAccess DA) break; } sensorElevation /= unitsMultiplier; // Standardise to mm to match sensor units - Analysis.AnalysisManager.ComputeLookupTables(sensorElevation, waterLevel); // First-run computing of tables Stopwatch timer = Stopwatch.StartNew(); //debugging @@ -178,40 +176,31 @@ protected override void SolveInstance(IGH_DataAccess DA) this.kinectSensor = KinectController.sensor; } - if (this.kinectSensor != null) { if (KinectController.depthFrameData != null) { int trimmedWidth = KinectController.depthWidth - leftColumns - rightColumns; int trimmedHeight = KinectController.depthHeight - topRows - bottomRows; - - + // initialize all arrays pointCloud = new Point3d[trimmedWidth * trimmedHeight]; - vertexColors = new Color[trimmedWidth * trimmedHeight]; int[] depthFrameDataInt = new int[trimmedWidth * trimmedHeight]; double[] averagedDepthFrameData = new double[trimmedWidth * trimmedHeight]; - //initialize outputs + // initialize outputs outputMesh = new List(); output = new List(); //debugging Point3d tempPoint = new Point3d(); Core.PixelSize depthPixelSize = Core.GetDepthPixelSpacing(sensorElevation); - // Only initialise a full-size vertex color array if a mesh-coloring visualisation is enabled - if (Analysis.AnalysisManager.enabledMeshVisualisationOptions.Count > 0) - vertexColors = new Color[trimmedWidth * trimmedHeight]; - else - vertexColors = new Color[0]; - - //trim the depth array and cast ushort values to int + // trim the depth array and cast ushort values to int Core.CopyAsIntArray(KinectController.depthFrameData, depthFrameDataInt, leftColumns, rightColumns, topRows, bottomRows, KinectController.depthHeight, KinectController.depthWidth); averageFrames = averageFrames < 1 ? 1 : averageFrames; //make sure there is at least one frame in the render buffer - //reset everything when resizing Kinect's field of view or changing the amounts of frame to average across + // reset everything when resizing Kinect's field of view or changing the amounts of frame to average across if (renderBuffer.Count > averageFrames || quadMesh.Faces.Count != (trimmedWidth - 2) * (trimmedHeight - 2)) { renderBuffer.Clear(); @@ -221,9 +210,9 @@ protected override void SolveInstance(IGH_DataAccess DA) else renderBuffer.AddLast(depthFrameDataInt); - output.Add(String.Format("Render buffer length: {0} frames", renderBuffer.Count)); //debugging + output.Add("Render buffer length:".PadRight(30, ' ') + renderBuffer.Count + " frames"); //debugging - //average across multiple frames + // average across multiple frames for (int pixel = 0; pixel < depthFrameDataInt.Length; pixel++) { if (depthFrameDataInt[pixel] > 200 && depthFrameDataInt[pixel] <= lookupTable.Length) //We have a valid pixel. TODO remove reference to the lookup table @@ -249,7 +238,7 @@ protected override void SolveInstance(IGH_DataAccess DA) } timer.Stop(); - output.Add("Frames averaging: " + timer.ElapsedMilliseconds.ToString() + " ms"); + output.Add("Frames averaging: ".PadRight(30, ' ') + timer.ElapsedMilliseconds.ToString() + " ms"); timer.Restart(); //debugging if (blurRadius > 1) //apply gaussian blur @@ -257,32 +246,40 @@ protected override void SolveInstance(IGH_DataAccess DA) GaussianBlurProcessor gaussianBlurProcessor = new GaussianBlurProcessor(blurRadius, trimmedWidth, trimmedHeight); gaussianBlurProcessor.Apply(averagedDepthFrameData); timer.Stop(); - output.Add("Gaussian blurring: " + timer.ElapsedMilliseconds.ToString() + " ms"); + output.Add("Gaussian blurring: ".PadRight(30, ' ') + timer.ElapsedMilliseconds.ToString() + " ms"); timer.Restart(); //debugging } + // Setup variables for the coloring process + Analysis.AnalysisManager.ComputeLookupTables(sensorElevation); // First-run computing of tables + var enabledMeshColoring = Analysis.AnalysisManager.GetEnabledMeshColoring(); + var enabledColorTable = enabledMeshColoring.lookupTable; + var hasColorTable = enabledColorTable.Length > 0; // Setting the 'no analysis' option == empty table + vertexColors = new Color[hasColorTable ? trimmedWidth * trimmedHeight : 0]; // A 0-length array wont be used in meshing + var pixelsForAnalysis = new Point3d[4]; + // Setup variables for per-pixel loop + pointCloud = new Point3d[trimmedWidth * trimmedHeight]; int arrayIndex = 0; - for (int rows = 0; rows < trimmedHeight; rows++) { for (int columns = 0; columns < trimmedWidth; columns++) { + depthPoint = averagedDepthFrameData[arrayIndex]; tempPoint.X = columns * -unitsMultiplier * depthPixelSize.x; tempPoint.Y = rows * -unitsMultiplier * depthPixelSize.y; - - depthPoint = averagedDepthFrameData[arrayIndex]; tempPoint.Z = (depthPoint - sensorElevation) * -unitsMultiplier; - pointCloud[arrayIndex] = tempPoint; - - if (vertexColors.Length > 0) // Proxy for whether a mesh-coloring visualisation has been enabled - vertexColors[arrayIndex] = Analysis.AnalysisManager.GetPixelColor((int)depthPoint); - + pointCloud[arrayIndex] = tempPoint; // Add new point to point cloud itself + if (hasColorTable) // Perform analysis as needed and lookup result in table + { + var pixelIndex = enabledMeshColoring.GetPixelIndexForAnalysis(tempPoint, pixelsForAnalysis); + vertexColors[arrayIndex] = enabledColorTable[pixelIndex]; + } arrayIndex++; } } - + //keep only the desired amount of frames in the buffer while (renderBuffer.Count >= averageFrames) { @@ -291,14 +288,14 @@ protected override void SolveInstance(IGH_DataAccess DA) //debugging timer.Stop(); - output.Add("Point Cloud generation: " + timer.ElapsedMilliseconds.ToString() + " ms"); + output.Add("Point cloud generation/color: ".PadRight(30, ' ') + timer.ElapsedMilliseconds.ToString() + " ms"); timer.Restart(); //debugging quadMesh = Core.CreateQuadMesh(quadMesh, pointCloud, vertexColors, trimmedWidth, trimmedHeight); outputMesh.Add(quadMesh); timer.Stop(); //debugging - output.Add("Meshing: " + timer.ElapsedMilliseconds.ToString() + " ms"); + output.Add("Meshing: ".PadRight(30, ' ') + timer.ElapsedMilliseconds.ToString() + " ms"); } DA.SetDataList(0, outputMesh); From 925fc2dabd768d7ac692867e456f124cde83f2a3 Mon Sep 17 00:00:00 2001 From: Philip Belesky Date: Fri, 20 Sep 2019 15:46:25 +1000 Subject: [PATCH 3/9] Add structure to allow analysis classes to output geometry (i.e. water levels; contours) + split up Analysis.cs Analysis.cs was getting a little large; the new file contains just the implementations of each analysis option --- SandWorm/Analysis.cs | 146 +++++----------------------------- SandWorm/Analytics.cs | 145 +++++++++++++++++++++++++++++++++ SandWorm/SandWorm.csproj | 1 + SandWorm/SandWormComponent.cs | 28 +++++-- 4 files changed, 189 insertions(+), 131 deletions(-) create mode 100644 SandWorm/Analytics.cs diff --git a/SandWorm/Analysis.cs b/SandWorm/Analysis.cs index c66b1ca..bb44dd1 100644 --- a/SandWorm/Analysis.cs +++ b/SandWorm/Analysis.cs @@ -10,6 +10,8 @@ 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. @@ -19,9 +21,9 @@ static AnalysisManager() // Note that the order of items here determines their m { options = new List { - new Water(), new Contours(), - new None(), - new Elevation(), new Slope(), new Aspect() + new Analytics.Water(), new Analytics.Contours(), + new Analytics.None(), + new Analytics.Elevation(), new Analytics.Slope(), new Analytics.Aspect() }; // Default to showing elevation analysis options[3].IsEnabled = true; @@ -33,12 +35,22 @@ public static MeshColorAnalysis GetEnabledMeshColoring() { foreach (var enabledOption in GetEnabledAnalyses()) { - var optionType = enabledOption.GetType(); - var optionTest = optionType.IsSubclassOf(typeof(MeshColorAnalysis)); if (enabledOption.GetType().IsSubclassOf(typeof(MeshColorAnalysis))) return enabledOption as MeshColorAnalysis; } - return null; // Shouldn't happen; a mesh color (even no color) is always set + 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) @@ -99,6 +111,10 @@ public abstract class MeshGeometryAnalysis : MeshAnalysis { /// A form of analysis that outputs geometry (i.e. contours) based on the mesh public MeshGeometryAnalysis(string menuName) : base(menuName, false) { } // Note: not mutually exclusive + + // Note that the use of may potentially exclude some geometric types as returnable + // Note also the need to hard-code params useful to any of the analytics; operator overloading wont work :( + public abstract void GetGeometryForAnalysis(ref List outputGeometry, double waterLevel); } public abstract class MeshColorAnalysis : MeshAnalysis @@ -127,123 +143,5 @@ public void ComputeLinearRanges(params VisualisationRangeWithColor[] lookUpRange } } } - - public class None : MeshColorAnalysis - { - public None() : base("No Visualisation") { } - - public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts) - { - return 0; // Should never be called (see below) - } - - public override void ComputeLookupTableForAnalysis(double sensorElevation) - { - lookupTable = new Color[0]; // Empty color table allows pixel loop to skip lookup - } - } - - public class Elevation : MeshColorAnalysis - { - public Elevation() : base("Visualise Elevation") { } - - public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts) - { - return (int)vertex.Z; - } - - public override void ComputeLookupTableForAnalysis(double sensorElevation) - { - var normalElevationRange = new VisualisationRangeWithColor - { - ValueStart = 0, - ValueEnd = (int)sensorElevation - 201, - ColorStart = new ColorHSL(0.20, 0.35, 0.02), - ColorEnd = new ColorHSL(0.50, 0.85, 0.85) - }; // A clear gradient for pixels inside the expected normal model height - - var extraElevationRange = new VisualisationRangeWithColor - { - ValueStart = (int)sensorElevation - 200, - ValueEnd = (int)sensorElevation + 1, - ColorStart = new ColorHSL(1.00, 0.85, 0.76), - ColorEnd = new ColorHSL(0.50, 0.85, 0.99) - }; // A fallback gradiend for those outside (TODO: set sensible colors here) - ComputeLinearRanges(normalElevationRange, extraElevationRange); - } - } - - private class Slope : MeshColorAnalysis - { - public Slope() : base("Visualise Slope") { } - - public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] neighbours) - { - return 22; // TODO: benchmark different methods for passing pixels before enabling a real calculation - // Loop over the neighbouring pixels; calculate slopes relative to vertex - //double slopeSum = 0; - //for (int i = 0; i < neighbours.Length; i++) - //{ - // double rise = vertex.Z - neighbours[i].Z; - // double run = Math.Sqrt(Math.Pow(vertex.X - neighbours[i].X, 2) + Math.Pow(vertex.Y - neighbours[i].Y, 2)); - // slopeSum += rise / run; - //} - //double slopeAverage = Math.Abs(slopeSum / neighbours.Length); - //double slopeAsPercent = slopeAverage * 100; // Array is keyed as 0 - 100 - //return (int)slopeAsPercent; // Cast to int as its cross-referenced to the lookup - } - - public override void ComputeLookupTableForAnalysis(double sensorElevation) - { - var slopeRange = new VisualisationRangeWithColor - { - ValueStart = 0, - ValueEnd = 100, - ColorStart = new ColorHSL(1.0, 1.0, 1.0), // White - ColorEnd = new ColorHSL(1.0, 1.0, 0.3) // Dark Red - }; - ComputeLinearRanges(slopeRange); - } - } - - private class Aspect : MeshColorAnalysis - { - public Aspect() : base("Visualise Aspect") { } - - public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts) - { - return 44; - } - - public override void ComputeLookupTableForAnalysis(double sensorElevation) - { - var rightAspect = new VisualisationRangeWithColor - { - ValueStart = 0, - ValueEnd = 180, - ColorStart = new ColorHSL(1.0, 1.0, 1.0), // White - ColorEnd = new ColorHSL(1.0, 1.0, 0.3) // Dark Red - }; - var leftAspect = new VisualisationRangeWithColor - { - ValueStart = 180, // For the other side of the aspect we loop back to the 0 value - ValueEnd = 359, - ColorStart = new ColorHSL(1.0, 1.0, 0.3), // Dark Red - ColorEnd = new ColorHSL(1.0, 1.0, 1.0) // White - }; - ComputeLinearRanges(rightAspect, leftAspect); - } - } - - - public class Contours : MeshGeometryAnalysis - { - public Contours() : base("Show Contour Lines") { } - } - - public class Water : MeshGeometryAnalysis - { - public Water() : base("Show Water Level") { } - } } } \ No newline at end of file diff --git a/SandWorm/Analytics.cs b/SandWorm/Analytics.cs new file mode 100644 index 0000000..2444445 --- /dev/null +++ b/SandWorm/Analytics.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using Rhino.Display; +using Rhino.Geometry; + +namespace SandWorm +{ + class Analytics + { + /// Implementations of particular analysis options + + public class None : Analysis.MeshColorAnalysis + { + public None() : base("No Visualisation") { } + + public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts) + { + return 0; // Should never be called (see below) + } + + public override void ComputeLookupTableForAnalysis(double sensorElevation) + { + lookupTable = new Color[0]; // Empty color table allows pixel loop to skip lookup + } + } + + public class Elevation : Analysis.MeshColorAnalysis + { + public Elevation() : base("Visualise Elevation") { } + + public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts) + { + return (int)vertex.Z; + } + + public override void ComputeLookupTableForAnalysis(double sensorElevation) + { + var normalElevationRange = new Analysis.VisualisationRangeWithColor + { + ValueStart = 0, + ValueEnd = (int)sensorElevation - 201, + ColorStart = new ColorHSL(0.20, 0.35, 0.02), + ColorEnd = new ColorHSL(0.50, 0.85, 0.85) + }; // A clear gradient for pixels inside the expected normal model height + + var extraElevationRange = new Analysis.VisualisationRangeWithColor + { + ValueStart = (int)sensorElevation - 200, + ValueEnd = (int)sensorElevation + 1, + ColorStart = new ColorHSL(1.00, 0.85, 0.76), + ColorEnd = new ColorHSL(0.50, 0.85, 0.99) + }; // A fallback gradiend for those outside (TODO: set sensible colors here) + ComputeLinearRanges(normalElevationRange, extraElevationRange); + } + } + + public class Slope : Analysis.MeshColorAnalysis + { + public Slope() : base("Visualise Slope") { } + + public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] neighbours) + { + // Loop over the neighbouring pixels; calculate slopes relative to vertex + double slopeSum = 0; + for (int i = 0; i < neighbours.Length; i++) + { + double rise = vertex.Z - neighbours[i].Z; + double run = Math.Sqrt(Math.Pow(vertex.X - neighbours[i].X, 2) + Math.Pow(vertex.Y - neighbours[i].Y, 2)); + slopeSum += rise / run; + } + double slopeAverage = Math.Abs(slopeSum / neighbours.Length); + double slopeAsPercent = slopeAverage * 100; // Array is keyed as 0 - 100 + return (int)slopeAsPercent; // Cast to int as its cross-referenced to the lookup + } + + public override void ComputeLookupTableForAnalysis(double sensorElevation) + { + var slopeRange = new Analysis.VisualisationRangeWithColor + { + ValueStart = 0, + ValueEnd = 100, + ColorStart = new ColorHSL(1.0, 1.0, 1.0), // White + ColorEnd = new ColorHSL(1.0, 1.0, 0.3) // Dark Red + }; + ComputeLinearRanges(slopeRange); + } + } + + public class Aspect : Analysis.MeshColorAnalysis + { + public Aspect() : base("Visualise Aspect") { } + + public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts) + { + return 44; + } + + public override void ComputeLookupTableForAnalysis(double sensorElevation) + { + var rightAspect = new Analysis.VisualisationRangeWithColor + { + ValueStart = 0, + ValueEnd = 180, + ColorStart = new ColorHSL(1.0, 1.0, 1.0), // White + ColorEnd = new ColorHSL(1.0, 1.0, 0.3) // Dark Red + }; + var leftAspect = new Analysis.VisualisationRangeWithColor + { + ValueStart = 180, // For the other side of the aspect we loop back to the 0 value + ValueEnd = 359, + ColorStart = new ColorHSL(1.0, 1.0, 0.3), // Dark Red + ColorEnd = new ColorHSL(1.0, 1.0, 1.0) // White + }; + ComputeLinearRanges(rightAspect, leftAspect); + } + } + + public class Contours : Analysis.MeshGeometryAnalysis + { + public Contours() : base("Show Contour Lines") { } + + public override void GetGeometryForAnalysis(ref List outputGeometry, double wl) + { + var dummyLine = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 1)); + var contour = NurbsCurve.CreateFromLine(dummyLine); + outputGeometry.Add(contour); // TODO: actual implementation + } + } + + public class Water : Analysis.MeshGeometryAnalysis + { + public Water() : base("Show Water Level") { } + + public override void GetGeometryForAnalysis(ref List outputGeometry, double wl) + { + var waterPlane = new Plane(new Point3d(0, 0, 0), new Vector3d(0, 0, 1)); + var xInterval = new Interval(0, 100); + var yInterval = new Interval(0, 100); + var waterSrf = new PlaneSurface(waterPlane, xInterval, yInterval); + outputGeometry.Add(waterSrf); // TODO: actual implementation + } + } + } +} diff --git a/SandWorm/SandWorm.csproj b/SandWorm/SandWorm.csproj index 4559d73..7c7481e 100644 --- a/SandWorm/SandWorm.csproj +++ b/SandWorm/SandWorm.csproj @@ -91,6 +91,7 @@ + diff --git a/SandWorm/SandWormComponent.cs b/SandWorm/SandWormComponent.cs index 3f74a1c..a3f6fe3 100644 --- a/SandWorm/SandWormComponent.cs +++ b/SandWorm/SandWormComponent.cs @@ -22,6 +22,7 @@ public class SandWorm : GH_Component private KinectSensor kinectSensor = null; private Point3d[] pointCloud; private List outputMesh = null; + private List outputAnalysisGeometry = null; public static List output = null;//debugging private LinkedList renderBuffer = new LinkedList(); public int[] runningSum = Enumerable.Range(1, 217088).Select(i => new int()).ToArray(); @@ -89,7 +90,8 @@ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pM /// protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) { - pManager.AddMeshParameter("Mesh", "M", "Resulting Mesh", GH_ParamAccess.list); + 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 } @@ -182,7 +184,7 @@ protected override void SolveInstance(IGH_DataAccess DA) { int trimmedWidth = KinectController.depthWidth - leftColumns - rightColumns; int trimmedHeight = KinectController.depthHeight - topRows - bottomRows; - + // initialize all arrays pointCloud = new Point3d[trimmedWidth * trimmedHeight]; int[] depthFrameDataInt = new int[trimmedWidth * trimmedHeight]; @@ -190,6 +192,7 @@ protected override void SolveInstance(IGH_DataAccess DA) // initialize outputs outputMesh = new List(); + outputAnalysisGeometry = new List(); output = new List(); //debugging Point3d tempPoint = new Point3d(); @@ -199,7 +202,7 @@ protected override void SolveInstance(IGH_DataAccess DA) Core.CopyAsIntArray(KinectController.depthFrameData, depthFrameDataInt, leftColumns, rightColumns, topRows, bottomRows, KinectController.depthHeight, KinectController.depthWidth); averageFrames = averageFrames < 1 ? 1 : averageFrames; //make sure there is at least one frame in the render buffer - + // reset everything when resizing Kinect's field of view or changing the amounts of frame to average across if (renderBuffer.Count > averageFrames || quadMesh.Faces.Count != (trimmedWidth - 2) * (trimmedHeight - 2)) { @@ -230,10 +233,10 @@ protected override void SolveInstance(IGH_DataAccess DA) renderBuffer.Last.Value[pixel] = (int)sensorElevation; } } - + averagedDepthFrameData[pixel] = runningSum[pixel] / renderBuffer.Count; //calculate average values - if (renderBuffer.Count >= averageFrames) + if (renderBuffer.Count >= averageFrames) runningSum[pixel] -= renderBuffer.First.Value[pixel]; //subtract the oldest value from the sum } @@ -279,7 +282,7 @@ protected override void SolveInstance(IGH_DataAccess DA) arrayIndex++; } } - + //keep only the desired amount of frames in the buffer while (renderBuffer.Count >= averageFrames) { @@ -296,10 +299,21 @@ protected override void SolveInstance(IGH_DataAccess DA) timer.Stop(); //debugging output.Add("Meshing: ".PadRight(30, ' ') + timer.ElapsedMilliseconds.ToString() + " ms"); + timer.Restart(); //debugging + + // Add extra outputs (water planes; contours; etc) based on the mesh and currently enabled analysis + foreach (var enabledAnalysis in Analysis.AnalysisManager.GetEnabledMeshAnalytics()) + { + enabledAnalysis.GetGeometryForAnalysis(ref outputAnalysisGeometry, waterLevel); + } + + timer.Stop(); //debugging + output.Add("Analysing: ".PadRight(30, ' ') + timer.ElapsedMilliseconds.ToString() + " ms"); } DA.SetDataList(0, outputMesh); - DA.SetDataList(1, output); //debugging + DA.SetDataList(1, outputAnalysisGeometry); + DA.SetDataList(2, output); //debugging } if (tickRate > 0) // Allow users to force manual recalculation From 111f14f5d58e00be587784c2ccd24e71c76d4c40 Mon Sep 17 00:00:00 2001 From: Philip Belesky Date: Fri, 20 Sep 2019 17:59:56 +1000 Subject: [PATCH 4/9] Finish first actually-working prototype of the slope calculation --- SandWorm/Analysis.cs | 2 +- SandWorm/Analytics.cs | 45 ++++++++++++++++++-------- SandWorm/SandWormComponent.cs | 60 +++++++++++++++++++++++++---------- 3 files changed, 76 insertions(+), 31 deletions(-) diff --git a/SandWorm/Analysis.cs b/SandWorm/Analysis.cs index bb44dd1..5e127dc 100644 --- a/SandWorm/Analysis.cs +++ b/SandWorm/Analysis.cs @@ -124,7 +124,7 @@ public abstract class MeshColorAnalysis : MeshAnalysis public MeshColorAnalysis(string menuName) : base(menuName, true) { } // Note: is mutually exclusive - public abstract int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts); + public abstract int GetPixelIndexForAnalysis(Point3d vertex, List analysisPts); public abstract void ComputeLookupTableForAnalysis(double sensorElevation); diff --git a/SandWorm/Analytics.cs b/SandWorm/Analytics.cs index 2444445..744f085 100644 --- a/SandWorm/Analytics.cs +++ b/SandWorm/Analytics.cs @@ -14,7 +14,7 @@ public class None : Analysis.MeshColorAnalysis { public None() : base("No Visualisation") { } - public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts) + public override int GetPixelIndexForAnalysis(Point3d vertex, List analysisPts) { return 0; // Should never be called (see below) } @@ -29,7 +29,7 @@ public class Elevation : Analysis.MeshColorAnalysis { public Elevation() : base("Visualise Elevation") { } - public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts) + public override int GetPixelIndexForAnalysis(Point3d vertex, List analysisPts) { return (int)vertex.Z; } @@ -58,32 +58,51 @@ public override void ComputeLookupTableForAnalysis(double sensorElevation) public class Slope : Analysis.MeshColorAnalysis { public Slope() : base("Visualise Slope") { } + private int slopeCap = 500; // As measuring in % need an uppper limit on the value - public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] neighbours) + public override int GetPixelIndexForAnalysis(Point3d vertex, List neighbours) { // Loop over the neighbouring pixels; calculate slopes relative to vertex double slopeSum = 0; - for (int i = 0; i < neighbours.Length; i++) + for (int i = 0; i < neighbours.Count; i++) { double rise = vertex.Z - neighbours[i].Z; double run = Math.Sqrt(Math.Pow(vertex.X - neighbours[i].X, 2) + Math.Pow(vertex.Y - neighbours[i].Y, 2)); - slopeSum += rise / run; + slopeSum += Math.Abs(rise / run); } - double slopeAverage = Math.Abs(slopeSum / neighbours.Length); + double slopeAverage = slopeSum / neighbours.Count; double slopeAsPercent = slopeAverage * 100; // Array is keyed as 0 - 100 - return (int)slopeAsPercent; // Cast to int as its cross-referenced to the lookup + + if (slopeAsPercent >= slopeCap) + return slopeCap; + else + return (int)slopeAsPercent; // Cast to int as its then cross-referenced to the lookup } public override void ComputeLookupTableForAnalysis(double sensorElevation) { - var slopeRange = new Analysis.VisualisationRangeWithColor + var slightSlopeRange = new Analysis.VisualisationRangeWithColor { ValueStart = 0, - ValueEnd = 100, - ColorStart = new ColorHSL(1.0, 1.0, 1.0), // White - ColorEnd = new ColorHSL(1.0, 1.0, 0.3) // Dark Red + ValueEnd = 30, + 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 + { + ValueStart = 30, + ValueEnd = 200, + ColorStart = new ColorHSL(0.15, 1.0, 0.5), // Green + ColorEnd = new ColorHSL(0.0, 1.0, 0.5) // Red + }; + var extremeSlopeRange = new Analysis.VisualisationRangeWithColor + { + ValueStart = 200, + ValueEnd = slopeCap, + ColorStart = new ColorHSL(0.0, 1.0, 0.5), // Red + ColorEnd = new ColorHSL(0.0, 1.0, 0.0) // Black }; - ComputeLinearRanges(slopeRange); + ComputeLinearRanges(slightSlopeRange, moderateSlopeRange, extremeSlopeRange); } } @@ -91,7 +110,7 @@ public class Aspect : Analysis.MeshColorAnalysis { public Aspect() : base("Visualise Aspect") { } - public override int GetPixelIndexForAnalysis(Point3d vertex, params Point3d[] analysisPts) + public override int GetPixelIndexForAnalysis(Point3d vertex, List analysisPts) { return 44; } diff --git a/SandWorm/SandWormComponent.cs b/SandWorm/SandWormComponent.cs index a3f6fe3..809d432 100644 --- a/SandWorm/SandWormComponent.cs +++ b/SandWorm/SandWormComponent.cs @@ -253,14 +253,6 @@ protected override void SolveInstance(IGH_DataAccess DA) timer.Restart(); //debugging } - // Setup variables for the coloring process - Analysis.AnalysisManager.ComputeLookupTables(sensorElevation); // First-run computing of tables - var enabledMeshColoring = Analysis.AnalysisManager.GetEnabledMeshColoring(); - var enabledColorTable = enabledMeshColoring.lookupTable; - var hasColorTable = enabledColorTable.Length > 0; // Setting the 'no analysis' option == empty table - vertexColors = new Color[hasColorTable ? trimmedWidth * trimmedHeight : 0]; // A 0-length array wont be used in meshing - var pixelsForAnalysis = new Point3d[4]; - // Setup variables for per-pixel loop pointCloud = new Point3d[trimmedWidth * trimmedHeight]; int arrayIndex = 0; @@ -272,16 +264,54 @@ protected override void SolveInstance(IGH_DataAccess DA) tempPoint.X = columns * -unitsMultiplier * depthPixelSize.x; tempPoint.Y = rows * -unitsMultiplier * depthPixelSize.y; tempPoint.Z = (depthPoint - sensorElevation) * -unitsMultiplier; - pointCloud[arrayIndex] = tempPoint; // Add new point to point cloud itself - if (hasColorTable) // Perform analysis as needed and lookup result in table + arrayIndex++; + } + } + + //debugging + timer.Stop(); + output.Add("Point cloud generation: ".PadRight(30, ' ') + timer.ElapsedMilliseconds.ToString() + " ms"); + timer.Restart(); //debugging + + // Setup variables for the coloring process + Analysis.AnalysisManager.ComputeLookupTables(sensorElevation); // First-run computing of tables + var enabledMeshColoring = Analysis.AnalysisManager.GetEnabledMeshColoring(); + var enabledColorTable = enabledMeshColoring.lookupTable; + // Loop through point cloud to assign colors (TODO: should be very amenable to parallelising?) + if (enabledColorTable.Length > 0) // Setting the 'no analysis' option == empty table + { + vertexColors = new Color[pointCloud.Length]; // A 0-length array wont be used in meshing + var neighbourPixels = new List(); + // TODO: replace below with a more robust method of determing analytic method + bool usingNeighbours = enabledMeshColoring.Name == "Visualise Slope" || enabledMeshColoring.Name == "Visualise Aspect"; + for (int i = 0; i < pointCloud.Length; i++) + { + if (usingNeighbours) // If analysis needs to be passed adjacent pixels { - var pixelIndex = enabledMeshColoring.GetPixelIndexForAnalysis(tempPoint, pixelsForAnalysis); - vertexColors[arrayIndex] = enabledColorTable[pixelIndex]; + neighbourPixels.Clear(); + if (i >= trimmedWidth) + neighbourPixels.Add(pointCloud[i - trimmedWidth]); // North neighbour + if ((i + 1) % (trimmedWidth) != 0) + neighbourPixels.Add(pointCloud[i + 1]); // East neighbour + if (i < trimmedWidth * (trimmedHeight - 1)) + neighbourPixels.Add(pointCloud[i + trimmedWidth]); // South neighbour + if (i % trimmedWidth != 0) + neighbourPixels.Add(pointCloud[i - 1]); // West neighbour } - arrayIndex++; + var colorIndex = enabledMeshColoring.GetPixelIndexForAnalysis(pointCloud[i], neighbourPixels); + vertexColors[i] = enabledColorTable[colorIndex]; } } + else + { + vertexColors = new Color[0]; // Unset vertex colors to clear previous results + } + + //debugging + timer.Stop(); + output.Add("Point cloud coloring: ".PadRight(30, ' ') + timer.ElapsedMilliseconds.ToString() + " ms"); + timer.Restart(); //debugging //keep only the desired amount of frames in the buffer while (renderBuffer.Count >= averageFrames) @@ -289,10 +319,6 @@ protected override void SolveInstance(IGH_DataAccess DA) renderBuffer.RemoveFirst(); } - //debugging - timer.Stop(); - output.Add("Point cloud generation/color: ".PadRight(30, ' ') + timer.ElapsedMilliseconds.ToString() + " ms"); - timer.Restart(); //debugging quadMesh = Core.CreateQuadMesh(quadMesh, pointCloud, vertexColors, trimmedWidth, trimmedHeight); outputMesh.Add(quadMesh); From b8fa16bcc73619cddc7480c924f4161581aa921d Mon Sep 17 00:00:00 2001 From: Philip Belesky Date: Sun, 22 Sep 2019 23:08:04 +1000 Subject: [PATCH 5/9] Simplify and tweak color span provision; better tune elevation colors --- SandWorm/Analysis.cs | 19 ++++++++------- SandWorm/Analytics.cs | 56 ++++++++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/SandWorm/Analysis.cs b/SandWorm/Analysis.cs index 5e127dc..83884b8 100644 --- a/SandWorm/Analysis.cs +++ b/SandWorm/Analysis.cs @@ -73,8 +73,7 @@ public static void ComputeLookupTables(double sensorElevation) public class VisualisationRangeWithColor { /// Describes a numeric range (e.g. elevation or slope values) and color range to visualise it. - public int ValueStart { get; set; } - public int ValueEnd { get; set; } + public int ValueSpan { get; set; } public ColorHSL ColorStart { get; set; } public ColorHSL ColorEnd { get; set; } @@ -114,7 +113,7 @@ public MeshGeometryAnalysis(string menuName) : base(menuName, false) { } // Note // Note that the use of may potentially exclude some geometric types as returnable // Note also the need to hard-code params useful to any of the analytics; operator overloading wont work :( - public abstract void GetGeometryForAnalysis(ref List outputGeometry, double waterLevel); + public abstract void GetGeometryForAnalysis(ref List outputGeometry, double waterLevel, Point3d[] edgePts); } public abstract class MeshColorAnalysis : MeshAnalysis @@ -130,16 +129,18 @@ public MeshColorAnalysis(string menuName) : base(menuName, true) { } // Note: is public void ComputeLinearRanges(params VisualisationRangeWithColor[] lookUpRanges) { - var lookupTableMaximumSize = 1; - foreach (var range in lookUpRanges) lookupTableMaximumSize += range.ValueEnd - range.ValueStart; - lookupTable = new Color[lookupTableMaximumSize]; + var lookupTableMaximumSize = lookUpRanges.Length; + foreach (var range in lookUpRanges) lookupTableMaximumSize += range.ValueSpan; + lookupTable = new Color[lookupTableMaximumSize]; // Populate dict values by interpolating colors within each of the lookup ranges + var index = 0; foreach (var range in lookUpRanges) - for (var i = range.ValueStart; i < range.ValueEnd; i++) + for (var i = 0; i <= range.ValueSpan; i++) { - var progress = ((double)i - range.ValueStart) / (range.ValueEnd - range.ValueStart); - lookupTable[i] = range.InterpolateColor(progress); + var progress = (double)i / range.ValueSpan; + lookupTable[index] = range.InterpolateColor(progress); + index++; } } } diff --git a/SandWorm/Analytics.cs b/SandWorm/Analytics.cs index 744f085..766c7b9 100644 --- a/SandWorm/Analytics.cs +++ b/SandWorm/Analytics.cs @@ -31,27 +31,38 @@ public Elevation() : base("Visualise Elevation") { } public override int GetPixelIndexForAnalysis(Point3d vertex, List analysisPts) { - return (int)vertex.Z; + if (vertex.Z > 0) + return (int)vertex.Z; + return 0; // Usually occurs when sensor height is configured incorrectly } public override void ComputeLookupTableForAnalysis(double sensorElevation) { - var normalElevationRange = new Analysis.VisualisationRangeWithColor + var sElevationRange = new Analysis.VisualisationRangeWithColor { - ValueStart = 0, - ValueEnd = (int)sensorElevation - 201, - ColorStart = new ColorHSL(0.20, 0.35, 0.02), - ColorEnd = new ColorHSL(0.50, 0.85, 0.85) - }; // A clear gradient for pixels inside the expected normal model height - - var extraElevationRange = new Analysis.VisualisationRangeWithColor + ValueSpan = 50, + 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 = 50, + 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 { - ValueStart = (int)sensorElevation - 200, - ValueEnd = (int)sensorElevation + 1, - ColorStart = new ColorHSL(1.00, 0.85, 0.76), - ColorEnd = new ColorHSL(0.50, 0.85, 0.99) - }; // A fallback gradiend for those outside (TODO: set sensible colors here) - ComputeLinearRanges(normalElevationRange, extraElevationRange); + ValueSpan = 50, + 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 = 50, + ColorStart = new ColorHSL(0.10, 1, 0.6), // Orange + ColorEnd = new ColorHSL(0.00, 1, 0.7) // Red + }; + ComputeLinearRanges(sElevationRange, mElevationRange, lElevationRange, xlElevationRange); } } @@ -83,22 +94,19 @@ public override void ComputeLookupTableForAnalysis(double sensorElevation) { var slightSlopeRange = new Analysis.VisualisationRangeWithColor { - ValueStart = 0, - ValueEnd = 30, + ValueSpan = 30, 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 { - ValueStart = 30, - ValueEnd = 200, + ValueSpan = 30, ColorStart = new ColorHSL(0.15, 1.0, 0.5), // Green ColorEnd = new ColorHSL(0.0, 1.0, 0.5) // Red }; var extremeSlopeRange = new Analysis.VisualisationRangeWithColor { - ValueStart = 200, - ValueEnd = slopeCap, + ValueSpan = 200, ColorStart = new ColorHSL(0.0, 1.0, 0.5), // Red ColorEnd = new ColorHSL(0.0, 1.0, 0.0) // Black }; @@ -119,15 +127,13 @@ public override void ComputeLookupTableForAnalysis(double sensorElevation) { var rightAspect = new Analysis.VisualisationRangeWithColor { - ValueStart = 0, - ValueEnd = 180, + ValueSpan = 180, ColorStart = new ColorHSL(1.0, 1.0, 1.0), // White ColorEnd = new ColorHSL(1.0, 1.0, 0.3) // Dark Red }; var leftAspect = new Analysis.VisualisationRangeWithColor { - ValueStart = 180, // For the other side of the aspect we loop back to the 0 value - ValueEnd = 359, + ValueSpan = 180, // For the other side of the aspect we loop back to the 0 value ColorStart = new ColorHSL(1.0, 1.0, 0.3), // Dark Red ColorEnd = new ColorHSL(1.0, 1.0, 1.0) // White }; From b0978557c808f3cd3102a9dc3648be00c142a3c8 Mon Sep 17 00:00:00 2001 From: Philip Belesky Date: Sun, 22 Sep 2019 23:44:56 +1000 Subject: [PATCH 6/9] Finish implementing water plane draw --- SandWorm/Analytics.cs | 15 ++++++++------- SandWorm/SandWormComponent.cs | 7 +++++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/SandWorm/Analytics.cs b/SandWorm/Analytics.cs index 766c7b9..1fd41bb 100644 --- a/SandWorm/Analytics.cs +++ b/SandWorm/Analytics.cs @@ -145,7 +145,7 @@ public class Contours : Analysis.MeshGeometryAnalysis { public Contours() : base("Show Contour Lines") { } - public override void GetGeometryForAnalysis(ref List outputGeometry, double wl) + public override void GetGeometryForAnalysis(ref List outputGeometry, double waterLevel, Point3d[] edgePts) { var dummyLine = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 1)); var contour = NurbsCurve.CreateFromLine(dummyLine); @@ -157,13 +157,14 @@ public class Water : Analysis.MeshGeometryAnalysis { public Water() : base("Show Water Level") { } - public override void GetGeometryForAnalysis(ref List outputGeometry, double wl) + public override void GetGeometryForAnalysis(ref List outputGeometry, double waterLevel, Point3d[] edgePts) { - var waterPlane = new Plane(new Point3d(0, 0, 0), new Vector3d(0, 0, 1)); - var xInterval = new Interval(0, 100); - var yInterval = new Interval(0, 100); - var waterSrf = new PlaneSurface(waterPlane, xInterval, yInterval); - outputGeometry.Add(waterSrf); // TODO: actual implementation + var waterPlane = new Plane(new Point3d(edgePts[0].X, edgePts[0].Y, waterLevel), new Vector3d(0, 0, 1)); + var waterSrf = new PlaneSurface(waterPlane, + new Interval(edgePts[1].X, edgePts[0].X), + new Interval(edgePts[1].Y, edgePts[0].Y) + ); + outputGeometry.Add(waterSrf); } } } diff --git a/SandWorm/SandWormComponent.cs b/SandWorm/SandWormComponent.cs index 809d432..4147a14 100644 --- a/SandWorm/SandWormComponent.cs +++ b/SandWorm/SandWormComponent.cs @@ -44,7 +44,7 @@ public class SandWorm : GH_Component public static double unitsMultiplier; // Analysis state - private int waterLevel = 1000; + private int waterLevel = 50; /// /// Each implementation of GH_Component must provide a public @@ -330,7 +330,10 @@ protected override void SolveInstance(IGH_DataAccess DA) // Add extra outputs (water planes; contours; etc) based on the mesh and currently enabled analysis foreach (var enabledAnalysis in Analysis.AnalysisManager.GetEnabledMeshAnalytics()) { - enabledAnalysis.GetGeometryForAnalysis(ref outputAnalysisGeometry, waterLevel); + var edgePts = new Point3d[2]; + edgePts[0] = pointCloud[0]; + edgePts[1] = pointCloud[(trimmedHeight * trimmedWidth) - 1]; + enabledAnalysis.GetGeometryForAnalysis(ref outputAnalysisGeometry, waterLevel, edgePts); } timer.Stop(); //debugging From 4a571740ccdf816fe3917d8b0281e7a541854ae1 Mon Sep 17 00:00:00 2001 From: Philip Belesky Date: Mon, 23 Sep 2019 00:09:45 +1000 Subject: [PATCH 7/9] Add basic contouring (not optimised) --- SandWorm/Analysis.cs | 2 +- SandWorm/Analytics.cs | 22 +++++++++++++--------- SandWorm/SandWormComponent.cs | 31 ++++++++++++++++--------------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/SandWorm/Analysis.cs b/SandWorm/Analysis.cs index 83884b8..73c98fc 100644 --- a/SandWorm/Analysis.cs +++ b/SandWorm/Analysis.cs @@ -113,7 +113,7 @@ public MeshGeometryAnalysis(string menuName) : base(menuName, false) { } // Note // Note that the use of may potentially exclude some geometric types as returnable // Note also the need to hard-code params useful to any of the analytics; operator overloading wont work :( - public abstract void GetGeometryForAnalysis(ref List outputGeometry, double waterLevel, Point3d[] edgePts); + public abstract void GetGeometryForAnalysis(ref List outputGeometry, int wl, int ci, Mesh mesh); } public abstract class MeshColorAnalysis : MeshAnalysis diff --git a/SandWorm/Analytics.cs b/SandWorm/Analytics.cs index 1fd41bb..3101a05 100644 --- a/SandWorm/Analytics.cs +++ b/SandWorm/Analytics.cs @@ -145,24 +145,28 @@ public class Contours : Analysis.MeshGeometryAnalysis { public Contours() : base("Show Contour Lines") { } - public override void GetGeometryForAnalysis(ref List outputGeometry, double waterLevel, Point3d[] edgePts) + public override void GetGeometryForAnalysis(ref List outputGeometry, int wl, int contourInterval, Mesh mesh) { - var dummyLine = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 1)); - var contour = NurbsCurve.CreateFromLine(dummyLine); - outputGeometry.Add(contour); // TODO: actual implementation - } + 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); +; } } public class Water : Analysis.MeshGeometryAnalysis { public Water() : base("Show Water Level") { } - public override void GetGeometryForAnalysis(ref List outputGeometry, double waterLevel, Point3d[] edgePts) + public override void GetGeometryForAnalysis(ref List outputGeometry, int waterLevel, int ci, Mesh mesh) { - var waterPlane = new Plane(new Point3d(edgePts[0].X, edgePts[0].Y, waterLevel), new Vector3d(0, 0, 1)); + var bounds = mesh.GetBoundingBox(false); + var waterPlane = new Plane(new Point3d(bounds.Max.X, bounds.Max.Y, waterLevel), new Vector3d(0, 0, 1)); + var test = new Interval(bounds.Max.X, bounds.Min.X); var waterSrf = new PlaneSurface(waterPlane, - new Interval(edgePts[1].X, edgePts[0].X), - new Interval(edgePts[1].Y, edgePts[0].Y) + new Interval(bounds.Min.X, bounds.Max.X), + new Interval(bounds.Min.Y, bounds.Max.Y) ); outputGeometry.Add(waterSrf); } diff --git a/SandWorm/SandWormComponent.cs b/SandWorm/SandWormComponent.cs index 4147a14..e42f735 100644 --- a/SandWorm/SandWormComponent.cs +++ b/SandWorm/SandWormComponent.cs @@ -22,7 +22,7 @@ public class SandWorm : GH_Component private KinectSensor kinectSensor = null; private Point3d[] pointCloud; private List outputMesh = null; - private List outputAnalysisGeometry = null; + private List outputGeometry = null; public static List output = null;//debugging private LinkedList renderBuffer = new LinkedList(); public int[] runningSum = Enumerable.Range(1, 217088).Select(i => new int()).ToArray(); @@ -45,6 +45,7 @@ public class SandWorm : GH_Component // Analysis state private int waterLevel = 50; + private int contourInterval = 10; /// /// Each implementation of GH_Component must provide a public @@ -66,7 +67,8 @@ public SandWorm() protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) { pManager.AddNumberParameter("SensorHeight", "SH", "The height (in document units) of the sensor above your model", GH_ParamAccess.item, sensorElevation); - pManager.AddIntegerParameter("WaterLevel", "WL", "WaterLevel", GH_ParamAccess.item, 1000); + pManager.AddIntegerParameter("WaterLevel", "WL", "The water level (if this analysis is enabled)", GH_ParamAccess.item, 1000); + pManager.AddIntegerParameter("ContourInterval", "CI", "The interval (if this analysis is enabled)", GH_ParamAccess.item, 1000); 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); @@ -83,6 +85,7 @@ protected override void RegisterInputParams(GH_Component.GH_InputParamManager pM pManager[6].Optional = true; pManager[7].Optional = true; pManager[8].Optional = true; + pManager[9].Optional = true; } /// @@ -130,13 +133,14 @@ protected override void SolveInstance(IGH_DataAccess DA) { DA.GetData(0, ref sensorElevation); DA.GetData(1, ref waterLevel); - 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 averageFrames); - DA.GetData(8, ref blurRadius); + DA.GetData(2, ref contourInterval); + DA.GetData(3, ref leftColumns); + DA.GetData(4, ref rightColumns); + DA.GetData(5, ref topRows); + DA.GetData(6, ref bottomRows); + DA.GetData(7, ref tickRate); + DA.GetData(8, ref averageFrames); + DA.GetData(9, ref blurRadius); switch (units.ToString()) { @@ -192,7 +196,7 @@ protected override void SolveInstance(IGH_DataAccess DA) // initialize outputs outputMesh = new List(); - outputAnalysisGeometry = new List(); + outputGeometry = new List(); output = new List(); //debugging Point3d tempPoint = new Point3d(); @@ -330,10 +334,7 @@ protected override void SolveInstance(IGH_DataAccess DA) // Add extra outputs (water planes; contours; etc) based on the mesh and currently enabled analysis foreach (var enabledAnalysis in Analysis.AnalysisManager.GetEnabledMeshAnalytics()) { - var edgePts = new Point3d[2]; - edgePts[0] = pointCloud[0]; - edgePts[1] = pointCloud[(trimmedHeight * trimmedWidth) - 1]; - enabledAnalysis.GetGeometryForAnalysis(ref outputAnalysisGeometry, waterLevel, edgePts); + enabledAnalysis.GetGeometryForAnalysis(ref outputGeometry, waterLevel, contourInterval, quadMesh); } timer.Stop(); //debugging @@ -341,7 +342,7 @@ protected override void SolveInstance(IGH_DataAccess DA) } DA.SetDataList(0, outputMesh); - DA.SetDataList(1, outputAnalysisGeometry); + DA.SetDataList(1, outputGeometry); DA.SetDataList(2, output); //debugging } From f40393feeaef8eeb2898aa5d968cd8ba5a0e1fde Mon Sep 17 00:00:00 2001 From: Philip Belesky Date: Mon, 23 Sep 2019 00:17:53 +1000 Subject: [PATCH 8/9] Add array bounds check to the color lookup --- SandWorm/Analytics.cs | 1 - SandWorm/SandWormComponent.cs | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/SandWorm/Analytics.cs b/SandWorm/Analytics.cs index 3101a05..4ab4a7f 100644 --- a/SandWorm/Analytics.cs +++ b/SandWorm/Analytics.cs @@ -163,7 +163,6 @@ public override void GetGeometryForAnalysis(ref List outputGeometr { var bounds = mesh.GetBoundingBox(false); var waterPlane = new Plane(new Point3d(bounds.Max.X, bounds.Max.Y, waterLevel), new Vector3d(0, 0, 1)); - var test = new Interval(bounds.Max.X, bounds.Min.X); var waterSrf = new PlaneSurface(waterPlane, new Interval(bounds.Min.X, bounds.Max.X), new Interval(bounds.Min.Y, bounds.Max.Y) diff --git a/SandWorm/SandWormComponent.cs b/SandWorm/SandWormComponent.cs index e42f735..1bc45e4 100644 --- a/SandWorm/SandWormComponent.cs +++ b/SandWorm/SandWormComponent.cs @@ -303,7 +303,11 @@ protected override void SolveInstance(IGH_DataAccess DA) if (i % trimmedWidth != 0) neighbourPixels.Add(pointCloud[i - 1]); // West neighbour } + var colorIndex = enabledMeshColoring.GetPixelIndexForAnalysis(pointCloud[i], neighbourPixels); + if (colorIndex >= enabledColorTable.Length) + colorIndex = enabledColorTable.Length - 1; // Happens if sensorHeight is out of whack + vertexColors[i] = enabledColorTable[colorIndex]; } } From f350e64a8d53161a6ee867b8a66c6a4d109063e1 Mon Sep 17 00:00:00 2001 From: Philip Belesky Date: Mon, 23 Sep 2019 23:10:30 +1000 Subject: [PATCH 9/9] Run code cleanup --- SandWorm/Analysis.cs | 37 +++++++++++++------------ SandWorm/Analytics.cs | 52 ++++++++++++++++++++++------------- SandWorm/SandWormComponent.cs | 2 +- 3 files changed, 54 insertions(+), 37 deletions(-) diff --git a/SandWorm/Analysis.cs b/SandWorm/Analysis.cs index f1c37e7..aed12f9 100644 --- a/SandWorm/Analysis.cs +++ b/SandWorm/Analysis.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; -using System.Windows.Media.Media3D; using Rhino.Display; using Rhino.Geometry; @@ -11,7 +9,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. @@ -26,18 +23,19 @@ static AnalysisManager() // Note that the order of items here determines their m new Analytics.Elevation(), new Analytics.Slope(), new Analytics.Aspect() }; // Default to showing elevation analysis - options[3].IsEnabled = true; + options[3].isEnabled = true; } - public static List GetEnabledAnalyses() => options.FindAll(x => x.IsEnabled); + 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 } @@ -58,10 +56,10 @@ 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 = + exclusiveOption.isEnabled = selectedOption == exclusiveOption; // Toggle selected item; untoggle other exclusive items else - selectedOption.IsEnabled = !selectedOption.IsEnabled; // Simple toggle for independent items + selectedOption.isEnabled = !selectedOption.isEnabled; // Simple toggle for independent items } public static void ComputeLookupTables(double sensorElevation) @@ -74,6 +72,7 @@ public class VisualisationRangeWithColor { /// Describes a numeric range (e.g. elevation or slope values) and color range to visualise it. public int ValueSpan { get; set; } + public ColorHSL ColorStart { get; set; } public ColorHSL ColorEnd { get; set; } @@ -90,8 +89,7 @@ public ColorHSL InterpolateColor(double progress) // Progress is assumed to be a public abstract class MeshAnalysis { /// Some form of analysis that applies, or derives from, the mesh. - - public bool IsEnabled; // Whether to apply the analysis + public bool isEnabled; // Whether to apply the analysis public MeshAnalysis(string menuName, bool exclusive) { @@ -109,11 +107,14 @@ public MeshAnalysis(string menuName, bool exclusive) public abstract class MeshGeometryAnalysis : MeshAnalysis { /// A form of analysis that outputs geometry (i.e. contours) based on the mesh - public MeshGeometryAnalysis(string menuName) : base(menuName, false) { } // Note: not mutually exclusive + public MeshGeometryAnalysis(string menuName) : base(menuName, false) + { + } // Note: not mutually exclusive // Note that the use of may potentially exclude some geometric types as returnable // Note also the need to hard-code params useful to any of the analytics; operator overloading wont work :( - public abstract void GetGeometryForAnalysis(ref List outputGeometry, int wl, int ci, Mesh mesh); + public abstract void GetGeometryForAnalysis(ref List outputGeometry, int wl, int ci, + Mesh mesh); } public abstract class MeshColorAnalysis : MeshAnalysis @@ -121,7 +122,9 @@ public abstract class MeshColorAnalysis : MeshAnalysis /// A form of analysis that colors the vertices of the entire mesh public Color[] lookupTable; // Dictionary of integers that map to color values - public MeshColorAnalysis(string menuName) : base(menuName, true) { } // Note: is mutually exclusive + public MeshColorAnalysis(string menuName) : base(menuName, true) + { + } // Note: is mutually exclusive public abstract int GetPixelIndexForAnalysis(Point3d vertex, List analysisPts); @@ -138,11 +141,11 @@ public void ComputeLinearRanges(params VisualisationRangeWithColor[] lookUpRange foreach (var range in lookUpRanges) for (var i = 0; i <= range.ValueSpan; i++) { - var progress = (double)i / range.ValueSpan; + var progress = (double) i / range.ValueSpan; lookupTable[index] = range.InterpolateColor(progress); index++; } } } } -} +} \ No newline at end of file diff --git a/SandWorm/Analytics.cs b/SandWorm/Analytics.cs index 4ab4a7f..9ab62bd 100644 --- a/SandWorm/Analytics.cs +++ b/SandWorm/Analytics.cs @@ -27,12 +27,14 @@ public override void ComputeLookupTableForAnalysis(double sensorElevation) public class Elevation : Analysis.MeshColorAnalysis { - public Elevation() : base("Visualise Elevation") { } + public Elevation() : base("Visualise Elevation") + { + } public override int GetPixelIndexForAnalysis(Point3d vertex, List analysisPts) { if (vertex.Z > 0) - return (int)vertex.Z; + return (int) vertex.Z; return 0; // Usually occurs when sensor height is configured incorrectly } @@ -68,26 +70,30 @@ public override void ComputeLookupTableForAnalysis(double sensorElevation) public class Slope : Analysis.MeshColorAnalysis { - public Slope() : base("Visualise Slope") { } - private int slopeCap = 500; // As measuring in % need an uppper limit on the value + private readonly int slopeCap = 500; // As measuring in % need an uppper limit on the value + + public Slope() : base("Visualise Slope") + { + } public override int GetPixelIndexForAnalysis(Point3d vertex, List neighbours) { // Loop over the neighbouring pixels; calculate slopes relative to vertex double slopeSum = 0; - for (int i = 0; i < neighbours.Count; i++) + for (var i = 0; i < neighbours.Count; i++) { - double rise = vertex.Z - neighbours[i].Z; - double run = Math.Sqrt(Math.Pow(vertex.X - neighbours[i].X, 2) + Math.Pow(vertex.Y - neighbours[i].Y, 2)); + var rise = vertex.Z - neighbours[i].Z; + var run = Math.Sqrt(Math.Pow(vertex.X - neighbours[i].X, 2) + + Math.Pow(vertex.Y - neighbours[i].Y, 2)); slopeSum += Math.Abs(rise / run); } - double slopeAverage = slopeSum / neighbours.Count; - double slopeAsPercent = slopeAverage * 100; // Array is keyed as 0 - 100 + + var slopeAverage = slopeSum / neighbours.Count; + var slopeAsPercent = slopeAverage * 100; // Array is keyed as 0 - 100 if (slopeAsPercent >= slopeCap) return slopeCap; - else - return (int)slopeAsPercent; // Cast to int as its then cross-referenced to the lookup + return (int) slopeAsPercent; // Cast to int as its then cross-referenced to the lookup } public override void ComputeLookupTableForAnalysis(double sensorElevation) @@ -116,7 +122,9 @@ public override void ComputeLookupTableForAnalysis(double sensorElevation) public class Aspect : Analysis.MeshColorAnalysis { - public Aspect() : base("Visualise Aspect") { } + public Aspect() : base("Visualise Aspect") + { + } public override int GetPixelIndexForAnalysis(Point3d vertex, List analysisPts) { @@ -143,23 +151,29 @@ public override void ComputeLookupTableForAnalysis(double sensorElevation) public class Contours : Analysis.MeshGeometryAnalysis { - public Contours() : base("Show Contour Lines") { } + public Contours() : base("Show Contour Lines") + { + } - public override void GetGeometryForAnalysis(ref List outputGeometry, int wl, int contourInterval, Mesh mesh) + public override void GetGeometryForAnalysis(ref List outputGeometry, int wl, + int 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); -; } + } } public class Water : Analysis.MeshGeometryAnalysis { - public Water() : base("Show Water Level") { } + public Water() : base("Show Water Level") + { + } - public override void GetGeometryForAnalysis(ref List outputGeometry, int waterLevel, int ci, Mesh mesh) + public override void GetGeometryForAnalysis(ref List outputGeometry, int waterLevel, int ci, + 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)); @@ -167,8 +181,8 @@ public override void GetGeometryForAnalysis(ref List outputGeometr new Interval(bounds.Min.X, bounds.Max.X), new Interval(bounds.Min.Y, bounds.Max.Y) ); - outputGeometry.Add(waterSrf); + outputGeometry.Add(waterSrf); } } } -} +} \ No newline at end of file diff --git a/SandWorm/SandWormComponent.cs b/SandWorm/SandWormComponent.cs index 82a3330..8a88be3 100644 --- a/SandWorm/SandWormComponent.cs +++ b/SandWorm/SandWormComponent.cs @@ -89,7 +89,7 @@ protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown men base.AppendAdditionalComponentMenuItems(menu); foreach (Analysis.MeshAnalysis option in Analysis.AnalysisManager.options) // Add analysis items to menu { - Menu_AppendItem(menu, option.Name, SetMeshVisualisation, true, option.IsEnabled); + 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)