using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using Microsoft.Msagl.Core; using Microsoft.Msagl.Core.Geometry; using Microsoft.Msagl.Core.Geometry.Curves; using Microsoft.Msagl.Core.Layout; using Microsoft.Msagl.Core.Routing; using Microsoft.Msagl.Routing; using Microsoft.Msagl.Routing.Rectilinear; namespace Microsoft.Msagl.Layout.Layered { /// /// A facade for LayeredLayout that implements AlgorithmBase /// todo: handle progress properly /// public class LayeredLayout : AlgorithmBase { private GeometryGraph geometryGraph; private SugiyamaLayoutSettings settings; private LayeredLayoutEngine engine; /// /// Layered layout arranged the given graph on layers inferred from the directed edge structure /// /// graph to be laid out /// The settings for the algorithm. public LayeredLayout(GeometryGraph geometryGraph, SugiyamaLayoutSettings settings) { this.geometryGraph = geometryGraph; this.settings = settings; this.engine = new LayeredLayoutEngine(geometryGraph, settings); } /// /// estimate the aspect ratio without actually doing layout /// /// width / height public double EstimateAspectRatio() { Point dimensions = this.engine.CalculateApproximateDimensions(); Debug.Assert(dimensions.X > 0, "The estimated width of the layered layout should be greater than 0"); Debug.Assert(dimensions.Y > 0, "The estimated width of the layered layout should be greater than 0"); return dimensions.X / dimensions.Y; } /// /// Apply layout /// protected override void RunInternal() { PreRunTransform(geometryGraph, settings.Transformation); engine.Run(); geometryGraph.AlgorithmData = engine; PostRunTransform(geometryGraph, settings.Transformation); } internal static void PreRunTransform(GeometryGraph geomGraph, PlaneTransformation matrix) { if (matrix.IsIdentity) return; var matrixInverse = matrix.Inverse; foreach (Node n in geomGraph.Nodes) n.Transform(matrixInverse); //calculate new label widths and heights foreach (Edge e in geomGraph.Edges) { if (e.Label != null) { e.OriginalLabelWidth = e.Label.Width; e.OriginalLabelHeight = e.Label.Height; var r = new Rectangle(matrixInverse*new Point(0, 0), matrixInverse*new Point(e.Label.Width, e.Label.Height)); e.Label.Width = r.Width; e.Label.Height = r.Height; } } geomGraph.UpdateBoundingBox(); } static void PostRunTransform(GeometryGraph geometryGraph, PlaneTransformation transformation) { bool transform = !transformation.IsIdentity; if (transform) { foreach (Node n in geometryGraph.Nodes) { n.Transform(transformation); } //restore labels widths and heights foreach (Edge e in geometryGraph.Edges) { if (e.Label != null) { e.Label.Width = e.OriginalLabelWidth; e.Label.Height = e.OriginalLabelHeight; } } TransformCurves(geometryGraph, transformation); } geometryGraph.UpdateBoundingBox(); } static void TransformCurves(GeometryGraph geometryGraph, PlaneTransformation transformation) { geometryGraph.BoundingBox = new Rectangle(transformation * geometryGraph.LeftBottom, transformation * geometryGraph.RightTop); foreach (Edge e in geometryGraph.Edges) { if (e.Label != null) e.Label.Center = transformation * e.Label.Center; TransformEdgeCurve(transformation, e); } } static void TransformEdgeCurve(PlaneTransformation transformation, Edge e) { if (e.Curve != null) { e.Curve = e.Curve.Transform(transformation); var eg = e.EdgeGeometry; if (eg.SourceArrowhead != null) eg.SourceArrowhead.TipPosition = transformation * eg.SourceArrowhead.TipPosition; if (eg.TargetArrowhead != null) eg.TargetArrowhead.TipPosition = transformation * eg.TargetArrowhead.TipPosition; TransformUnderlyingPolyline(e, transformation); } } static void TransformUnderlyingPolyline(Edge e, PlaneTransformation transformation) { if (e.UnderlyingPolyline != null) { for (Site s = e.UnderlyingPolyline.HeadSite; s != null; s = s.Next) { s.Point = transformation * s.Point; } } } /// ///Tries to find the layers if they were there /// /// public static void RecoverAlgorithmData(GeometryGraph graph) { var recoveryEngine = new RecoveryLayeredLayoutEngine(graph); graph.AlgorithmData=recoveryEngine.GetEngine(); } /// /// adaptes to the node boundary curve change /// public static void IncrementalLayout(GeometryGraph geometryGraph, Node node) { ValidateArg.IsNotNull(geometryGraph, "geometryGraph"); LayeredLayoutEngine engine = geometryGraph.AlgorithmData as LayeredLayoutEngine; if (engine == null) return; PreRunTransform(geometryGraph, engine.SugiyamaSettings.Transformation); engine.IncrementalRun(node); PostRunTransform(geometryGraph, engine.SugiyamaSettings.Transformation); } } }