diff --git a/src/Artemis.Core/Artemis.Core.csproj.DotSettings b/src/Artemis.Core/Artemis.Core.csproj.DotSettings
index a18836e5e..d42d584d5 100644
--- a/src/Artemis.Core/Artemis.Core.csproj.DotSettings
+++ b/src/Artemis.Core/Artemis.Core.csproj.DotSettings
@@ -93,4 +93,5 @@
True
True
True
+ True
True
\ No newline at end of file
diff --git a/src/Artemis.Core/VisualScripting/Extensions/NodeExtension.cs b/src/Artemis.Core/VisualScripting/Extensions/NodeExtension.cs
new file mode 100644
index 000000000..5c187a4c8
--- /dev/null
+++ b/src/Artemis.Core/VisualScripting/Extensions/NodeExtension.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Linq;
+
+namespace Artemis.Core;
+
+public static class NodeExtension
+{
+ #region Methods
+
+ public static double EstimateHeight(this INode node)
+ {
+ const double PIN_HEIGHT = 26;
+ const double TITLE_HEIGHT = 46;
+
+ int inputPinCount = node.Pins.Count(x => x.Direction == PinDirection.Input)
+ + node.PinCollections.Where(x => x.Direction == PinDirection.Input).Sum(x => x.Count() + 1);
+ int outputPinCount = node.Pins.Count(x => x.Direction == PinDirection.Output)
+ + node.PinCollections.Where(x => x.Direction == PinDirection.Output).Sum(x => x.Count() + 1);
+
+ return TITLE_HEIGHT + (Math.Max(inputPinCount, outputPinCount) * PIN_HEIGHT);
+ }
+
+ public static double EstimateWidth(this INode node) => 120; // DarthAffe 13.09.2022: For now just assume they are all the same size
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/VisualScripting/Extensions/NodeScriptExtension.cs b/src/Artemis.Core/VisualScripting/Extensions/NodeScriptExtension.cs
new file mode 100644
index 000000000..f0961dfb6
--- /dev/null
+++ b/src/Artemis.Core/VisualScripting/Extensions/NodeScriptExtension.cs
@@ -0,0 +1,88 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Artemis.Core;
+
+public static class NodeScriptExtension
+{
+ #region Methods
+
+ public static void Organize(this NodeScript nodeScript)
+ {
+ const double SPACING_HORIZONTAL = 160;
+ const double SPACING_VERTICAL = 20;
+
+ Dictionary levels = nodeScript.Nodes.ToDictionary(node => node, _ => -1);
+
+ List currentLevelNodes = nodeScript.Nodes.Where(x => x.IsExitNode).ToList();
+ foreach (INode currentLevelNode in currentLevelNodes)
+ levels[currentLevelNode] = 0; // DarthAffe 13.09.2022: Init-exit nodes as zero
+
+ int currentLevel = 1;
+ while (currentLevelNodes.Count > 0)
+ {
+ List nextLevelNodes = currentLevelNodes.SelectMany(node => node.Pins
+ .Where(x => x.Direction == PinDirection.Input)
+ .SelectMany(x => x.ConnectedTo)
+ .Select(x => x.Node)
+ .Concat(node.PinCollections
+ .Where(x => x.Direction == PinDirection.Input)
+ .SelectMany(x => x)
+ .SelectMany(x => x.ConnectedTo)
+ .Select(x => x.Node)))
+ .Distinct()
+ .ToList();
+
+ foreach (INode nextLevelNode in nextLevelNodes)
+ if (currentLevel > levels[nextLevelNode])
+ levels[nextLevelNode] = currentLevel;
+
+ currentLevelNodes = nextLevelNodes;
+ currentLevel++;
+ }
+
+ void LayoutLevel(IList nodes, double posX, double posY)
+ {
+ foreach (INode node in nodes)
+ {
+ node.X = posX;
+ node.Y = posY;
+
+ posY += SPACING_VERTICAL + node.EstimateHeight();
+ }
+ }
+
+ List? unusedNodes = null;
+ double unusedPosY = 0;
+ double level0Width = 0;
+
+ double positionX = 0;
+ foreach (IGrouping levelGroup in levels.GroupBy(x => x.Value, x => x.Key).OrderBy(x => x.Key))
+ {
+ List nodes = levelGroup.ToList();
+ double levelHeight = nodes.Sum(x => x.EstimateHeight()) + ((nodes.Count - 1) * SPACING_VERTICAL);
+ double levelWidth = nodes.Max(x => x.EstimateWidth());
+ double positionY = -(levelHeight / 2.0);
+
+ if (levelGroup.Key == -1)
+ {
+ unusedNodes = nodes;
+ unusedPosY = positionY;
+ }
+ else
+ {
+ if (levelGroup.Key == 0)
+ level0Width = levelWidth;
+
+ LayoutLevel(nodes, positionX, positionY);
+
+ positionX -= SPACING_HORIZONTAL + levelWidth;
+ }
+ }
+
+ if (unusedNodes != null)
+ LayoutLevel(unusedNodes, level0Width + (SPACING_HORIZONTAL / 2.0), unusedPosY);
+ }
+
+ #endregion
+}
\ No newline at end of file