Skip to content

Commit 53620e9

Browse files
committed
Improve the implementation of the Graphviz algorithm to better work with delegate graph implementations.
Fix #41
1 parent 36702b7 commit 53620e9

2 files changed

Lines changed: 164 additions & 44 deletions

File tree

src/QuikGraph.Graphviz/GraphvizAlgorithm.cs

Lines changed: 73 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
using System.Collections.Generic;
33
using System.Diagnostics;
44
using System.IO;
5+
using System.Linq;
6+
#if SUPPORTS_AGGRESSIVE_INLINING
7+
using System.Runtime.CompilerServices;
8+
#endif
59
using JetBrains.Annotations;
610
using QuikGraph.Graphviz.Dot;
711

@@ -175,11 +179,14 @@ public string Generate()
175179
Output = new StringWriter();
176180
// Build vertex id map
177181
int i = 0;
182+
var vertices = new HashSet<TVertex>(VisitedGraph.Vertices);
178183
foreach (TVertex vertex in VisitedGraph.Vertices)
179184
{
180185
_verticesIds.Add(vertex, i++);
181186
}
182187

188+
var edges = new HashSet<TEdge>(VisitedGraph.Edges);
189+
183190
Output.Write(VisitedGraph.IsDirected ? "digraph " : "graph ");
184191
Output.Write(GraphFormat.Name);
185192
Output.WriteLine(" {");
@@ -200,26 +207,13 @@ public string Generate()
200207
Output.WriteLine($"edge [{edgeFormat}];");
201208
}
202209

203-
// Initialize vertices map
204-
var verticesColors = new Dictionary<TVertex, GraphColor>();
205-
foreach (TVertex vertex in VisitedGraph.Vertices)
206-
{
207-
verticesColors[vertex] = GraphColor.White;
208-
}
209-
210-
var edgeColors = new Dictionary<TEdge, GraphColor>();
211-
foreach (TEdge edge in VisitedGraph.Edges)
212-
{
213-
edgeColors[edge] = GraphColor.White;
214-
}
215-
216210
if (VisitedGraph is IClusteredGraph clusteredGraph)
217211
{
218-
WriteClusters(verticesColors, edgeColors, clusteredGraph);
212+
WriteClusters(vertices, edges, clusteredGraph);
219213
}
220214

221-
WriteVertices(verticesColors, VisitedGraph.Vertices);
222-
WriteEdges(edgeColors, VisitedGraph.Edges);
215+
WriteVertices(vertices);
216+
WriteEdges(edges);
223217

224218
Output.Write("}");
225219
return Output.ToString();
@@ -244,12 +238,12 @@ public string Generate([NotNull] IDotEngine dot, [NotNull] string outputFilePath
244238
}
245239

246240
private void WriteClusters(
247-
[NotNull] IDictionary<TVertex, GraphColor> verticesColors,
248-
[NotNull] IDictionary<TEdge, GraphColor> edgeColors,
241+
[NotNull, ItemNotNull] ICollection<TVertex> remainingVertices,
242+
[NotNull, ItemNotNull] ICollection<TEdge> remainingEdges,
249243
[NotNull] IClusteredGraph parent)
250244
{
251-
Debug.Assert(verticesColors != null);
252-
Debug.Assert(edgeColors != null);
245+
Debug.Assert(remainingVertices != null);
246+
Debug.Assert(remainingEdges != null);
253247
Debug.Assert(parent != null);
254248

255249
++ClusterCount;
@@ -260,65 +254,100 @@ private void WriteClusters(
260254
OnFormatCluster(subGraph);
261255
if (subGraph is IClusteredGraph clusteredGraph)
262256
{
263-
WriteClusters(verticesColors, edgeColors, clusteredGraph);
257+
WriteClusters(remainingVertices, remainingEdges, clusteredGraph);
264258
}
265259

266260
if (parent.Collapsed)
267261
{
268262
foreach (TVertex vertex in subGraph.Vertices)
269263
{
270-
verticesColors[vertex] = GraphColor.Black;
264+
remainingVertices.Remove(vertex);
271265
}
272266

273267
foreach (TEdge edge in subGraph.Edges)
274268
{
275-
edgeColors[edge] = GraphColor.Black;
269+
remainingEdges.Remove(edge);
276270
}
277271
}
278272
else
279273
{
280-
WriteVertices(verticesColors, subGraph.Vertices);
281-
WriteEdges(edgeColors, subGraph.Edges);
274+
WriteVertices(remainingVertices, subGraph.Vertices);
275+
WriteEdges(remainingEdges, subGraph.Edges);
282276
}
283277
Output.WriteLine("}");
284278
}
285279
}
286280

