Fixing SimpleTree layout algorithm by correctly aligning the vertices. (#76)

Fix tree layout
Adding optional gap between connected components.
This commit is contained in:
Edgardo Zoppi 2017-01-05 20:23:36 -03:00 коммит произвёл Alexander Smirnov
Родитель 6012ed1bf3
Коммит 23ca31e797
2 изменённых файлов: 86 добавлений и 38 удалений

Просмотреть файл

@ -43,17 +43,38 @@ namespace GraphX.PCL.Logic.Algorithms.LayoutAlgorithms
_direction = 1;
GenerateSpanningTree(cancellationToken);
//DoWidthAndHeightOptimization();
//DoWidthAndHeightOptimization();
//first layout the vertices with 0 in-edge
foreach ( var source in SpanningTree.Vertices.Where( v => SpanningTree.InDegree( v ) == 0 ) )
CalculatePosition( source, null, 0 );
var graph = new UndirectedBidirectionalGraph<TVertex, TEdge>(VisitedGraph);
var scca = new QuickGraph.Algorithms.ConnectedComponents.ConnectedComponentsAlgorithm<TVertex, TEdge>(graph);
scca.Compute();
//then the others
foreach ( var source in SpanningTree.Vertices )
CalculatePosition( source, null, 0 );
// Order connected components by their vertices count
// Group vertices by connected component (they should be placed together)
// Order vertices inside each conected component by in degree first, then out dregee
// (roots should be placed in the first layer and leafs in the last layer)
var components = from e in scca.Components
group e.Key by e.Value into c
orderby c.Count() descending
select c;
AssignPositions(cancellationToken);
foreach (var c in components)
{
var firstOfComponent = true;
var vertices = from v in c
orderby VisitedGraph.InDegree(v), VisitedGraph.OutDegree(v) descending
select v;
foreach (var source in vertices)
{
CalculatePosition(source, null, 0, firstOfComponent);
if ( firstOfComponent )
firstOfComponent = false;
}
}
AssignPositions(cancellationToken);
}
public override void ResetGraph(IEnumerable<TVertex> vertices, IEnumerable<TEdge> edges)
@ -68,33 +89,32 @@ namespace GraphX.PCL.Logic.Algorithms.LayoutAlgorithms
protected virtual void GenerateSpanningTree(CancellationToken cancellationToken)
{
SpanningTree = new BidirectionalGraph<TVertex, Edge<TVertex>>( false );
SpanningTree.AddVertexRange( VisitedGraph.Vertices );
IQueue<TVertex> vb = new QuickGraph.Collections.Queue<TVertex>();
vb.Enqueue( VisitedGraph.Vertices.OrderBy( v => VisitedGraph.InDegree( v ) ).First() );
switch ( Parameters.SpanningTreeGeneration )
SpanningTree.AddVertexRange(VisitedGraph.Vertices.OrderBy(v => VisitedGraph.InDegree(v)));
EdgeAction<TVertex, TEdge> action = e =>
{
cancellationToken.ThrowIfCancellationRequested();
SpanningTree.AddEdge(new Edge<TVertex>(e.Source, e.Target));
};
switch ( Parameters.SpanningTreeGeneration )
{
case SpanningTreeGeneration.BFS:
var bfsAlgo = new BreadthFirstSearchAlgorithm<TVertex, TEdge>( VisitedGraph, vb, new Dictionary<TVertex, GraphColor>() );
bfsAlgo.TreeEdge += e =>
{
cancellationToken.ThrowIfCancellationRequested();
SpanningTree.AddEdge(new Edge<TVertex>(e.Source, e.Target));
};
bfsAlgo.Compute();
var bfsAlgo = new BreadthFirstSearchAlgorithm<TVertex, TEdge>( VisitedGraph );
bfsAlgo.TreeEdge += action;
bfsAlgo.Compute();
break;
case SpanningTreeGeneration.DFS:
var dfsAlgo = new DepthFirstSearchAlgorithm<TVertex, TEdge>( VisitedGraph );
dfsAlgo.TreeEdge += e =>
{
cancellationToken.ThrowIfCancellationRequested();
SpanningTree.AddEdge(new Edge<TVertex>(e.Source, e.Target));
};
dfsAlgo.Compute();
dfsAlgo.TreeEdge += action;
dfsAlgo.ForwardOrCrossEdge += action;
dfsAlgo.Compute();
break;
}
}
protected virtual double CalculatePosition( TVertex v, TVertex parent, int l )
protected virtual double CalculatePosition( TVertex v, TVertex parent, int l, bool firstOfComponent )
{
if ( Data.ContainsKey( v ) )
return -1; //this vertex is already layed out
@ -113,7 +133,13 @@ namespace GraphX.PCL.Logic.Algorithms.LayoutAlgorithms
layer.NextPosition += Layers[l - 1].LastTranslate;
Layers[l - 1].LastTranslate = 0;
}
layer.Size = Math.Max( layer.Size, size.Height + Parameters.LayerGap );
if ( firstOfComponent )
{
layer.NextPosition += Parameters.ComponentGap;
}
layer.Size = Math.Max( layer.Size, size.Height );
layer.Vertices.Add( v );
if ( SpanningTree.OutDegree( v ) == 0 )
{
@ -126,20 +152,25 @@ namespace GraphX.PCL.Logic.Algorithms.LayoutAlgorithms
//first put the children
foreach ( var child in SpanningTree.OutEdges( v ).Select( e => e.Target ) )
{
double childPos = CalculatePosition( child, v, l + 1 );
double childPos = CalculatePosition( child, v, l + 1, firstOfComponent );
if ( childPos >= 0 )
{
minPos = Math.Min( minPos, childPos );
maxPos = Math.Max( maxPos, childPos );
}
}
if ( firstOfComponent )
firstOfComponent = false;
}
if ( minPos != double.MaxValue )
d.Position = ( minPos + maxPos ) / 2.0;
else
d.Position = layer.NextPosition;
d.Translate = Math.Max( layer.NextPosition - d.Position, 0 );
layer.LastTranslate = d.Translate;
layer.LastTranslate = d.Translate;
d.Position += d.Translate;
layer.NextPosition = d.Position;
}
@ -155,11 +186,11 @@ namespace GraphX.PCL.Logic.Algorithms.LayoutAlgorithms
foreach ( var layer in Layers )
{
foreach ( var v in layer.Vertices )
foreach ( var v in layer.Vertices )
{
cancellationToken.ThrowIfCancellationRequested();
Size size = Sizes[v];
var size = Sizes[v];
var d = Data[v];
if ( d.Parent != null )
{
@ -167,13 +198,15 @@ namespace GraphX.PCL.Logic.Algorithms.LayoutAlgorithms
d.Translate += Data[d.Parent].Translate;
}
VertexPositions[v] =
changeCoordinates
? new Point( _direction * ( layerSize + size.Height / 2.0 ), d.Position )
: new Point( d.Position, _direction * ( layerSize + size.Height / 2.0 ) );
var x = d.Position - size.Width / 2.0;
var y = _direction * (layerSize + (layer.Size - size.Height) / 2.0);
var pos = changeCoordinates ? new Point(y, x) : new Point(x, y);
VertexPositions[v] = pos;
}
layerSize += layer.Size;
}
layerSize += layer.Size + Parameters.LayerGap;
}
if ( _direction < 0 )
NormalizePositions();

Просмотреть файл

@ -2,6 +2,21 @@
{
public class SimpleTreeLayoutParameters : LayoutParametersBase
{
private double _componentGap = 10;
/// <summary>
/// Gets or sets the gap between the connected components.
/// </summary>
public double ComponentGap
{
get { return _componentGap; }
set
{
if (_componentGap == value) return;
_componentGap = value;
NotifyPropertyChanged("ComponentGap");
}
}
private double _vertexGap = 10;
/// <summary>
/// Gets or sets the gap between the vertices.