Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 218 additions & 20 deletions src/MedWNetworkSim.App/MainWindow.xaml

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions src/MedWNetworkSim.App/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ public MainWindow()

public MainWindowViewModel ViewModel { get; }

private void NewNetwork_Click(object sender, RoutedEventArgs e)
{
ExecuteWithErrorHandling(ViewModel.CreateNewNetwork);
}

private void OpenFile_Click(object sender, RoutedEventArgs e)
{
var dialog = new OpenFileDialog
Expand Down Expand Up @@ -67,6 +72,46 @@ private void AutoArrange_Click(object sender, RoutedEventArgs e)
ExecuteWithErrorHandling(ViewModel.AutoArrangeNodes);
}

private void AddTrafficType_Click(object sender, RoutedEventArgs e)
{
ExecuteWithErrorHandling(ViewModel.AddTrafficDefinition);
}

private void RemoveTrafficType_Click(object sender, RoutedEventArgs e)
{
ExecuteWithErrorHandling(ViewModel.RemoveSelectedTrafficDefinition);
}

private void AddNode_Click(object sender, RoutedEventArgs e)
{
ExecuteWithErrorHandling(ViewModel.AddNode);
}

private void RemoveNode_Click(object sender, RoutedEventArgs e)
{
ExecuteWithErrorHandling(ViewModel.RemoveSelectedNode);
}

private void AddTrafficProfile_Click(object sender, RoutedEventArgs e)
{
ExecuteWithErrorHandling(ViewModel.AddTrafficProfileToSelectedNode);
}

private void RemoveTrafficProfile_Click(object sender, RoutedEventArgs e)
{
ExecuteWithErrorHandling(ViewModel.RemoveSelectedTrafficProfileFromNode);
}

private void AddEdge_Click(object sender, RoutedEventArgs e)
{
ExecuteWithErrorHandling(ViewModel.AddEdge);
}

private void RemoveEdge_Click(object sender, RoutedEventArgs e)
{
ExecuteWithErrorHandling(ViewModel.RemoveSelectedEdge);
}

