diff --git a/FlowForge.sln.DotSettings.user b/FlowForge.sln.DotSettings.user index a7132a1f49d791829c15f644cbd531f3e9b159d7..38314764b54d7bc9c8ca3de9692c8f321a9f2066 100644 --- a/FlowForge.sln.DotSettings.user +++ b/FlowForge.sln.DotSettings.user @@ -1,3 +1,8 @@ <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABidirectionalGraph_002Ecs_002Fl_003AC_0021_003FUsers_003Fmp455017_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fa8e81c4118651b5932b855cf99986877c2c24685d864158c9053e4f62e213fce_003FBidirectionalGraph_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> - <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIDictionary_00602_002Ecs_002Fl_003AC_0021_003FUsers_003Fmp455017_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb756d57b94fb4ab9aa95a9b2a97fce1fa24ea0_003Fa1_003F8276683c_003FIDictionary_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary> \ No newline at end of file + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEdge_002Ecs_002Fl_003AC_0021_003FUsers_003Ftimon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fc6dec8134d4e5115e30fcbbde23a657edc52dc4585c5673fe87fae89530f2_003FEdge_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIDictionary_00602_002Ecs_002Fl_003AC_0021_003FUsers_003Fmp455017_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb756d57b94fb4ab9aa95a9b2a97fce1fa24ea0_003Fa1_003F8276683c_003FIDictionary_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANode_002Ecs_002Fl_003AC_0021_003FUsers_003Ftimon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F362e998b40631a36346249d25c3c8c712372e68c65f251adc18f8931b4a77a32_003FNode_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANode_002Ecs_002Fl_003AC_0021_003FUsers_003Ftimon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F50b63217dc36f9d6edcabbd75adb3a7f68f1ebd3b4645b30614b43a9eda189_003FNode_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASplineRouter_002Ecs_002Fl_003AC_0021_003FUsers_003Ftimon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fb2961a4f945481bf9a55f7d27e451184c68497a74672874e415414929e5d53a_003FSplineRouter_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> + <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASugiyamaLayoutSettings_002Ecs_002Fl_003AC_0021_003FUsers_003Ftimon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fe49262ac60bbccd8c34311e852def374298e4cf859347b2c83e6a6079f4e7e_003FSugiyamaLayoutSettings_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary> \ No newline at end of file diff --git a/FordFulkersonAlgorithm.cs b/FordFulkersonAlgorithm.cs index ac1e578f1e372ceecfe82185f0775272aa045f04..91ddedb45db6903e812bbbc4064ef32cb3e7aa29 100644 --- a/FordFulkersonAlgorithm.cs +++ b/FordFulkersonAlgorithm.cs @@ -6,6 +6,12 @@ namespace FlowForge; public class FordFulkersonAlgorithm { + public enum SearchStrategy + { + BreadthFirstSearch, + DepthFirstSearch + } + private readonly FlowGraph _flowGraph; public FordFulkersonAlgorithm(FlowGraph flowGraph) @@ -13,7 +19,7 @@ public class FordFulkersonAlgorithm _flowGraph = flowGraph; } - public double Run(string sourceId, string targetId) + public double Run(string sourceId, string targetId, SearchStrategy strategy=SearchStrategy.BreadthFirstSearch) { // get source and target nodes FlowNode? source = _flowGraph.GetVertexById(sourceId); @@ -35,7 +41,7 @@ public class FordFulkersonAlgorithm Console.WriteLine("Start of Ford-Fulkerson Algorithm..."); // execute as long as there is an augmenting path - while (FindAugmentingPathBfs(source, target, pathFlow)) + while (FindAugmentingPath(source, target, pathFlow, strategy)) { Console.WriteLine("Augmenting Path found:"); foreach (var edge in pathFlow.Keys) @@ -92,23 +98,33 @@ public class FordFulkersonAlgorithm // return maximum flow after no augmenting paths were found anymore return maxFlow; } - - - public bool FindAugmentingPathBfs(FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow) + + private bool FindAugmentingPath(FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow, SearchStrategy strategy) { // parent map to walk back path var parentMap = new Dictionary<FlowNode, FlowEdge>(); - // queue for breath first search - var queue = new Queue<FlowNode>(); - queue.Enqueue(source); + // Choose the appropriate data structure based on the strategy + IEnumerable<FlowNode> nodesToVisit; + if (strategy == SearchStrategy.BreadthFirstSearch) + { + var queue = new Queue<FlowNode>(); + queue.Enqueue(source); + nodesToVisit = queue; + } + else + { + var stack = new Stack<FlowNode>(); + stack.Push(source); + nodesToVisit = stack; + } // map to store visited nodes var visited = new HashSet<FlowNode> { source }; - while (queue.Count > 0) + while (nodesToVisit.Any()) { - FlowNode current = queue.Dequeue(); + var current = strategy == SearchStrategy.BreadthFirstSearch ? ((Queue<FlowNode>)nodesToVisit).Dequeue() : ((Stack<FlowNode>)nodesToVisit).Pop(); var outEdges = _flowGraph.Graph.OutEdges(current).ToList(); @@ -125,79 +141,35 @@ public class FordFulkersonAlgorithm // if we reached the target node if (currentEdge.Target.Equals(target)) { - FlowNode currentNode = target; - pathFlow.Clear(); - - while (currentNode != source) - { - FlowEdge pathEdge = parentMap[currentNode]; - pathFlow.Add(pathEdge, pathEdge.Residual); - currentNode = pathEdge.Source; - } - - // return the augmenting path found + BuildAugmentingPath(source, target, parentMap, pathFlow); return true; } // search further - queue.Enqueue(currentEdge.Target); + if (strategy == SearchStrategy.BreadthFirstSearch) + { + ((Queue<FlowNode>)nodesToVisit).Enqueue(currentEdge.Target); + } + else + { + ((Stack<FlowNode>)nodesToVisit).Push(currentEdge.Target); + } } } return false; } - public bool FindAugmentingPathDfs(FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow) + private static void BuildAugmentingPath(FlowNode source, FlowNode target, Dictionary<FlowNode, FlowEdge> parentMap, Dictionary<FlowEdge, double> pathFlow) { - // parent map to walk back path - var parentMap = new Dictionary<FlowNode, FlowEdge>(); - - // queue for breath first search - var stack = new Stack<FlowNode>(); - stack.Push(source); - - // map to store visited nodes - var visited = new HashSet<FlowNode> { source }; + FlowNode currentNode = target; + pathFlow.Clear(); - while (stack.Count > 0) + while (currentNode != source) { - FlowNode current = stack.Pop(); - - var outEdges = _flowGraph.Graph.OutEdges(current).ToList(); - - var sortedOutEdges = outEdges.OrderBy(edge => edge.Target.Id); - - // go through all outgoing edges - foreach (FlowEdge currentEdge in sortedOutEdges) - { - if (currentEdge.Residual <= 0 || visited.Contains(currentEdge.Target)) continue; - - visited.Add(currentEdge.Target); - parentMap.Add(currentEdge.Target, currentEdge); - - // if we reached the target node - if (currentEdge.Target.Equals(target)) - { - FlowNode currentNode = target; - pathFlow.Clear(); - - while (currentNode != source) - { - FlowEdge pathEdge = parentMap[currentNode]; - pathFlow.Add(pathEdge, pathEdge.Residual); - currentNode = pathEdge.Source; - } - - // return the augmenting path found - return true; - } - - // search further - stack.Push(currentEdge.Target); - } + FlowEdge pathEdge = parentMap[currentNode]; + pathFlow[pathEdge] = pathEdge.Residual; + currentNode = pathEdge.Source; } - - return false; } -} - +} \ No newline at end of file diff --git a/GraphVisualizer.cs b/GraphVisualizer.cs index ab4098e696b0f9d3a43dd7191b63a3d47efced91..bd54a96aaf768bf27f15c9d85ebd358aa2ade387 100644 --- a/GraphVisualizer.cs +++ b/GraphVisualizer.cs @@ -16,6 +16,8 @@ public static class GraphVisualizer foreach (var edge in flowGraph.Graph.Edges) { + if(edge.IsBackwards) continue; + var msaglEdge = msaglGraph.AddEdge(edge.Source.Id, edge.Target.Id); if (edge.IsBackwards) diff --git a/MainWindow.xaml b/MainWindow.xaml index cdaf38b5dc4cdfedc00c99ffb5319e64772c412e..ed51c4033958746219d228e5930129dac700a650 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -30,7 +30,7 @@ HorizontalAlignment="Left" Margin="350,10,0,10" Grid.Row="0" - Click="RunAlgorithmButton_Click"/> + Click="FordFulkersonButton_Click"/> <!-- Second Button in the first row --> <Button x:Name="RunEdmondsKarpButton" @@ -41,7 +41,7 @@ HorizontalAlignment="Left" Margin="180,10,0,10" Grid.Row="0" - Click="RunAlgorithmButton_Click"/> + Click="EdmondsKarpButton_Click"/> <!-- Placeholder for graph viewer --> <Grid x:Name="GraphViewerGrid" Grid.Row="1"/> diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 5f822db480913e60c8d4b74ce820eabd877d6249..a1bb900ad1a6f56a364bf5053cce620791ec9ac3 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -1,22 +1,16 @@ -using System; + +using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; - using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - using System.Windows.Forms.Integration; +using Microsoft.Msagl.Core.Geometry.Curves; +using Microsoft.Msagl.Core.Routing; using MsaglDrawing = Microsoft.Msagl.Drawing; using Microsoft.Msagl.GraphViewerGdi; +using Microsoft.Msagl.Layout.Layered; +using Microsoft.Msagl.Routing; + namespace FlowForge { @@ -25,11 +19,14 @@ namespace FlowForge private GViewer? _gViewer; private FlowGraph? _flowGraph; private FordFulkersonAlgorithm? _fordFulkerson; + + private Dictionary<string, Microsoft.Msagl.Core.Geometry.Point> _nodePositions = new Dictionary<string, Microsoft.Msagl.Core.Geometry.Point>(); + private MsaglDrawing.Graph _msaglGraph; public MainWindow() { InitializeComponent(); - InitializeGraph(); + SetupGraphViewer(); } @@ -51,11 +48,59 @@ namespace FlowForge // Event handler for "Initialize and Display Graph" button private void InitializeGraphButton_Click(object sender, RoutedEventArgs e) { + InitializeGraph(); DisplayGraph(); } + + // Event handler for "Run Ford-Fulkerson" button + private void FordFulkersonButton_Click(object sender, RoutedEventArgs e) + { + /* + // Move node "1" to position (100, 200) + _msaglGraph.FindNode("1").GeometryNode.Center = new Microsoft.Msagl.Core.Geometry.Point(20,700); + SetStraightLineEdges(_msaglGraph); + + _gViewer.NeedToCalculateLayout = false; + _gViewer.Graph = _msaglGraph;*/ + + if (_gViewer != null) _gViewer.NeedToCalculateLayout = true; + SaveNodePositions(_gViewer?.Graph); // Save positions before running the algorithm + if (_fordFulkerson == null) + { + MessageBox.Show("Please initialize the graph first by clicking the 'Initialize Graph' button!", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + //SaveNodePositions(_gViewer.Graph); // Save positions before running the algorithm + + double maxFlow = _fordFulkerson.Run("1", "4", FordFulkersonAlgorithm.SearchStrategy.DepthFirstSearch); + + + DisplayGraph(); + MessageBox.Show($"Maximum flow from source to sink: {maxFlow}"); + } + + private void EdmondsKarpButton_Click(object sender, RoutedEventArgs e) + { + if (_fordFulkerson == null) + { + MessageBox.Show("Please initialize the graph first by clicking the 'Initialize Graph' button!", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + if (_gViewer != null) _gViewer.NeedToCalculateLayout = true; + SaveNodePositions(_gViewer?.Graph); // Save positions before running the algorithm + double maxFlow = _fordFulkerson.Run("1", "4", FordFulkersonAlgorithm.SearchStrategy.BreadthFirstSearch); + DisplayGraph(); + MessageBox.Show($"Maximum flow from source to sink: {maxFlow}"); + } + private void InitializeGraph() { + if (_gViewer != null) _gViewer.NeedToCalculateLayout = true; + _nodePositions.Clear(); + // Initialize a FlowGraph _flowGraph = new FlowGraph(); @@ -82,103 +127,126 @@ namespace FlowForge private void DisplayGraph() { // Convert QuickGraph to MSAGL Graph - var msaglGraph = GraphVisualizer.ConvertToMsaglGraph(_flowGraph); - + _msaglGraph = GraphVisualizer.ConvertToMsaglGraph(_flowGraph); + _msaglGraph.Attr.LayerDirection = MsaglDrawing.LayerDirection.TB; + // Farbe für Knoten 1 und 4 festlegen - var node1 = msaglGraph.FindNode("1"); + var node1 = _msaglGraph.FindNode("1"); if (node1 != null) { node1.Attr.FillColor = MsaglDrawing.Color.Green; // Knoten 1 wird grün } - var node4 = msaglGraph.FindNode("4"); + var node4 = _msaglGraph.FindNode("4"); if (node4 != null) { node4.Attr.FillColor = MsaglDrawing.Color.Red; // Knoten 4 wird rot } - foreach (var node in msaglGraph.Nodes) + foreach (var node in _msaglGraph.Nodes) { node.Attr.Shape = MsaglDrawing.Shape.Circle; // Knoten als Kreis } - foreach (var edge in msaglGraph.Edges) + foreach (var edge in _msaglGraph.Edges) { if (edge.Label != null) { - edge.Label.FontSize = 4; // Setze die Schriftgröße des Kantenlabels auf 8 + edge.Label.FontSize = 4; // Setze die Schriftgröße des Kantenlabels auf 4 } } // Assign MSAGL graph to viewer for display if (_gViewer != null) { - _gViewer.Graph = msaglGraph; + + _gViewer.Graph = _msaglGraph; + + if (_nodePositions.Any()) + { + var originalScale = _gViewer.ZoomF; + + ApplyNodePositions(_msaglGraph); + + var sugiyamaSettings = new SugiyamaLayoutSettings + { + + }; + + + + // Step 3: Apply layout settings to the graph + _msaglGraph.LayoutAlgorithmSettings = sugiyamaSettings; + + _msaglGraph.LayoutAlgorithmSettings.EdgeRoutingSettings.RouteMultiEdgesAsBundles = false; + + var router = new SplineRouter( + _msaglGraph.GeometryGraph, + _msaglGraph.LayoutAlgorithmSettings.EdgeRoutingSettings + ); + router.Run(); + + foreach (var edge in _msaglGraph.Edges) + { + if (edge.GeometryEdge != null && edge.Label != null) + { + // Set the label to the midpoint of the edge path + edge.Label.GeometryLabel.Center = edge.GeometryEdge.Curve.BoundingBox.Center; + } + } + + //SetStraightLineEdges(_msaglGraph); + + _gViewer.ZoomF = originalScale; + + _gViewer.Invalidate(); + _gViewer.NeedToCalculateLayout = false; + _gViewer.Graph = _msaglGraph; + } } } - - - // Event handler for "Run Ford-Fulkerson" button - private void RunAlgorithmButton_Click(object sender, RoutedEventArgs e) + + private void SaveNodePositions(MsaglDrawing.Graph? msaglGraph) { - if (_fordFulkerson == null) + _nodePositions.Clear(); + foreach (var node in msaglGraph.Nodes) { - MessageBox.Show("Please initialize the graph first by clicking the 'Initialize Graph' button!", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - return; + var position = node.GeometryNode.Center; + _nodePositions[node.Id] = new Microsoft.Msagl.Core.Geometry.Point(position.X, position.Y); } - - double maxFlow = _fordFulkerson.Run("1", "4"); - - MessageBox.Show($"Maximaler Fluss von Quelle zu Senke: {maxFlow}"); - - - /* - // Dictionary to store the flow along the augmenting path - var pathFlow = new Dictionary<FlowEdge, double>(); - - // Try to find an augmenting path from source (1) to target (4) - bool pathFound = _fordFulkerson.FindAugmentingPath(_flowGraph.GetVertexById("1"), _flowGraph.GetVertexById("4"), pathFlow); - - if (pathFound) + } + + private void ApplyNodePositions(MsaglDrawing.Graph msaglGraph) + { + foreach (var node in msaglGraph.Nodes) { - // Find the start and end nodes - var source = _flowGraph.GetVertexById("1"); - var target = _flowGraph.GetVertexById("4"); - - Console.WriteLine("Augmenting path found:"); - - // To print in correct order, traverse the path from target to source - var pathInOrder = new List<FlowEdge>(); - var current = target; - - // We trace back from the target to the source using the edges stored in pathFlow - while (current != source) + if (_nodePositions.TryGetValue(node.Id, out var position)) { - var edge = pathFlow.Keys.FirstOrDefault(e => e.Target == current); - if (edge == null) continue; - - pathInOrder.Insert(0, edge); // Insert the edge at the beginning to reverse the order - current = edge.Source; // Move to the previous node + node.GeometryNode.Center = new Microsoft.Msagl.Core.Geometry.Point(position.X, position.Y); } + } + } + + private void SetStraightLineEdges(MsaglDrawing.Graph msaglGraph) + { + var geometryGraph = msaglGraph.GeometryGraph; - // Now we print the path from source to target in order - foreach (var edge in pathInOrder) - { - Console.WriteLine($"{edge.Source.Id} -> {edge.Target.Id}, Remaining Flow: {pathFlow[edge]}"); - } + foreach (var edge in geometryGraph.Edges) + { + var sourceCenter = edge.Source.Center; + var targetCenter = edge.Target.Center; + + // Create a straight line from source to target + // Create a straight line from source to target + edge.Curve = new LineSegment(sourceCenter, targetCenter); + edge.LineWidth = 2; } - else + + foreach (var edge in msaglGraph.Edges) { - Console.WriteLine("No augmenting path found."); + edge.Attr.ArrowheadAtTarget = MsaglDrawing.ArrowStyle.Normal; // Ensure arrowhead is displayed + edge.Attr.ArrowheadLength = 1; } -*/ - - // Run the Ford-Fulkerson algorithm - //double maxFlow = _fordFulkerson.Run("1", "4"); - //MessageBox.Show($"Calculated Maximum Flow from Source to Sink: {maxFlow}"); - - // Update the visualization (optional) - DisplayGraph(); } } } \ No newline at end of file