287-
private void WriteVertices(
288-
[NotNull] IDictionary<TVertex, GraphColor> verticesColors,
289-
[NotNull, ItemNotNull] IEnumerable<TVertex> vertices)
281+
#if SUPPORTS_AGGRESSIVE_INLINING
282+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
283+
#endif
284+
private void WriteVertex([NotNull] TVertex vertex)
285+
{
286+
Debug.Assert(vertex != null);
287+
288+
OnFormatVertex(vertex);
289+
}
290+
291+
private void WriteVertices([NotNull, ItemNotNull] IEnumerable<TVertex> vertices)
290292
{
291-
Debug.Assert(verticesColors != null);
292293
Debug.Assert(vertices != null);
293294

294295
foreach (TVertex vertex in vertices)
295296
{
296-
if (verticesColors[vertex] != GraphColor.White)
297-
continue;
297+
WriteVertex(vertex);
298+
}
299+
}
298300

299-
OnFormatVertex(vertex);
300-
verticesColors[vertex] = GraphColor.Black;
301+
private void WriteVertices(
302+
[NotNull, ItemNotNull] ICollection<TVertex> remainingVertices,
303+
[NotNull, ItemNotNull] IEnumerable<TVertex> vertices)
304+
{
305+
Debug.Assert(remainingVertices != null);
306+
Debug.Assert(vertices != null);
307+
308+
foreach (TVertex vertex in vertices.Where(remainingVertices.Contains))
309+
{
310+
WriteVertex(vertex);
311+
remainingVertices.Remove(vertex);
301312
}
302313
}
303314

304-
private void WriteEdges(
305-
[NotNull] IDictionary<TEdge, GraphColor> edgesColors,
306-
[NotNull, ItemNotNull] IEnumerable<TEdge> edges)
315+
#if SUPPORTS_AGGRESSIVE_INLINING
316+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
317+
#endif
318+
private void WriteEdge([NotNull] TEdge edge)
319+
{
320+
Debug.Assert(edge != null);
321+
322+
Output.Write(VisitedGraph.IsDirected
323+
? $"{_verticesIds[edge.Source]} -> {_verticesIds[edge.Target]}"
324+
: $"{_verticesIds[edge.Source]} -- {_verticesIds[edge.Target]}");
325+
326+
OnFormatEdge(edge);
327+
}
328+
329+
private void WriteEdges([NotNull, ItemNotNull] IEnumerable<TEdge> edges)
307330
{
308-
Debug.Assert(edgesColors != null);
309331
Debug.Assert(edges != null);
310332

311333
foreach (TEdge edge in edges)
312334
{
313-
if (edgesColors[edge] != GraphColor.White)
314-
continue;
335+
WriteEdge(edge);
336+
}
337+
}
315338

316-
Output.Write(VisitedGraph.IsDirected
317-
? $"{_verticesIds[edge.Source]} -> {_verticesIds[edge.Target]}"
318-
: $"{_verticesIds[edge.Source]} -- {_verticesIds[edge.Target]}");
319339

320-
OnFormatEdge(edge);
321-
edgesColors[edge] = GraphColor.Black;
340+
private void WriteEdges(
341+
[NotNull, ItemNotNull] ICollection<TEdge> remainingEdges,
342+
[NotNull, ItemNotNull] IEnumerable<TEdge> edges)
343+
{
344+
Debug.Assert(remainingEdges != null);
345+
Debug.Assert(edges != null);
346+
347+
foreach (TEdge edge in edges.Where(remainingEdges.Contains))
348+
{
349+
WriteEdge(edge);
350+
remainingEdges.Remove(edge);
322351
}
323352
}
324353
}