private void NodeThumb_OnDragDelta(object sender, DragDeltaEventArgs e)
{
if (sender is Thumb { DataContext: NodeViewModel node })
Expand Down
230 changes: 196 additions & 34 deletions src/MedWNetworkSim.App/ViewModels/EdgeViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,150 @@ public sealed class EdgeViewModel : ObservableObject
private const double LabelWidth = 176d;
private const double LabelHeight = 58d;

public EdgeViewModel(EdgeModel model, NodeViewModel sourceNode, NodeViewModel targetNode)
private string id;
private string fromNodeId;
private string toNodeId;
private double time;
private double cost;
private double? capacity;
private bool isBidirectional;
private NodeViewModel? sourceNode;
private NodeViewModel? targetNode;

public EdgeViewModel(EdgeModel model, NodeViewModel? sourceNode, NodeViewModel? targetNode)
{
Model = model;
SourceNode = sourceNode;
TargetNode = targetNode;
id = model.Id;
fromNodeId = model.FromNodeId;
toNodeId = model.ToNodeId;
time = model.Time;
cost = model.Cost;
capacity = model.Capacity;
isBidirectional = model.IsBidirectional;
UpdateResolvedNodes(sourceNode, targetNode);
}

public event EventHandler? DefinitionChanged;

public string Id
{
get => id;
set
{
if (!SetProperty(ref id, value))
{
return;
}

DefinitionChanged?.Invoke(this, EventArgs.Empty);
}
}

public string FromNodeId
{
get => fromNodeId;
set
{
if (!SetProperty(ref fromNodeId, value))
{
return;
}

DefinitionChanged?.Invoke(this, EventArgs.Empty);
}
}

SourceNode.PropertyChanged += HandleEndpointChanged;
TargetNode.PropertyChanged += HandleEndpointChanged;
public string ToNodeId
{
get => toNodeId;
set
{
if (!SetProperty(ref toNodeId, value))
{
return;
}

DefinitionChanged?.Invoke(this, EventArgs.Empty);
}
}

public EdgeModel Model { get; }
public double Time
{
get => time;
set
{
if (!SetProperty(ref time, value))
{
return;
}

public NodeViewModel SourceNode { get; }
OnPropertyChanged(nameof(TotalCost));
OnPropertyChanged(nameof(SummaryLabel));
DefinitionChanged?.Invoke(this, EventArgs.Empty);
}
}

public NodeViewModel TargetNode { get; }
public double Cost
{
get => cost;
set
{
if (!SetProperty(ref cost, value))
{
return;
}

public double Time => Model.Time;
OnPropertyChanged(nameof(TotalCost));
OnPropertyChanged(nameof(SummaryLabel));
DefinitionChanged?.Invoke(this, EventArgs.Empty);
}
}

public double Cost => Model.Cost;
public double? Capacity
{
get => capacity;
set
{
if (!SetProperty(ref capacity, value))
{
return;
}

public double? Capacity => Model.Capacity;
OnPropertyChanged(nameof(CapacityLabel));
DefinitionChanged?.Invoke(this, EventArgs.Empty);
}
}

public bool IsBidirectional => Model.IsBidirectional;
public bool IsBidirectional
{
get => isBidirectional;
set
{
if (!SetProperty(ref isBidirectional, value))
{
return;
}

public string DirectionLabel => IsBidirectional ? "2-way" : "1-way";
OnPropertyChanged(nameof(DirectionLabel));
OnPropertyChanged(nameof(ArrowVisibility));
OnPropertyChanged(nameof(ArrowPoints));
DefinitionChanged?.Invoke(this, EventArgs.Empty);
}
}

public double TotalCost => Time + Cost;

public string DirectionLabel => IsBidirectional ? "2-way" : "1-way";

public string SummaryLabel => $"t {Time:0.##} | c {Cost:0.##} | tc {TotalCost:0.##}";

public string CapacityLabel => Capacity.HasValue
? $"cap {Capacity.Value:0.##}"
: "cap inf";

public Visibility ArrowVisibility => IsBidirectional || !HasValidEndpoints ? Visibility.Collapsed : Visibility.Visible;

public Visibility EdgeVisibility => HasValidEndpoints ? Visibility.Visible : Visibility.Collapsed;

public double X1 => GetSegmentEndpoints().start.X;

public double Y1 => GetSegmentEndpoints().start.Y;
Expand All @@ -50,19 +166,11 @@ public EdgeViewModel(EdgeModel model, NodeViewModel sourceNode, NodeViewModel ta

public double LabelTop => ((Y1 + Y2) / 2d) - (LabelHeight / 2d);

public string SummaryLabel => $"t {Time:0.##} | c {Cost:0.##} | tc {TotalCost:0.##}";

public string CapacityLabel => Capacity.HasValue
? $"cap {Capacity.Value:0.##}"
: "cap inf";

public Visibility ArrowVisibility => IsBidirectional ? Visibility.Collapsed : Visibility.Visible;

public string ArrowPoints
{
get
{
if (IsBidirectional)
if (IsBidirectional || !HasValidEndpoints)
{
return string.Empty;
}
Expand Down Expand Up @@ -97,27 +205,76 @@ public string ArrowPoints
}
}

public void ResolveNodes(IReadOnlyDictionary<string, NodeViewModel> nodeMap)
{
nodeMap.TryGetValue(FromNodeId, out var resolvedSourceNode);
nodeMap.TryGetValue(ToNodeId, out var resolvedTargetNode);
UpdateResolvedNodes(resolvedSourceNode, resolvedTargetNode);
}

public EdgeModel ToModel()
{
return new EdgeModel
{
Id = Model.Id,
FromNodeId = Model.FromNodeId,
ToNodeId = Model.ToNodeId,
Time = Model.Time,
Cost = Model.Cost,
Capacity = Model.Capacity,
IsBidirectional = Model.IsBidirectional
Id = Id,
FromNodeId = FromNodeId,
ToNodeId = ToNodeId,
Time = Time,
Cost = Cost,
Capacity = Capacity,
IsBidirectional = IsBidirectional
};
}

private bool HasValidEndpoints => sourceNode is not null && targetNode is not null;

private void UpdateResolvedNodes(NodeViewModel? newSourceNode, NodeViewModel? newTargetNode)
{
if (ReferenceEquals(sourceNode, newSourceNode) && ReferenceEquals(targetNode, newTargetNode))
{
return;
}

if (sourceNode is not null)
{
sourceNode.PropertyChanged -= HandleEndpointChanged;
}

if (targetNode is not null)
{
targetNode.PropertyChanged -= HandleEndpointChanged;
}

sourceNode = newSourceNode;
targetNode = newTargetNode;

if (sourceNode is not null)
{
sourceNode.PropertyChanged += HandleEndpointChanged;
}

if (targetNode is not null)
{
targetNode.PropertyChanged += HandleEndpointChanged;
}

OnGeometryChanged();
OnPropertyChanged(nameof(EdgeVisibility));
OnPropertyChanged(nameof(ArrowVisibility));
}

private (Point start, Point end) GetSegmentEndpoints()
{
var source = new Point(SourceNode.CenterX, SourceNode.CenterY);
var target = new Point(TargetNode.CenterX, TargetNode.CenterY);
if (sourceNode is null || targetNode is null)
{
return (new Point(0d, 0d), new Point(0d, 0d));
}

var source = new Point(sourceNode.CenterX, sourceNode.CenterY);
var target = new Point(targetNode.CenterX, targetNode.CenterY);

var outbound = FindRectangleIntersection(source, target, SourceNode.Width / 2d, SourceNode.Height / 2d);
var inbound = FindRectangleIntersection(target, source, TargetNode.Width / 2d, TargetNode.Height / 2d);
var outbound = FindRectangleIntersection(source, target, sourceNode.Width / 2d, sourceNode.Height / 2d);
var inbound = FindRectangleIntersection(target, source, targetNode.Width / 2d, targetNode.Height / 2d);
return (outbound, inbound);
}

Expand Down Expand Up @@ -145,6 +302,11 @@ private void HandleEndpointChanged(object? sender, PropertyChangedEventArgs e)
return;
}

OnGeometryChanged();
}

private void OnGeometryChanged()
{
OnPropertyChanged(nameof(X1));
OnPropertyChanged(nameof(Y1));
OnPropertyChanged(nameof(X2));
Expand Down
Loading
Loading