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..c8d0bc34 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/README.md @@ -0,0 +1,19 @@ + + +# Walls LOD 200 + +The WallsLOD200 function. + +|Input Name|Type|Description| +|---|---|---| + + +
+ +|Output Name|Type|Description| +|---|---|---| + + +
+ +## 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/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/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/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..737b5337 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Inputs.g.cs @@ -0,0 +1,49 @@ +// 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(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[]{ }); + } + + + if(validator != null) + { + validator.PostConstruct(this); + } + } + + } +} \ 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..1d911025 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/dependencies/WallsLOD200Outputs.g.cs @@ -0,0 +1,29 @@ +// 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 + { + + /// + /// Construct a WallsLOD200Outputs with default inputs. + /// This should be used for testing only. + /// + public WallsLOD200Outputs() : base() + { + } + + } +} \ 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..01ef4c72 --- /dev/null +++ b/LayoutFunctions/WallsLOD200/hypar.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://hypar.io/Schemas/Function.json", + "id": "35198423-a74c-4d58-9a9d-c88798dc6bfa", + "name": "Walls LOD 200", + "description": "The WallsLOD200 function.", + "language": "C#", + "model_output": "Walls:LOD200", + "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" +} \ 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..506c444d --- /dev/null +++ b/LayoutFunctions/WallsLOD200/src/WallsLOD200.cs @@ -0,0 +1,139 @@ +using Elements; +using Elements.Geometry; + +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) + { + Random random = new Random(21); + var output = new WallsLOD200Outputs(); + + if (inputModels.TryGetValue("Walls", out var wallsModel)) + { + 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; + } + + public static List UnifyLines(List lines) + { + // Remove duplicate lines based on their hash codes + List dedupedlines = RemoveDuplicateLines(lines); + // Merge collinear lines that are touching or overlapping + List mergedLines = MergeCollinearLines(dedupedlines); + + return mergedLines; + } + + private static List RemoveDuplicateLines(List lines) + { + HashSet uniqueLines = new(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.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; + } + } +} \ 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 + + + + + + + +