tests/QuikGraph.Graphviz.Tests/Extensions/GraphvizExtensionsTests.cs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
34
using System.Net;
45
using System.Text;
@@ -41,6 +42,96 @@ public void ToGraphviz()
4142
Assert.AreEqual(expectedDot, dotGraph);
4243
}
4344

45+
[Test]
46+
public void ToGraphviz_DelegateGraph()
47+
{
48+
int[] vertices = { 1, 2, 3, 4, 5 };
49+
var graph = new DelegateVertexAndEdgeListGraph<int, Edge<int>>(
50+
vertices,
51+
(int vertex, out IEnumerable<Edge<int>> outEdges) =>
52+
{
53+
if (vertex == 1)
54+
{
55+
outEdges = new[] { new Edge<int>(1, 2), new Edge<int>(1, 3) };
56+
return true;
57+
}
58+
59+
if (vertex == 2)
60+
{
61+
outEdges = new[] { new Edge<int>(2, 4) };
62+
return true;
63+
}
64+
65+
if (vertex is 3 or 4 or 5)
66+
{
67+
outEdges = new Edge<int>[] { };
68+
return true;
69+
}
70+
71+
outEdges = null;
72+
return false;
73+
});
74+
75+
string expectedDot =
76+
@"digraph G {" + Environment.NewLine
77+
+ @"0;" + Environment.NewLine
78+
+ @"1;" + Environment.NewLine
79+
+ @"2;" + Environment.NewLine
80+
+ @"3;" + Environment.NewLine
81+
+ @"4;" + Environment.NewLine
82+
+ @"0 -> 1;" + Environment.NewLine
83+
+ @"0 -> 2;" + Environment.NewLine
84+
+ @"1 -> 3;" + Environment.NewLine
85+
+ @"}";
86+
string dotGraph = graph.ToGraphviz();
87+
Assert.AreEqual(expectedDot, dotGraph);
88+
}
89+
90+
[Test]
91+
public void ToGraphviz_EquatableEdgeDelegateGraph()
92+
{
93+
int[] vertices = { 1, 2, 3, 4, 5 };
94+
var graph = new DelegateVertexAndEdgeListGraph<int, EquatableEdge<int>>(
95+
vertices,
96+
(int vertex, out IEnumerable<EquatableEdge<int>> outEdges) =>
97+
{
98+
if (vertex == 1)
99+
{
100+
outEdges = new[] { new EquatableEdge<int>(1, 2), new EquatableEdge<int>(1, 3) };
101+
return true;
102+
}
103+
104+
if (vertex == 2)
105+
{
106+
outEdges = new[] { new EquatableEdge<int>(2, 4) };
107+
return true;
108+
}
109+
110+
if (vertex is 3 or 4 or 5)
111+
{
112+
outEdges = new EquatableEdge<int>[] { };
113+
return true;
114+
}
115+
116+
outEdges = null;
117+
return false;
118+
});
119+
120+
string expectedDot =
121+
@"digraph G {" + Environment.NewLine
122+
+ @"0;" + Environment.NewLine
123+
+ @"1;" + Environment.NewLine
124+
+ @"2;" + Environment.NewLine
125+
+ @"3;" + Environment.NewLine
126+
+ @"4;" + Environment.NewLine
127+
+ @"0 -> 1;" + Environment.NewLine
128+
+ @"0 -> 2;" + Environment.NewLine
129+
+ @"1 -> 3;" + Environment.NewLine
130+
+ @"}";
131+
string dotGraph = graph.ToGraphviz();
132+
Assert.AreEqual(expectedDot, dotGraph);
133+
}
134+
44135
[Test]
45136
public void ToGraphvizWithEmptyInit()
46137
{

0 commit comments

Comments
 (0)