using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Msagl.Core;
using Microsoft.Msagl.Core.DataStructures;
using Microsoft.Msagl.Core.Geometry.Curves;
using Microsoft.Msagl.Core.Layout;
using Microsoft.Msagl.Core.Routing;
#if USE_PHYLOTREE
using Microsoft.Msagl.Prototype.Phylo;
#endif
namespace Microsoft.Msagl.Layout.Layered {
public enum SnapToGridByY
{
None,
Top,
Bottom
}
///
/// controls many properties of the layout algorithm
///
public sealed class SugiyamaLayoutSettings : LayoutAlgorithmSettings {
double yLayerSep = 10*3;
///
/// Separation between to neighboring layers
///
public double LayerSeparation {
get { return yLayerSep; }
set { yLayerSep = Math.Max(10, value); }
}
///
/// adds a constraint to keep one node to the left of another on the same layer
///
///
///
public void AddLeftRightConstraint(Node leftNode, Node rightNode) {
HorizontalConstraints.LeftRightConstraints.Insert(new Tuple(leftNode, rightNode));
}
///
/// removes a left-right constraint from
///
///
///
public void RemoveLeftRightConstraint(Node leftNode, Node rightNode) {
HorizontalConstraints.LeftRightConstraints.Remove(new Tuple(leftNode, rightNode));
}
readonly VerticalConstraintsForSugiyama verticalConstraints = new VerticalConstraintsForSugiyama();
///
/// minLayer, maxLayer, same layer, up-down, up-down vertical and left-right constraints are supported by this class
///
public VerticalConstraintsForSugiyama VerticalConstraints {
get { return verticalConstraints; }
}
readonly HorizontalConstraintsForSugiyama horizontalConstraints = new HorizontalConstraintsForSugiyama();
internal HorizontalConstraintsForSugiyama HorizontalConstraints {
get { return horizontalConstraints; }
}
///
/// Pins the nodes of the list to the max layer and
///
public void PinNodesToMaxLayer(params Node[] nodes) {
ValidateArg.IsNotNull(nodes, "nodes");
for (int i = 0; i < nodes.Length; i++)
VerticalConstraints.PinNodeToMaxLayer(nodes[i]);
}
///
/// Pins the nodes of the list to the min layer and
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ToMin")]
public void PinNodesToMinLayer(params Node[] nodes) {
ValidateArg.IsNotNull(nodes, "nodes");
for (int i = 0; i < nodes.Length; i++)
VerticalConstraints.PinNodeToMinLayer(nodes[i]);
}
///
/// adds a same layer constraint
///
public void PinNodesToSameLayer(params Node[] nodes) {
ValidateArg.IsNotNull(nodes, "nodes");
for (int i = 1; i < nodes.Length; i++)
VerticalConstraints.SameLayerConstraints.Insert(new Tuple(nodes[0], nodes[i]));
}
///
/// these nodes belong to the same layer and are adjacent positioned from left to right
///
///
public void AddSameLayerNeighbors(params Node[] neighbors) {
AddSameLayerNeighbors(new List(neighbors));
}
///
/// these nodes belong to the same layer and are adjacent positioned from left to right
///
///
public void AddSameLayerNeighbors(IEnumerable neighbors) {
var neibs = new List(neighbors);
HorizontalConstraints.AddSameLayerNeighbors(neibs);
for (int i = 0; i < neibs.Count - 1; i++)
VerticalConstraints.SameLayerConstraints.Insert(new Tuple(neibs[i], neibs[i + 1]));
}
///
/// adds a pair of adjacent neighbors
///
///
///
public void AddSameLayerNeighbors(Node leftNode, Node rightNode) {
HorizontalConstraints.AddSameLayerNeighborsPair(leftNode, rightNode);
VerticalConstraints.SameLayerConstraints.Insert(new Tuple(leftNode, rightNode));
}
///
///
///
public void RemoveAllConstraints() {
HorizontalConstraints.Clear();
VerticalConstraints.Clear();
}
///
/// adds an up-down constraint to the couple of nodes
///
///
///
public void AddUpDownConstraint(Node upperNode, Node lowerNode) {
VerticalConstraints.UpDownConstraints.Insert(new Tuple(upperNode, lowerNode));
}
///
/// adds a constraint where the top node center is positioned exactly above the lower node center
///
///
///
public void AddUpDownVerticalConstraint(Node upperNode, Node lowerNode) {
VerticalConstraints.UpDownConstraints.Insert(new Tuple(upperNode, lowerNode));
HorizontalConstraints.UpDownVerticalConstraints.Insert(new Tuple(upperNode, lowerNode));
}
///
/// adds vertical up down constraints udDownIds[0]->upDownIds[1]-> ... -> upDownsIds[upDownIds.Length-1]
///
///
public void AddUpDownVerticalConstraints(params Node[] upDownNodes) {
ValidateArg.IsNotNull(upDownNodes, "upDownNodes");
for (int i = 1; i < upDownNodes.Length; i++)
AddUpDownVerticalConstraint(upDownNodes[i - 1], upDownNodes[i]);
}
///
/// if set to true the algorithm only calculates the layers and exits
///
public bool LayeringOnly { get; set; }
int repetitionCoefficentForOrdering = 1;
///
/// This coefficient if set to the value greater than 1 will force the algorithm to search for layouts with fewer edge crossings
///
#if PROPERTY_GRID_SUPPORT
[DisplayName("Repetition coefficient for ordering")]
[Description(
"This coefficient if set to the value greater than 1 will force the algorithm to search for layouts with fewer edge crossings"
)]
[DefaultValue(1)]
#endif
public int RepetitionCoefficientForOrdering {
get { return repetitionCoefficentForOrdering; }
set { repetitionCoefficentForOrdering = Math.Max(value, 1); }
}
int randomSeedForOrdering = 1;
///
/// The seed for the random element inside of the ordering
///
#if PROPERTY_GRID_SUPPORT
[DisplayName("Random seed for ordering")]
[Description("The seed for the random element inside of the ordering")]
[DefaultValue(1)]
#endif
public int RandomSeedForOrdering {
get { return randomSeedForOrdering; }
set { randomSeedForOrdering = value; }
}
int noGainStepsBound = 5;
///
/// Maximal number of sequential steps without gain in the adjacent swap process
///
#if PROPERTY_GRID_SUPPORT
[DisplayName("No gain steps bound")]
[Description("Maximal number of sequential steps without gain in the adjacent swap process")]
[DefaultValue(5)]
#endif
public int NoGainAdjacentSwapStepsBound {
get { return noGainStepsBound; }
set { noGainStepsBound = value; }
}
int maxNumberOfPassesInOrdering = 24;
///
/// Maximal number of passes over layers applying the median algorithm in ordering
///
#if PROPERTY_GRID_SUPPORT
[DisplayName("Maximal number of passes in ordering")]
[Description("Maximal number of passes over layers applying the median algorithm in ordering")]
[DefaultValue(24)]
#endif
public int MaxNumberOfPassesInOrdering {
get { return maxNumberOfPassesInOrdering; }
set { maxNumberOfPassesInOrdering = value; }
}
int groupSplit = 2;
///
/// The ratio of the group splitting algorithm used in the spatial hierarchy constructions for edge routing
///
#if PROPERTY_GRID_SUPPORT
[DisplayName("Group split ratio")]
[Description("The ratio of the group splitting algorithm used in the spatial hierarchy constructions")]
[DefaultValue(2)]
#endif
public int GroupSplit {
get { return groupSplit; }
set { groupSplit = value; }
}
double labelCornersPreserveCoefficient = 0.1;
///
/// We create a hexagon for a label boundary: this coeffiecient defines the ratio of the top and the bottom side to the width of the label
///
#if PROPERTY_GRID_SUPPORT
[DisplayName("Edge label crossing coefficient")]
[Description(
"The coefficient from 0 to 1 controlling the edge crossing with its label.\n 1 - allowing only a shallow crossing which can cause a sharp edge bend\n" +
"0 - allowing a deeper crossing resulting in a smoother edge")]
[DefaultValue(0.1)]
#endif
public double LabelCornersPreserveCoefficient {
get { return labelCornersPreserveCoefficient; }
set { labelCornersPreserveCoefficient = value; }
}
int brandesThreshold = 600;
///
/// When the number of vertices in the proper layered graph is the threshold or more we switch to
/// the faster, but not so accurate, method for x-coordinates calculations.
///
#if PROPERTY_GRID_SUPPORT
[DisplayName("Fast x-coordinate assignment algorithm threshold")]
[Description(
"If the number of vertices in the proper layered graph is more than the value we switch to the fast x-coordinate assignment algorithm of Brandes and Kopf"
)]
[DefaultValue(600)]
#endif
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming",
"CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Brandes")]
public int BrandesThreshold {
get { return brandesThreshold; }
set { brandesThreshold = value; }
}
///
/// Constructor
///
public SugiyamaLayoutSettings() {
this.EdgeRoutingSettings.EdgeRoutingMode = EdgeRoutingMode.SugiyamaSplines;
}
///
/// The only function one needs to call to calculate the layout.
///
/// The layout info will be inserted in to the corresponding fields of the graph
/// The settings for the algorithm.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability",
"CA2000:Dispose objects before losing scope"),
SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider",
MessageId = "System.String.Format(System.String,System.Object)")]
internal static LayeredLayoutEngine CalculateLayout(GeometryGraph msaglGraph, SugiyamaLayoutSettings settings, CancelToken cancelToken) {
var engine = new LayeredLayoutEngine(msaglGraph, settings);
#if USE_PHYLOTREE
PhyloTree phyloTree = msaglGraph as PhyloTree;
if (phyloTree != null) {
var pc=new PhyloTreeLayoutCalclulation(phyloTree, settings, engine.IntGraph, engine.Database);
pc.Run();
} else
#endif
engine.Run(cancelToken);
return engine;
}
///
/// Clones the object
///
///
public override LayoutAlgorithmSettings Clone() {
return MemberwiseClone() as LayoutAlgorithmSettings;
}
double minimalWidth;
///
/// The resulting layout should be not more narrow than this value.
///
public double MinimalWidth {
get { return minimalWidth; }
set { minimalWidth = Math.Max(value, 0); }
}
double minimalHeight;
///
/// The resulting layout should at least as high as this this value
///
public double MinimalHeight {
get { return minimalHeight; }
set { minimalHeight = Math.Max(value, 0); }
}
double minNodeHeight = 72*0.5/4;
double minNodeWidth = 72*0.75/4;
///
/// The minimal node height
///
public double MinNodeHeight {
get { return minNodeHeight; }
set { minNodeHeight = Math.Max(0.4, value); }
}
///
/// The minimal node width
///
public double MinNodeWidth {
get { return minNodeWidth; }
set { minNodeWidth = Math.Max(0.8, value); }
}
PlaneTransformation transformation = PlaneTransformation.UnitTransformation;
///
/// This transformation is to be applied to the standard top - bottom layout.
/// However, node boundaries remain unchanged.
///
public PlaneTransformation Transformation {
get { return transformation; }
set { transformation = value; }
}
double aspectRatio;
///
/// The ratio width/height of the final layout.
/// The value zero means that the aspect ratio has not been set.
///
public double AspectRatio {
get { return aspectRatio; }
set { aspectRatio = value; }
}
internal double ActualLayerSeparation(bool layersAreDoubled) {
return layersAreDoubled ? LayerSeparation/2.0 : LayerSeparation;
}
double maxAspectRatioEccentricity = 5;
///
/// InitialLayoutByCluster requires the layered layout aspect ratio
/// to be in the range [1/MaxAspectRatioEccentricity, MaxAspectRatioEccentricity]
/// before switching to FallbackLayoutSettings
///
public double MaxAspectRatioEccentricity
{
get { return maxAspectRatioEccentricity; }
set { maxAspectRatioEccentricity = value; }
}
///
/// If the aspect ratio of Layered layout is too slow, use the following layout settings instead
///
public LayoutAlgorithmSettings FallbackLayoutSettings { get; set; }
SnapToGridByY snapToGridByY = SnapToGridByY.None;
public SnapToGridByY SnapToGridByY
{
get { return snapToGridByY; }
set { snapToGridByY = value; }
}
double gridSizeByY = 0;
public double GridSizeByY
{
get { return gridSizeByY; }
set { gridSizeByY = value; }
}
double gridSizeByX = 0;
public double GridSizeByX
{
get { return gridSizeByX; }
set { gridSizeByX = value; }
}
}
}