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
+
+
+
+
+
+
+
+