diff --git a/AIStudio.Wpf.DiagramDesigner/Layout/Configuration.cs b/AIStudio.Wpf.DiagramDesigner/Layout/Configuration.cs
new file mode 100644
index 0000000..c49336c
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner/Layout/Configuration.cs
@@ -0,0 +1,69 @@
+//using System;
+
+//namespace AIStudio.Wpf.DiagramDesigner.Layout
+//{
+// public class Configuration
+// {
+// ///
+// /// The network whose nodes are to be repositioned.
+// ///
+// public DiagramViewModel Network { get; set; }
+
+// ///
+// /// A time modifier that is used to speed up, or slow down, time during the simulation.
+// /// A greater time modifier speeds up the physics simulation, at the cost of accuracy and stability.
+// ///
+// public float TimeModifier { get; set; } = 3.5f;
+
+// ///
+// /// Number of updates per iteration.
+// /// Increasing this number increases the accuracy of the physics simulation at the cost of performance.
+// ///
+// public int UpdatesPerIteration { get; set; } = 1;
+
+// ///
+// /// How strongly should nodes push eachother away?
+// /// A greater NodeRepulsionForce increases the distance between nodes.
+// ///
+// public float NodeRepulsionForce { get; set; } = 100;
+
+// ///
+// /// A function that maps each connection onto the equilibrium distance of its corresponding spring.
+// /// A greater equilibrium distance increases the distance between the two connected endpoints.
+// ///
+// public Func EquilibriumDistance { get; set; } = conn => 100;
+
+// ///
+// /// A function that maps each connection onto the springiness/stiffness constant of its corresponding spring.
+// /// (c.f. Hooke's law)
+// ///
+// public Func SpringConstant { get; set; } = conn => 1;
+
+// ///
+// /// A function that maps each connection onto the strength of its row force.
+// /// Since inputs/outputs are on the left/right of a node, networks tend to be layed out horizontally.
+// /// The row force is added onto the endpoints of the connection to make the nodes end up in a more horizontal layout.
+// ///
+// public Func RowForce { get; set; } = conn => 100;
+
+// ///
+// /// A function that maps each node onto its mass in the physics simulation.
+// /// Greater mass makes the node harder to move.
+// ///
+// public Func NodeMass { get; set; } = node => 10;
+
+// ///
+// /// The friction coefficient is used to control friction forces in the simulation.
+// /// Greater friction makes the simulation converge faster, as it slows nodes down when
+// /// they are swinging around. If the friction is too high, the nodes will stop moving before
+// /// they reach their optimal position or might not even move at all.
+// ///
+// public Func FrictionCoefficient { get; set; } = node => 2.5f;
+
+// ///
+// /// A predicate function that specifies whether or not a node is fixed.
+// /// Fixed nodes do not get moved in the simulation.
+// ///
+// public Func IsFixedNode { get; set; } = node => false;
+// }
+//}
diff --git a/AIStudio.Wpf.DiagramDesigner/Layout/Engine.cs b/AIStudio.Wpf.DiagramDesigner/Layout/Engine.cs
new file mode 100644
index 0000000..9c0482a
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner/Layout/Engine.cs
@@ -0,0 +1,123 @@
+//using System;
+//using System.Collections.Generic;
+//using System.Linq;
+//using System.Windows;
+//using AIStudio.Wpf.DiagramDesigner.Geometrys;
+
+//namespace AIStudio.Wpf.DiagramDesigner.Layout
+//{
+// internal class Engine
+// {
+// internal void ApplyRandomShift(DiagramViewModel network)
+// {
+// Random random = new Random();
+// foreach (var node in network.Items.OfType())
+// {
+// node.Position = node.Position + new VectorBase(random.NextDouble(), random.NextDouble());
+// }
+// }
+
+// internal void Update(int deltaTMillis, IState state, Configuration config)
+// {
+// // Calculate forces
+// int nodeCount = config.Network.Items.Count;
+// IList<(DesignerItemViewModelBase, VectorBase)> nodeForces = new List<(DesignerItemViewModelBase, VectorBase)>(nodeCount);
+
+// foreach (var node in config.Network.Items.OfType())
+// {
+// if (!config.IsFixedNode(node))
+// {
+// nodeForces.Add((node, CalculateNodeForce(node, state, config)));
+// }
+// }
+
+// // Apply forces
+// foreach (var (node, force) in nodeForces)
+// {
+// VectorBase speed = state.GetNodeSpeed(node);
+// VectorBase pos = state.GetNodePosition(node);
+// double deltaT = deltaTMillis / 1000.0;
+// state.SetNodePosition(node, pos + ((speed * deltaT) + (force * deltaT * deltaT / 2)));
+// state.SetNodeSpeed(node, speed + ((force / config.NodeMass(node)) * deltaT));
+// }
+// }
+
+// private VectorBase CalculateNodeForce(DesignerItemViewModelBase node, IState state, Configuration config)
+// {
+// VectorBase force = new VectorBase();
+
+// // Calculate total force on node from endpoints
+// if (node.InputConnectors.Count > 0 || node.OutputConnectors.Count > 0)
+// {
+// force += node.InputConnectors.Concat(node.OutputConnectors)
+// .Select(e => CalculateEndpointForce(e, state, config))
+// .Aggregate((v1, v2) => v1 + v2);
+// }
+
+// // Apply node repulsion force so nodes don't overlap
+// var nodeCenter = state.GetNodePosition(node) + (new VectorBase(node.Size.Width, node.Size.Height) / 2.0);
+// foreach (var otherNode in config.Network.Items.OfType())
+// {
+// if (node == otherNode)
+// {
+// continue;
+// }
+
+// var otherNodeCenter = state.GetNodePosition(otherNode) + (new VectorBase(otherNode.Size.Width, otherNode.Size.Height) / 2.0);
+// var thisToOther = otherNodeCenter - nodeCenter;
+// var dist = thisToOther.Length;
+// thisToOther.Normalize();
+
+// var repulsionX = thisToOther.X * (-1 * ((node.Size.Width + otherNode.Size.Width) / 2) / dist);
+// var repulsionY = thisToOther.Y * (-1 * ((node.Size.Height + otherNode.Size.Height) / 2) / dist);
+// force += new VectorBase(repulsionX, repulsionY) * config.NodeRepulsionForce;
+// }
+
+// // Apply friction to make the movement converge to a stable state.
+// float gravity = 9.8f;
+// float normalForce = gravity * config.NodeMass(node);
+// float kineticFriction = normalForce * config.FrictionCoefficient(node);
+// VectorBase frictionVector = new VectorBase();
+// var nodeSpeed = state.GetNodeSpeed(node);
+// if (nodeSpeed.Length > 0)
+// {
+// frictionVector = new VectorBase(nodeSpeed.X, nodeSpeed.Y);
+// frictionVector.Normalize();
+// frictionVector *= -1.0 * kineticFriction;
+// }
+// force += frictionVector;
+
+// return force;
+// }
+
+// private VectorBase CalculateEndpointForce(ConnectorInfoBase endpoint, IState state, Configuration config)
+// {
+// var pos = state.GetEndpointPosition(endpoint);
+
+// VectorBase force = new VectorBase();
+
+// foreach (var conn in endpoint.Connections.Items)
+// {
+// var otherSide = conn.Input == endpoint ? (ConnectorInfoBase)conn.Output : conn.Input;
+// var otherSidePos = state.GetEndpointPosition(otherSide);
+// var dist = (otherSidePos - pos).Length;
+// var angle = Math.Acos((otherSidePos.X - pos.X) / dist);
+// if (otherSidePos.Y < pos.Y)
+// {
+// angle *= -1.0;
+// }
+
+// // Put a spring between connected endpoints.
+// var hookForce = (dist - config.EquilibriumDistance(conn)) * config.SpringConstant(conn);
+// force += new VectorBase(Math.Cos(angle), Math.Sin(angle)) * hookForce;
+
+// // Try to 'straighten' out the graph horizontally.
+// var isLeftSide = endpoint.Orientation == ConnectorOrientation.Left;
+// var rowForce = (isLeftSide ? 1 : -1) * config.RowForce(conn);
+// force.X += rowForce;
+// }
+
+// return force;
+// }
+// }
+//}
diff --git a/AIStudio.Wpf.DiagramDesigner/Layout/ForceDirectedLayouter.cs b/AIStudio.Wpf.DiagramDesigner/Layout/ForceDirectedLayouter.cs
new file mode 100644
index 0000000..cf23015
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner/Layout/ForceDirectedLayouter.cs
@@ -0,0 +1,81 @@
+//using System;
+//using System.Threading;
+//using System.Threading.Tasks;
+//using System.Windows;
+
+//namespace AIStudio.Wpf.DiagramDesigner.Layout
+//{
+// ///
+// /// Reposition the nodes in a network using a physics-based approach.
+// /// The nodes are interpreted as point masses, and the connections are represented
+// /// by springs. This system, along with a few additional forces such as friction and a
+// /// horizontal force, is then simulated to calculate the new position of the nodes.
+// ///
+// public class ForceDirectedLayouter
+// {
+// ///
+// /// Layout the nodes in the network.
+// ///
+// /// The configuration to use.
+// /// The maximum amount of iterations after which the physics simulation ends.
+// public void Layout(Configuration config, int maxIterations)
+// {
+// var engine = new Engine();
+// var state = new BufferedState();
+
+// // Move each node so no two nodes have the exact same position.
+// engine.ApplyRandomShift(config.Network);
+
+// int deltaT = (int)Math.Ceiling(10.0 / (double)config.UpdatesPerIteration);
+// for (int i = 0; i < maxIterations * config.UpdatesPerIteration; i++)
+// {
+// engine.Update(deltaT, state, config);
+// }
+
+// foreach (var newNodePosition in state.NodePositions)
+// {
+// newNodePosition.Key.Position = new Point(newNodePosition.Value.X, newNodePosition.Value.Y);
+// }
+// }
+
+// ///
+// /// Layout the nodes in the network, updating the user interface at each iteration.
+// /// This method, contrary to Layout(), lets users see the simulation as it happens.
+// /// The cancellation token should be used to end the simulation.
+// ///
+// /// The configuration to use.
+// /// A cancellation token to end the layout process.
+// /// The async task
+// public async Task LayoutAsync(Configuration config, CancellationToken token)
+// {
+// var engine = new Engine();
+// var state = new LiveState();
+
+// // Move each node so no two nodes have the exact same position.
+// engine.ApplyRandomShift(config.Network);
+
+// DateTime start = DateTime.Now;
+// TimeSpan t = TimeSpan.Zero;
+// do
+// {
+// // Current real time
+// var newT = DateTime.Now - start;
+// var deltaT = newT - t;
+
+// // Current modified time
+// //int virtT = (int)(t.Milliseconds * Settings.TimeModifier);
+// int virtDeltaT = (int)(deltaT.Milliseconds * config.TimeModifier);
+// int virtDeltaTPerUpdate = virtDeltaT / config.UpdatesPerIteration;
+// for (int i = 0; i < config.UpdatesPerIteration; i++)
+// {
+// // Modified time in this update step
+// engine.Update(virtDeltaTPerUpdate, state, config);
+// }
+
+// t = newT;
+
+// await Task.Delay(14, token);
+// } while (!token.IsCancellationRequested);
+// }
+// }
+//}
diff --git a/AIStudio.Wpf.DiagramDesigner/Layout/State.cs b/AIStudio.Wpf.DiagramDesigner/Layout/State.cs
new file mode 100644
index 0000000..5691571
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner/Layout/State.cs
@@ -0,0 +1,100 @@
+//using System.Collections.Generic;
+//using System.Windows;
+//using AIStudio.Wpf.DiagramDesigner.Geometrys;
+
+//namespace AIStudio.Wpf.DiagramDesigner.Layout
+//{
+// internal interface IState
+// {
+// VectorBase GetNodePosition(DesignerItemViewModelBase node);
+// void SetNodePosition(DesignerItemViewModelBase node, VectorBase pos);
+// VectorBase GetEndpointPosition(ConnectorInfoBase endpoint);
+// VectorBase GetNodeSpeed(DesignerItemViewModelBase node);
+// void SetNodeSpeed(DesignerItemViewModelBase node, VectorBase speed);
+// }
+
+// internal class BufferedState : IState
+// {
+// private readonly Dictionary _nodePositions = new Dictionary();
+// private readonly Dictionary _endpointRelativePositions = new Dictionary();
+// public IEnumerable> NodePositions => _nodePositions;
+
+// private readonly Dictionary _nodeSpeeds = new Dictionary();
+
+// public VectorBase GetNodePosition(DesignerItemViewModelBase node)
+// {
+// if (!_nodePositions.TryGetValue(node, out VectorBase result))
+// {
+// result = new VectorBase(node.Position.X, node.Position.Y);
+// }
+
+// return result;
+// }
+
+// public void SetNodePosition(DesignerItemViewModelBase node, VectorBase pos)
+// {
+// _nodePositions[node] = pos;
+// }
+
+// public VectorBase GetEndpointPosition(ConnectorInfoBase endpoint)
+// {
+// if (!_endpointRelativePositions.TryGetValue(endpoint, out VectorBase result))
+// {
+// result = new VectorBase(endpoint.MiddlePosition.X, endpoint.MiddlePosition.Y) - GetNodePosition(endpoint.Parent);
+// _endpointRelativePositions[endpoint] = result;
+// }
+
+// return result + GetNodePosition(endpoint.Parent as DesignerItemViewModelBase);
+// }
+
+// public VectorBase GetNodeSpeed(DesignerItemViewModelBase node)
+// {
+// if (!_nodeSpeeds.TryGetValue(node, out VectorBase result))
+// {
+// result = new VectorBase(0, 0);
+// }
+
+// return result;
+// }
+
+// public void SetNodeSpeed(DesignerItemViewModelBase node, VectorBase speed)
+// {
+// _nodeSpeeds[node] = speed;
+// }
+// }
+
+// internal class LiveState : IState
+// {
+// private readonly Dictionary _nodeSpeeds = new Dictionary();
+
+// public VectorBase GetNodePosition(DesignerItemViewModelBase node)
+// {
+// return new VectorBase(node.Position.X, node.Position.Y);
+// }
+
+// public void SetNodePosition(DesignerItemViewModelBase node, VectorBase pos)
+// {
+// node.Position = new Point(pos.X, pos.Y);
+// }
+
+// public VectorBase GetEndpointPosition(ConnectorInfoBase endpoint)
+// {
+// return new VectorBase(endpoint.MiddlePosition.X, endpoint.MiddlePosition.Y);
+// }
+
+// public VectorBase GetNodeSpeed(DesignerItemViewModelBase node)
+// {
+// if (!_nodeSpeeds.TryGetValue(node, out VectorBase result))
+// {
+// result = new VectorBase(0, 0);
+// }
+
+// return result;
+// }
+
+// public void SetNodeSpeed(DesignerItemViewModelBase node, VectorBase speed)
+// {
+// _nodeSpeeds[node] = speed;
+// }
+// }
+//}
diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DesignerItemViewModelBase.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DesignerItemViewModelBase.cs
index dff8e2d..483812c 100644
--- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DesignerItemViewModelBase.cs
+++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DesignerItemViewModelBase.cs
@@ -6,6 +6,7 @@ using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
+using AIStudio.Wpf.DiagramDesigner.Controls;
using AIStudio.Wpf.DiagramDesigner.Geometrys;
using AIStudio.Wpf.DiagramDesigner.Models;
@@ -442,7 +443,11 @@ namespace AIStudio.Wpf.DiagramDesigner
{
get
{
- return new PointBase(Left, Top);
+ return TopLeft;
+ }
+ set
+ {
+ TopLeft = value;
}
}
@@ -625,6 +630,22 @@ namespace AIStudio.Wpf.DiagramDesigner
}
}
+ public List InputConnectors
+ {
+ get
+ {
+ return Connectors.Where(p => p.Orientation == ConnectorOrientation.Left || p.Orientation == ConnectorOrientation.Top).ToList();
+ }
+ }
+
+ public List OutputConnectors
+ {
+ get
+ {
+ return Connectors.Where(p => p.Orientation == ConnectorOrientation.Right || p.Orientation == ConnectorOrientation.Bottom).ToList();
+ }
+ }
+
protected ObservableCollection menuOptions;
public IEnumerable MenuOptions
{