From bdd8a8aac1bc14132f2910f84095d92d275b8e6f Mon Sep 17 00:00:00 2001 From: James Bradley Date: Fri, 19 Apr 2024 15:47:58 -0400 Subject: [PATCH 1/6] Wall merge function --- LayoutFunctions/WallsLOD200/.gitignore | 9 + .../WallsLOD200/.vscode/launch.json | 29 +++ .../WallsLOD200/.vscode/tasks.json | 15 ++ LayoutFunctions/WallsLOD200/README.md | 22 ++ LayoutFunctions/WallsLOD200/WallsLOD200.sln | 34 +++ .../WallsLOD200.Dependencies.csproj | 14 ++ .../dependencies/WallsLOD200Inputs.g.cs | 61 +++++ .../dependencies/WallsLOD200Outputs.g.cs | 50 +++++ LayoutFunctions/WallsLOD200/global.json | 7 + LayoutFunctions/WallsLOD200/hypar.json | 44 ++++ LayoutFunctions/WallsLOD200/src/Function.g.cs | 73 ++++++ .../WallsLOD200/src/WallsLOD200.cs | 208 ++++++++++++++++++ .../WallsLOD200/src/WallsLOD200.csproj | 13 ++ .../WallsLOD200/test/FunctionTest.g.cs | 24 ++ LayoutFunctions/WallsLOD200/test/Usings.cs | 1 + .../WallsLOD200/test/WallsLOD200.Tests.csproj | 28 +++ 16 files changed, 632 insertions(+) create mode 100644 LayoutFunctions/WallsLOD200/.gitignore create mode 100644 LayoutFunctions/WallsLOD200/.vscode/launch.json create mode 100644 LayoutFunctions/WallsLOD200/.vscode/tasks.json create mode 100644 LayoutFunctions/WallsLOD200/README.md create mode 100644 LayoutFunctions/WallsLOD200/WallsLOD200.sln create mode 100644 LayoutFunctions/WallsLOD200/dependencies/WallsLOD200.Dependencies.csproj create mode 100644 LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Inputs.g.cs create mode 100644 LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Outputs.g.cs create mode 100644 LayoutFunctions/WallsLOD200/global.json create mode 100644 LayoutFunctions/WallsLOD200/hypar.json create mode 100644 LayoutFunctions/WallsLOD200/src/Function.g.cs create mode 100644 LayoutFunctions/WallsLOD200/src/WallsLOD200.cs create mode 100644 LayoutFunctions/WallsLOD200/src/WallsLOD200.csproj create mode 100644 LayoutFunctions/WallsLOD200/test/FunctionTest.g.cs create mode 100644 LayoutFunctions/WallsLOD200/test/Usings.cs create mode 100644 LayoutFunctions/WallsLOD200/test/WallsLOD200.Tests.csproj diff --git a/LayoutFunctions/WallsLOD200/.gitignore b/LayoutFunctions/WallsLOD200/.gitignore new file mode 100644 index 00000000..f1d4f671 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/.gitignore @@ -0,0 +1,9 @@ + +bin/ +obj/ +*.glb +output.json +input.json +.vs/ +server/ +test/Generated/ \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/.vscode/launch.json b/LayoutFunctions/WallsLOD200/.vscode/launch.json new file mode 100644 index 00000000..9ca206eb --- /dev/null +++ b/LayoutFunctions/WallsLOD200/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + "version": "0.2.0", + "inputs": [ + { + "id": "workflowId", + "type": "promptString", + "description": "Enter the workflow id to run." + } + ], + "configurations": [ + { + "name": "Attach to Hypar Run", + "type": "coreclr", + "request": "attach", + "processName": "WallsLOD200.Server" + }, + { + "name": "Launch Hypar Run (Run once only)", + "type": "coreclr", + "request": "launch", + "program": "${workspaceFolder}/server/bin/Debug/net6.0/WallsLOD200.Server.dll", + "args": [ + "--workflow-id", + "${input:workflowId}" + ], + "preLaunchTask": "server-build" + } + ] +} \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/.vscode/tasks.json b/LayoutFunctions/WallsLOD200/.vscode/tasks.json new file mode 100644 index 00000000..b4c57f0b --- /dev/null +++ b/LayoutFunctions/WallsLOD200/.vscode/tasks.json @@ -0,0 +1,15 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "server-build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/server/WallsLOD200.Server.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/README.md b/LayoutFunctions/WallsLOD200/README.md new file mode 100644 index 00000000..0378f2cc --- /dev/null +++ b/LayoutFunctions/WallsLOD200/README.md @@ -0,0 +1,22 @@ + + +# Walls LOD 200 + +The WallsLOD200 function. + +|Input Name|Type|Description| +|---|---|---| +|Length|number|The Length.| +|Width|number|The Width.| + + +
+ +|Output Name|Type|Description| +|---|---|---| +|Volume|Number|The volume.| + + +
+ +## Additional Information \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/WallsLOD200.sln b/LayoutFunctions/WallsLOD200/WallsLOD200.sln new file mode 100644 index 00000000..82a956a1 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/WallsLOD200.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WallsLOD200", "src\WallsLOD200.csproj", "{0C340FE7-F967-4DEB-BF30-8A8EF00917FB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WallsLOD200.Dependencies", "dependencies\WallsLOD200.Dependencies.csproj", "{F6919E7E-3680-4356-B0BC-01CE5F4124C5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WallsLOD200.Tests", "test\WallsLOD200.Tests.csproj", "{A23EBAE9-531C-4B47-904A-AB0E21B846DC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0C340FE7-F967-4DEB-BF30-8A8EF00917FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C340FE7-F967-4DEB-BF30-8A8EF00917FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C340FE7-F967-4DEB-BF30-8A8EF00917FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C340FE7-F967-4DEB-BF30-8A8EF00917FB}.Release|Any CPU.Build.0 = Release|Any CPU + {F6919E7E-3680-4356-B0BC-01CE5F4124C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F6919E7E-3680-4356-B0BC-01CE5F4124C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F6919E7E-3680-4356-B0BC-01CE5F4124C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F6919E7E-3680-4356-B0BC-01CE5F4124C5}.Release|Any CPU.Build.0 = Release|Any CPU + {A23EBAE9-531C-4B47-904A-AB0E21B846DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A23EBAE9-531C-4B47-904A-AB0E21B846DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A23EBAE9-531C-4B47-904A-AB0E21B846DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A23EBAE9-531C-4B47-904A-AB0E21B846DC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200.Dependencies.csproj b/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200.Dependencies.csproj new file mode 100644 index 00000000..a58e7b7a --- /dev/null +++ b/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200.Dependencies.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + enable + enable + + + + + + + + diff --git a/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Inputs.g.cs b/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Inputs.g.cs new file mode 100644 index 00000000..5345ae2c --- /dev/null +++ b/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Inputs.g.cs @@ -0,0 +1,61 @@ +// This code was generated by Hypar. +// Edits to this code will be overwritten the next time you run 'hypar init'. +// DO NOT EDIT THIS FILE. + +using Elements; +using Elements.GeoJSON; +using Elements.Geometry; +using Elements.Geometry.Solids; +using Elements.Validators; +using Elements.Serialization.JSON; +using Hypar.Functions; +using Hypar.Functions.Execution; +using Hypar.Functions.Execution.AWS; +using Hypar.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using Line = Elements.Geometry.Line; +using Polygon = Elements.Geometry.Polygon; + +namespace WallsLOD200 +{ + #pragma warning disable // Disable all warnings + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.1.21.0 (Newtonsoft.Json v13.0.0.0)")] + + public class WallsLOD200Inputs : ArgsBase + + { + [Newtonsoft.Json.JsonConstructor] + + public WallsLOD200Inputs(double @length, double @width, Dictionary modelInputKeys, string gltfKey, string elementsKey, string ifcKey): + base(modelInputKeys, gltfKey, elementsKey, ifcKey) + { + var validator = Validator.Instance.GetFirstValidatorForType(); + if(validator != null) + { + validator.PreConstruct(new object[]{ @length, @width}); + } + + this.Length = @length; + this.Width = @width; + + if(validator != null) + { + validator.PostConstruct(this); + } + } + + /// The Length. + [Newtonsoft.Json.JsonProperty("Length", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(1.0D, 10.0D)] + public double Length { get; set; } + + /// The Width. + [Newtonsoft.Json.JsonProperty("Width", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.Range(1.0D, 10.0D)] + public double Width { get; set; } + + } +} \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Outputs.g.cs b/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Outputs.g.cs new file mode 100644 index 00000000..58d0a273 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Outputs.g.cs @@ -0,0 +1,50 @@ +// This code was generated by Hypar. +// Edits to this code will be overwritten the next time you run 'hypar init'. +// DO NOT EDIT THIS FILE. + +using Elements; +using Elements.GeoJSON; +using Elements.Geometry; +using Hypar.Functions; +using Hypar.Functions.Execution; +using Hypar.Functions.Execution.AWS; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System.Collections.Generic; + +namespace WallsLOD200 +{ + public class WallsLOD200Outputs: SystemResults + { + /// + /// The volume. + /// + [JsonProperty("Volume")] + public double Volume { get; set; } + + /// + /// Construct a WallsLOD200Outputs with default inputs. + /// This should be used for testing only. + /// + public WallsLOD200Outputs() : base() + { + } + + /// + /// Construct a WallsLOD200Outputs specifying all inputs. + /// + /// + [JsonConstructor] + public WallsLOD200Outputs(double volume) : base() + { + this.Volume = volume; + + } + + public override string ToString() + { + var json = JsonConvert.SerializeObject(this); + return json; + } + } +} \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/global.json b/LayoutFunctions/WallsLOD200/global.json new file mode 100644 index 00000000..4aef4472 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/global.json @@ -0,0 +1,7 @@ + +{ + "sdk": { + "version": "6.0.400", + "rollForward": "latestMinor" + } +} \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/hypar.json b/LayoutFunctions/WallsLOD200/hypar.json new file mode 100644 index 00000000..cce97d5a --- /dev/null +++ b/LayoutFunctions/WallsLOD200/hypar.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://hypar.io/Schemas/Function.json", + "id": "35198423-a74c-4d58-9a9d-c88798dc6bfa", + "name": "Walls LOD 200", + "description": "The WallsLOD200 function.", + "language": "C#", + "model_dependencies": [ + { + "name": "Walls" + } + ], + "input_schema": { + "type": "object", + "properties": { + "Length": { + "type": "number", + "description": "The Length.", + "minimum": 1.0, + "maximum": 10.0, + "multipleOf": 1.0, + "$hyparUnitType": "length" + }, + "Width": { + "type": "number", + "description": "The Width.", + "minimum": 1.0, + "maximum": 10.0, + "multipleOf": 1.0, + "$hyparUnitType": "length" + } + } + }, + "outputs": [ + { + "unit_type": "volume", + "name": "Volume", + "description": "The volume.", + "type": "number" + } + ], + "repository_url": "https://github.com/hypar-io/function", + "last_updated": "0001-01-01T00:00:00", + "cli_version": "1.11.0-alpha.18" +} \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/src/Function.g.cs b/LayoutFunctions/WallsLOD200/src/Function.g.cs new file mode 100644 index 00000000..c5782202 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/src/Function.g.cs @@ -0,0 +1,73 @@ +// This code was generated by Hypar. +// Edits to this code will be overwritten the next time you run 'hypar init'. +// DO NOT EDIT THIS FILE. + +using Amazon.Lambda.Core; +using Hypar.Functions.Execution; +using Hypar.Functions.Execution.AWS; +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))] +namespace WallsLOD200 +{ + public class Function + { + // Cache the model store for use by subsequent + // executions of this lambda. + private UrlModelStore store; + + public async Task Handler(WallsLOD200Inputs args) + { + // Preload dependencies (if they exist), + // so that they are available during model deserialization. + + var sw = System.Diagnostics.Stopwatch.StartNew(); + var asmLocation = this.GetType().Assembly.Location; + var asmDir = Path.GetDirectoryName(asmLocation); + + // Explicitly load the dependencies project, it might have types + // that aren't used in the function but are necessary for correct + // deserialization. + var asmName = Path.GetFileNameWithoutExtension(asmLocation); + var depPath = Path.Combine(asmDir, $"{asmName}.Dependencies.dll"); + if(File.Exists(depPath)) + { + Console.WriteLine($"Loading dependencies assembly from: {depPath}..."); + Assembly.LoadFrom(depPath); + Console.WriteLine("Dependencies assembly loaded."); + } + + // Load all reference assemblies. + Console.WriteLine($"Loading all referenced assemblies."); + foreach (var asm in this.GetType().Assembly.GetReferencedAssemblies()) + { + try + { + Console.WriteLine($"Assembly Name: {asm.FullName}"); + Assembly.Load(asm); + } + catch (Exception e) + { + Console.WriteLine($"Failed to load {asm.FullName}"); + Console.WriteLine(e.Message); + } + } + sw.Stop(); + Console.WriteLine($"Time to load assemblies: {sw.Elapsed.TotalSeconds})"); + + if(this.store == null) + { + this.store = new UrlModelStore(); + } + + + var l = new InvocationWrapper (store, WallsLOD200.Execute); + var output = await l.InvokeAsync(args); + return output; + } + } +} \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs b/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs new file mode 100644 index 00000000..2b76669e --- /dev/null +++ b/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs @@ -0,0 +1,208 @@ +using Elements; +using System.Linq; +using Elements.Search; +using Elements.Geometry; +using System.Collections.Generic; +using System.Diagnostics; + +namespace WallsLOD200 +{ + public static partial class WallsLOD200 + { + /// + /// The WallsLOD200 function. + /// + /// The input model. + /// The arguments to the execution. + /// A WallsLOD200Outputs instance containing computed results and the model with any new elements. + public static WallsLOD200Outputs Execute(Dictionary inputModels, WallsLOD200Inputs input) + { + // Your code here. + Random random = new Random(21); + var height = 1.0; + var volume = input.Length * input.Width * height; + var output = new WallsLOD200Outputs(volume); + + var wallsModel = inputModels["Walls"]; + var walls = wallsModel.AllElementsOfType(); + var wallGroups = walls.GroupBy(w => w.CenterLine.Start.Z); + // var wallGroups = model.AllElementsOfType().GroupBy(w => w.Centerline.Start.Z); + foreach (var group in wallGroups) + { + var lines = UnifyLines(group.ToList().Select(g => g.CenterLine).ToList()); + var newwalls = lines.Select(mc => new StandardWall(mc, 0.1, 3, random.NextMaterial())); + output.Model.AddElements(newwalls); + } + + var rectangle = Polygon.Rectangle(input.Length, input.Width); + var mass = new Mass(rectangle, height); + output.Model.AddElement(mass); + return output; + } + + public static List UnifyLines(List lines) + { + + // Remove duplicate lines based on their hash codes + List dedupedlines = null;//RemoveDuplicateLines(lines); + + // Merge collinear lines that are touching or overlapping + List mergedLines = null;//MergeCollinearLines(dedupedlines); + + // // Merge collinear lines that are touching or overlapping + // List mergedLines2 = MergeCollinearLines2(dedupedlines); + + // Create stopwatch for timing + Stopwatch stopwatch = new Stopwatch(); + + // Number of iterations + int iterations = 1; + + // Lists to store elapsed times for each method + List removeDuplicateLinesTimes = new List(); + List mergeCollinearLinesTimes = new List(); + List removeDuplicateLines2Times = new List(); + List mergeCollinearLines2Times = new List(); + + for (int i = 0; i < iterations; i++) + { + + // Timing for RemoveDuplicateLines + stopwatch.Restart(); + dedupedlines = RemoveDuplicateLines(lines); + stopwatch.Stop(); + removeDuplicateLinesTimes.Add(stopwatch.Elapsed); + + // Timing for MergeCollinearLines2 + stopwatch.Restart(); + mergedLines = MergeCollinearLines(dedupedlines); + stopwatch.Stop(); + mergeCollinearLinesTimes.Add(stopwatch.Elapsed); + } + + // Calculate min, max, and average times for each method + TimeSpan removeDuplicateLinesMin = removeDuplicateLinesTimes.Min(); + TimeSpan removeDuplicateLinesMax = removeDuplicateLinesTimes.Max(); + TimeSpan removeDuplicateLinesAverage = TimeSpan.FromTicks((long)removeDuplicateLinesTimes.Average(t => t.Ticks)); + + TimeSpan mergeCollinearLinesMin = mergeCollinearLinesTimes.Min(); + TimeSpan mergeCollinearLinesMax = mergeCollinearLinesTimes.Max(); + TimeSpan mergeCollinearLinesAverage = TimeSpan.FromTicks((long)mergeCollinearLinesTimes.Average(t => t.Ticks)); + + // Print results + Console.WriteLine("RemoveDuplicateLines: Min - " + removeDuplicateLinesMin + ", Max - " + removeDuplicateLinesMax + ", Average - " + removeDuplicateLinesAverage); + Console.WriteLine("MergeCollinearLines: Min - " + mergeCollinearLinesMin + ", Max - " + mergeCollinearLinesMax + ", Average - " + mergeCollinearLinesAverage); + Console.WriteLine($"Merged {lines.Count} Lines into {mergedLines.Count}"); + + return mergedLines; + } + + private static List RemoveDuplicateLines(List lines) + { + HashSet uniqueLines = new HashSet(new LineEqualityComparer()); + + foreach (Line line in lines) + { + uniqueLines.Add(line); + } + + return uniqueLines.ToList(); + } + + static List> GroupLinesByCollinearity(List lines) + { + Dictionary collinearGroups = new Dictionary(); + List> lineGroups = new List>(); + int groupId = 0; + + foreach (var line in lines) + { + bool addedToGroup = false; + foreach (var kvp in collinearGroups) + { + if (line.IsCollinear(kvp.Value)) + { + // Add line to existing group + lineGroups[kvp.Key].Add(line); + addedToGroup = true; + break; + } + } + + if (!addedToGroup) + { + // Create new group + collinearGroups.Add(groupId, line); + lineGroups.Add(new List() { line }); + groupId++; + } + } + + return lineGroups; + } + + private static List MergeCollinearLines(List lines) + { + var groupedLines = GroupLinesByCollinearity(lines); + + List merged = new List(); + + foreach (var group in groupedLines) + { + List mergedLines = new List(group); + + bool linesMerged; + do + { + linesMerged = false; + for (int i = 0; i < mergedLines.Count; i++) + { + Line line = mergedLines[i]; + for (int j = i + 1; j < mergedLines.Count; j++) + { + Line otherLine = mergedLines[j]; + + if (line.IsCollinear(otherLine) && (line.TryGetOverlap(otherLine, out var overlap) || line.DistanceTo(otherLine) < 0.0001)) + { + // Merge collinear lines + Line mergedLine = line.MergedCollinearLine(otherLine); + + // Update the list with the merged line + mergedLines.RemoveAt(j); + mergedLines[i] = mergedLine; + + linesMerged = true; + break; // Exit the inner loop as we have merged the lines + } + } + if (linesMerged) + break; // Exit the outer loop to restart the merging process + } + } while (linesMerged); + merged.AddRange(mergedLines); + } + return merged; + } + + private class LineEqualityComparer : IEqualityComparer + { + public bool Equals(Line x, Line y) + { + // Check if start and end points of lines are equal + return (x.Start == y.Start && x.End == y.End) || (x.Start == y.End && x.End == y.Start); + } + + public int GetHashCode(Line obj) + { + // Compute hash code based on start and end points + unchecked + { + int hash = 17; + hash = hash * 23 + obj.Start.GetHashCode(); + hash = hash * 23 + obj.End.GetHashCode(); + return hash; + } + } + } + } +} \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/src/WallsLOD200.csproj b/LayoutFunctions/WallsLOD200/src/WallsLOD200.csproj new file mode 100644 index 00000000..14b8e3b4 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/src/WallsLOD200.csproj @@ -0,0 +1,13 @@ + + + + + + + + net6.0 + enable + enable + + + diff --git a/LayoutFunctions/WallsLOD200/test/FunctionTest.g.cs b/LayoutFunctions/WallsLOD200/test/FunctionTest.g.cs new file mode 100644 index 00000000..43aaabea --- /dev/null +++ b/LayoutFunctions/WallsLOD200/test/FunctionTest.g.cs @@ -0,0 +1,24 @@ +// This code was generated by Hypar. +// Edits to this code will be overwritten the next time you run 'hypar init'. +// DO NOT EDIT THIS FILE. + +using Xunit; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using Xunit.Abstractions; +using System; +using System.Collections.Generic; + +namespace WallsLOD200.Tests +{ + public class FunctionTests + { + private readonly ITestOutputHelper output; + + public FunctionTests(ITestOutputHelper output) + { + this.output = output; + } + } +} \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/test/Usings.cs b/LayoutFunctions/WallsLOD200/test/Usings.cs new file mode 100644 index 00000000..8c927eb7 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/test/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/test/WallsLOD200.Tests.csproj b/LayoutFunctions/WallsLOD200/test/WallsLOD200.Tests.csproj new file mode 100644 index 00000000..452618d5 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/test/WallsLOD200.Tests.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + From ebeddcc1a8a9e24c48fd849e25ec550b256319b3 Mon Sep 17 00:00:00 2001 From: James Bradley Date: Fri, 19 Apr 2024 15:53:12 -0400 Subject: [PATCH 2/6] remove default mass --- LayoutFunctions/WallsLOD200/hypar.json | 29 ------------------- .../WallsLOD200/src/WallsLOD200.cs | 20 ++++--------- 2 files changed, 5 insertions(+), 44 deletions(-) diff --git a/LayoutFunctions/WallsLOD200/hypar.json b/LayoutFunctions/WallsLOD200/hypar.json index cce97d5a..f96b9221 100644 --- a/LayoutFunctions/WallsLOD200/hypar.json +++ b/LayoutFunctions/WallsLOD200/hypar.json @@ -9,35 +9,6 @@ "name": "Walls" } ], - "input_schema": { - "type": "object", - "properties": { - "Length": { - "type": "number", - "description": "The Length.", - "minimum": 1.0, - "maximum": 10.0, - "multipleOf": 1.0, - "$hyparUnitType": "length" - }, - "Width": { - "type": "number", - "description": "The Width.", - "minimum": 1.0, - "maximum": 10.0, - "multipleOf": 1.0, - "$hyparUnitType": "length" - } - } - }, - "outputs": [ - { - "unit_type": "volume", - "name": "Volume", - "description": "The volume.", - "type": "number" - } - ], "repository_url": "https://github.com/hypar-io/function", "last_updated": "0001-01-01T00:00:00", "cli_version": "1.11.0-alpha.18" diff --git a/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs b/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs index 2b76669e..4f1ba930 100644 --- a/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs +++ b/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs @@ -17,16 +17,12 @@ public static partial class WallsLOD200 /// A WallsLOD200Outputs instance containing computed results and the model with any new elements. public static WallsLOD200Outputs Execute(Dictionary inputModels, WallsLOD200Inputs input) { - // Your code here. Random random = new Random(21); - var height = 1.0; - var volume = input.Length * input.Width * height; - var output = new WallsLOD200Outputs(volume); + var output = new WallsLOD200Outputs(); var wallsModel = inputModels["Walls"]; var walls = wallsModel.AllElementsOfType(); var wallGroups = walls.GroupBy(w => w.CenterLine.Start.Z); - // var wallGroups = model.AllElementsOfType().GroupBy(w => w.Centerline.Start.Z); foreach (var group in wallGroups) { var lines = UnifyLines(group.ToList().Select(g => g.CenterLine).ToList()); @@ -34,9 +30,6 @@ public static WallsLOD200Outputs Execute(Dictionary inputModels, output.Model.AddElements(newwalls); } - var rectangle = Polygon.Rectangle(input.Length, input.Width); - var mass = new Mass(rectangle, height); - output.Model.AddElement(mass); return output; } @@ -44,13 +37,10 @@ public static List UnifyLines(List lines) { // Remove duplicate lines based on their hash codes - List dedupedlines = null;//RemoveDuplicateLines(lines); + List dedupedlines = null; // Merge collinear lines that are touching or overlapping - List mergedLines = null;//MergeCollinearLines(dedupedlines); - - // // Merge collinear lines that are touching or overlapping - // List mergedLines2 = MergeCollinearLines2(dedupedlines); + List mergedLines = null; // Create stopwatch for timing Stopwatch stopwatch = new Stopwatch(); @@ -90,8 +80,8 @@ public static List UnifyLines(List lines) TimeSpan mergeCollinearLinesAverage = TimeSpan.FromTicks((long)mergeCollinearLinesTimes.Average(t => t.Ticks)); // Print results - Console.WriteLine("RemoveDuplicateLines: Min - " + removeDuplicateLinesMin + ", Max - " + removeDuplicateLinesMax + ", Average - " + removeDuplicateLinesAverage); - Console.WriteLine("MergeCollinearLines: Min - " + mergeCollinearLinesMin + ", Max - " + mergeCollinearLinesMax + ", Average - " + mergeCollinearLinesAverage); + Console.WriteLine($"RemoveDuplicateLines: Min - {removeDuplicateLinesMin}, Max - {removeDuplicateLinesMax}, Average - {removeDuplicateLinesAverage}"); + Console.WriteLine($"MergeCollinearLines: Min -{mergeCollinearLinesMin}, Max - {mergeCollinearLinesMax}, Average - {mergeCollinearLinesAverage}"); Console.WriteLine($"Merged {lines.Count} Lines into {mergedLines.Count}"); return mergedLines; From eaa0fd8af6488b416345c0df8d5ca56e31d7681c Mon Sep 17 00:00:00 2001 From: James Bradley Date: Fri, 19 Apr 2024 16:25:21 -0400 Subject: [PATCH 3/6] use levels --- LayoutFunctions/WallsLOD200/README.md | 3 - .../WallsLOD200/dependencies/Level.g.cs | 58 +++++++++++++++++++ .../dependencies/WallsLOD200Inputs.g.cs | 16 +---- .../dependencies/WallsLOD200Outputs.g.cs | 21 ------- LayoutFunctions/WallsLOD200/hypar.json | 7 +++ .../WallsLOD200/src/WallsLOD200.cs | 24 +++++--- 6 files changed, 84 insertions(+), 45 deletions(-) create mode 100644 LayoutFunctions/WallsLOD200/dependencies/Level.g.cs diff --git a/LayoutFunctions/WallsLOD200/README.md b/LayoutFunctions/WallsLOD200/README.md index 0378f2cc..c8d0bc34 100644 --- a/LayoutFunctions/WallsLOD200/README.md +++ b/LayoutFunctions/WallsLOD200/README.md @@ -6,15 +6,12 @@ The WallsLOD200 function. |Input Name|Type|Description| |---|---|---| -|Length|number|The Length.| -|Width|number|The Width.|
|Output Name|Type|Description| |---|---|---| -|Volume|Number|The volume.|
diff --git a/LayoutFunctions/WallsLOD200/dependencies/Level.g.cs b/LayoutFunctions/WallsLOD200/dependencies/Level.g.cs new file mode 100644 index 00000000..268525c0 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/dependencies/Level.g.cs @@ -0,0 +1,58 @@ +//---------------------- +// +// Generated using the NJsonSchema v10.1.21.0 (Newtonsoft.Json v13.0.0.0) (http://NJsonSchema.org) +// +//---------------------- +using Elements; +using Elements.GeoJSON; +using Elements.Geometry; +using Elements.Geometry.Solids; +using Elements.Spatial; +using Elements.Validators; +using Elements.Serialization.JSON; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using Line = Elements.Geometry.Line; +using Polygon = Elements.Geometry.Polygon; + +namespace Elements +{ + #pragma warning disable // Disable all warnings + + /// A horizontal datum representing a building level at a specific elevation. + [JsonConverter(typeof(Elements.Serialization.JSON.JsonInheritanceConverter), "discriminator")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.1.21.0 (Newtonsoft.Json v13.0.0.0)")] + public partial class Level : Element + { + [JsonConstructor] + public Level(double @elevation, double? @height, System.Guid? @planView, System.Guid @id = default, string @name = null) + : base(id, name) + { + this.Elevation = @elevation; + this.Height = @height; + this.PlanView = @planView; + } + + + // Empty constructor + public Level() + : base() + { + } + + [JsonProperty("Elevation", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public double Elevation { get; set; } + + /// The vertical distance from this level to the next. May be null for a top level, like a roof. + [JsonProperty("Height", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public double? Height { get; set; } + + /// The default plan view for this level + [JsonProperty("Plan View", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Guid? PlanView { get; set; } + + + } +} \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Inputs.g.cs b/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Inputs.g.cs index 5345ae2c..737b5337 100644 --- a/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Inputs.g.cs +++ b/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Inputs.g.cs @@ -29,17 +29,15 @@ public class WallsLOD200Inputs : ArgsBase { [Newtonsoft.Json.JsonConstructor] - public WallsLOD200Inputs(double @length, double @width, Dictionary modelInputKeys, string gltfKey, string elementsKey, string ifcKey): + public WallsLOD200Inputs(Dictionary modelInputKeys, string gltfKey, string elementsKey, string ifcKey): base(modelInputKeys, gltfKey, elementsKey, ifcKey) { var validator = Validator.Instance.GetFirstValidatorForType(); if(validator != null) { - validator.PreConstruct(new object[]{ @length, @width}); + validator.PreConstruct(new object[]{ }); } - this.Length = @length; - this.Width = @width; if(validator != null) { @@ -47,15 +45,5 @@ public WallsLOD200Inputs(double @length, double @width, DictionaryThe Length. - [Newtonsoft.Json.JsonProperty("Length", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - [System.ComponentModel.DataAnnotations.Range(1.0D, 10.0D)] - public double Length { get; set; } - - /// The Width. - [Newtonsoft.Json.JsonProperty("Width", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - [System.ComponentModel.DataAnnotations.Range(1.0D, 10.0D)] - public double Width { get; set; } - } } \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Outputs.g.cs b/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Outputs.g.cs index 58d0a273..1d911025 100644 --- a/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Outputs.g.cs +++ b/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Outputs.g.cs @@ -16,11 +16,6 @@ namespace WallsLOD200 { public class WallsLOD200Outputs: SystemResults { - /// - /// The volume. - /// - [JsonProperty("Volume")] - public double Volume { get; set; } /// /// Construct a WallsLOD200Outputs with default inputs. @@ -30,21 +25,5 @@ public WallsLOD200Outputs() : base() { } - /// - /// Construct a WallsLOD200Outputs specifying all inputs. - /// - /// - [JsonConstructor] - public WallsLOD200Outputs(double volume) : base() - { - this.Volume = volume; - - } - - public override string ToString() - { - var json = JsonConvert.SerializeObject(this); - return json; - } } } \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/hypar.json b/LayoutFunctions/WallsLOD200/hypar.json index f96b9221..93204827 100644 --- a/LayoutFunctions/WallsLOD200/hypar.json +++ b/LayoutFunctions/WallsLOD200/hypar.json @@ -7,8 +7,15 @@ "model_dependencies": [ { "name": "Walls" + }, + { + "name": "Levels", + "optional": true } ], + "element_types": [ + "https://schemas.hypar.io/Level.json" + ], "repository_url": "https://github.com/hypar-io/function", "last_updated": "0001-01-01T00:00:00", "cli_version": "1.11.0-alpha.18" diff --git a/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs b/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs index 4f1ba930..04f5cb02 100644 --- a/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs +++ b/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs @@ -20,14 +20,24 @@ public static WallsLOD200Outputs Execute(Dictionary inputModels, Random random = new Random(21); var output = new WallsLOD200Outputs(); - var wallsModel = inputModels["Walls"]; - var walls = wallsModel.AllElementsOfType(); - var wallGroups = walls.GroupBy(w => w.CenterLine.Start.Z); - foreach (var group in wallGroups) + if (inputModels.TryGetValue("Walls", out var wallsModel)) { - var lines = UnifyLines(group.ToList().Select(g => g.CenterLine).ToList()); - var newwalls = lines.Select(mc => new StandardWall(mc, 0.1, 3, random.NextMaterial())); - output.Model.AddElements(newwalls); + var walls = wallsModel.AllElementsOfType(); + var wallGroups = walls.GroupBy(w => w.AdditionalProperties["Level"] ?? w.Transform.Origin.Z); + + var levels = new List(); + if (inputModels.TryGetValue("Levels", out var levelsModel)) + { + levels = levelsModel.AllElementsOfType().ToList(); + } + + foreach (var group in wallGroups) + { + var level = levels.FirstOrDefault(l => l.Id.ToString() == group.Key.ToString()) ?? new Level(0, 3, null); + var lines = UnifyLines(group.ToList().Select(g => g.CenterLine).ToList()); + var newwalls = lines.Select(mc => new StandardWall(mc, 0.1, level.Height ?? 3, random.NextMaterial(), new Transform().Moved(0, 0, level.Elevation))); + output.Model.AddElements(newwalls); + } } return output; From 1d83b8ce550788c3c78d45b69bdd50c20120974c Mon Sep 17 00:00:00 2001 From: James Bradley Date: Mon, 22 Apr 2024 16:41:13 -0400 Subject: [PATCH 4/6] remove timing code --- .../WallsLOD200/src/WallsLOD200.cs | 52 +------------------ 1 file changed, 2 insertions(+), 50 deletions(-) diff --git a/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs b/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs index 04f5cb02..b82e4729 100644 --- a/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs +++ b/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs @@ -1,9 +1,5 @@ using Elements; -using System.Linq; -using Elements.Search; using Elements.Geometry; -using System.Collections.Generic; -using System.Diagnostics; namespace WallsLOD200 { @@ -45,54 +41,10 @@ public static WallsLOD200Outputs Execute(Dictionary inputModels, public static List UnifyLines(List lines) { - // Remove duplicate lines based on their hash codes - List dedupedlines = null; - + List dedupedlines = RemoveDuplicateLines(lines); // Merge collinear lines that are touching or overlapping - List mergedLines = null; - - // Create stopwatch for timing - Stopwatch stopwatch = new Stopwatch(); - - // Number of iterations - int iterations = 1; - - // Lists to store elapsed times for each method - List removeDuplicateLinesTimes = new List(); - List mergeCollinearLinesTimes = new List(); - List removeDuplicateLines2Times = new List(); - List mergeCollinearLines2Times = new List(); - - for (int i = 0; i < iterations; i++) - { - - // Timing for RemoveDuplicateLines - stopwatch.Restart(); - dedupedlines = RemoveDuplicateLines(lines); - stopwatch.Stop(); - removeDuplicateLinesTimes.Add(stopwatch.Elapsed); - - // Timing for MergeCollinearLines2 - stopwatch.Restart(); - mergedLines = MergeCollinearLines(dedupedlines); - stopwatch.Stop(); - mergeCollinearLinesTimes.Add(stopwatch.Elapsed); - } - - // Calculate min, max, and average times for each method - TimeSpan removeDuplicateLinesMin = removeDuplicateLinesTimes.Min(); - TimeSpan removeDuplicateLinesMax = removeDuplicateLinesTimes.Max(); - TimeSpan removeDuplicateLinesAverage = TimeSpan.FromTicks((long)removeDuplicateLinesTimes.Average(t => t.Ticks)); - - TimeSpan mergeCollinearLinesMin = mergeCollinearLinesTimes.Min(); - TimeSpan mergeCollinearLinesMax = mergeCollinearLinesTimes.Max(); - TimeSpan mergeCollinearLinesAverage = TimeSpan.FromTicks((long)mergeCollinearLinesTimes.Average(t => t.Ticks)); - - // Print results - Console.WriteLine($"RemoveDuplicateLines: Min - {removeDuplicateLinesMin}, Max - {removeDuplicateLinesMax}, Average - {removeDuplicateLinesAverage}"); - Console.WriteLine($"MergeCollinearLines: Min -{mergeCollinearLinesMin}, Max - {mergeCollinearLinesMax}, Average - {mergeCollinearLinesAverage}"); - Console.WriteLine($"Merged {lines.Count} Lines into {mergedLines.Count}"); + List mergedLines = MergeCollinearLines(dedupedlines); return mergedLines; } From 8586f8477e4a802172553c612ad014927c7cb56f Mon Sep 17 00:00:00 2001 From: James Bradley Date: Tue, 23 Apr 2024 10:50:05 -0400 Subject: [PATCH 5/6] model output --- LayoutFunctions/WallsLOD200/hypar.json | 1 + 1 file changed, 1 insertion(+) diff --git a/LayoutFunctions/WallsLOD200/hypar.json b/LayoutFunctions/WallsLOD200/hypar.json index 93204827..01ef4c72 100644 --- a/LayoutFunctions/WallsLOD200/hypar.json +++ b/LayoutFunctions/WallsLOD200/hypar.json @@ -4,6 +4,7 @@ "name": "Walls LOD 200", "description": "The WallsLOD200 function.", "language": "C#", + "model_output": "Walls:LOD200", "model_dependencies": [ { "name": "Walls" From 2c989e6b6e5f75eb03525009cdd7ec8c2694bef4 Mon Sep 17 00:00:00 2001 From: James Bradley Date: Tue, 23 Apr 2024 12:08:34 -0400 Subject: [PATCH 6/6] pr comments --- .../dependencies/LineEqualityComparer.cs | 22 ++++++++++++++++ .../WallsLOD200/src/WallsLOD200.cs | 25 ++----------------- 2 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 LayoutFunctions/WallsLOD200/dependencies/LineEqualityComparer.cs diff --git a/LayoutFunctions/WallsLOD200/dependencies/LineEqualityComparer.cs b/LayoutFunctions/WallsLOD200/dependencies/LineEqualityComparer.cs new file mode 100644 index 00000000..2507b19e --- /dev/null +++ b/LayoutFunctions/WallsLOD200/dependencies/LineEqualityComparer.cs @@ -0,0 +1,22 @@ +using Elements.Geometry; + +namespace WallsLOD200 +{ + + public class LineEqualityComparer : IEqualityComparer + { + public bool Equals(Line? x, Line? y) + { + if (x != null && y != null) + { + return (x.Start.IsAlmostEqualTo(y.Start) && x.End.IsAlmostEqualTo(y.End)) || (x.Start.IsAlmostEqualTo(y.End) && x.End.IsAlmostEqualTo(y.Start)); + } + return false; + } + + public int GetHashCode(Line obj) + { + return obj.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs b/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs index b82e4729..506c444d 100644 --- a/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs +++ b/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs @@ -51,7 +51,7 @@ public static List UnifyLines(List lines) private static List RemoveDuplicateLines(List lines) { - HashSet uniqueLines = new HashSet(new LineEqualityComparer()); + HashSet uniqueLines = new(new LineEqualityComparer()); foreach (Line line in lines) { @@ -114,7 +114,7 @@ private static List MergeCollinearLines(List lines) { Line otherLine = mergedLines[j]; - if (line.IsCollinear(otherLine) && (line.TryGetOverlap(otherLine, out var overlap) || line.DistanceTo(otherLine) < 0.0001)) + if (line.TryGetOverlap(otherLine, out var overlap) || line.DistanceTo(otherLine) < 0.0001) { // Merge collinear lines Line mergedLine = line.MergedCollinearLine(otherLine); @@ -135,26 +135,5 @@ private static List MergeCollinearLines(List lines) } return merged; } - - private class LineEqualityComparer : IEqualityComparer - { - public bool Equals(Line x, Line y) - { - // Check if start and end points of lines are equal - return (x.Start == y.Start && x.End == y.End) || (x.Start == y.End && x.End == y.Start); - } - - public int GetHashCode(Line obj) - { - // Compute hash code based on start and end points - unchecked - { - int hash = 17; - hash = hash * 23 + obj.Start.GetHashCode(); - hash = hash * 23 + obj.End.GetHashCode(); - return hash; - } - } - } } } \ No newline at end of file