зеркало из https://github.com/stride3d/GraphX.git
Fixing SimpleTree layout algorithm by correctly aligning the vertices. (#76)
Fix tree layout Adding optional gap between connected components.
This commit is contained in:
Родитель
6012ed1bf3
Коммит
23ca31e797
|
@ -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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче