internal EdgeConstraintGenerator( IEnumerable<Edge> edges, IdealEdgeLengthSettings settings, AxisSolver horizontalSolver, AxisSolver verticalSolver) { // filter out self edges this.edges = edges.Where(e => e.Source != e.Target); this.settings = settings; this.horizontalSolver = horizontalSolver; this.verticalSolver = verticalSolver; foreach (var e in this.edges) { TNode u = CreateTNode(e.Source), v = CreateTNode(e.Target); u.outNeighbours.Add(v); v.inNeighbours.Add(u); } foreach (var v in nodeMap.Values) { if(v.stackNode==null) { DFS(v); } } while (stack.Count > 0) { component = new List<TNode>(); RDFS(stack.Last.Value); if (component.Count > 1) { var cyclicComponent = new Set<Node>(); foreach (var v in component) { cyclicComponent.Insert(v.v); } cyclicComponents.Add(cyclicComponent); } } switch (settings.EdgeDirectionConstraints) { case Directions.South: this.addConstraint = this.AddSConstraint; break; case Directions.North: this.addConstraint = this.AddNConstraint; break; case Directions.West: this.addConstraint = this.AddWConstraint; break; case Directions.East: this.addConstraint = this.AddEConstraint; break; } }
/// <summary> /// Creates a VerticalSeparationConstraint for each edge in the given set to structural constraints, /// to require these edges to be downward pointing. Also checks for cycles, and edges involved /// in a cycle receive no VerticalSeparationConstraint, but can optionally receive a circle constraint. /// </summary> /// <param name="edges"></param> /// <param name="settings"></param> /// <param name="horizontalSolver"></param> /// <param name="verticalSolver"></param> internal static void GenerateEdgeConstraints( IEnumerable <Edge> edges, IdealEdgeLengthSettings settings, AxisSolver horizontalSolver, AxisSolver verticalSolver) { if (settings.EdgeDirectionConstraints == Directions.None) { return; } EdgeConstraintGenerator g = new EdgeConstraintGenerator(edges, settings, horizontalSolver, verticalSolver); g.GenerateSeparationConstraints(); }
/// <summary> /// Obtain a starting configuration that is feasible with respect to the structural /// constraints. This is necessary to avoid e.g. cycles in the constraint graph; /// for example, dragging the root of a downward-pointing tree downward below other /// nodes of the tree can result in auto-generation of constraints generating some /// constraints with the root on the right-hand side, and the structural constraints /// have it on the left-hand side. /// /// When AvoidOverlaps==true and we reach ConstraintLevel>=2 then we also need to remove /// overlaps... prior to this we need to force horizontal resolving of overlaps /// between *all* nodes involved in vertical equality constraints (i.e. no skipping), /// and then vertical overlap resolution of all nodes involved in horizontal equality /// constraints /// </summary> static internal void Enforce(FastIncrementalLayoutSettings settings, int currentConstraintLevel, IEnumerable <FiNode> nodes, List <IConstraint> horizontalConstraints, List <IConstraint> verticalConstraints, IEnumerable <Cluster> clusterHierarchies, Func <Cluster, LayoutAlgorithmSettings> clusterSettings) { foreach (LockPosition l in settings.locks) { l.Project(); } ResetPositions(nodes); double dblVpad = settings.NodeSeparation + Pad; double dblHpad = settings.NodeSeparation; double dblCVpad = settings.ClusterMargin + Pad; double dblCHpad = settings.ClusterMargin; for (int level = settings.MinConstraintLevel; level <= currentConstraintLevel; ++level) { // to obtain a feasible solution when equality constraints are present we need to be extra careful // but the solution below is a little bit crummy, is not currently optimized when there are no // equality constraints and we do not really have any scenarios involving equality constraints at // the moment, and also the fact that it turns off DeferToVertical causes it to resolve too // many overlaps horizontally, so let's skip it for now. //if (level >= 2 && settings.AvoidOverlaps) //{ // RemoveOverlapsOnEqualityConstraints(dblVpad, dblHpad, horizontalConstraints, verticalConstraints); //} var hsSolver = new AxisSolver(true, nodes, clusterHierarchies, level >= 2 && settings.AvoidOverlaps, level, clusterSettings); hsSolver.structuralConstraints = horizontalConstraints; hsSolver.OverlapRemovalParameters = new Core.Geometry.OverlapRemovalParameters { AllowDeferToVertical = true, ConsiderProportionalOverlap = settings.IdealEdgeLength.EdgeDirectionConstraints != Core.Geometry.Directions.None }; hsSolver.Initialize(dblHpad, dblVpad, dblCHpad, dblCVpad, v => v.Center); hsSolver.SetDesiredPositions(); hsSolver.Solve(); ResetPositions(nodes); var vsSolver = new AxisSolver(false, nodes, clusterHierarchies, level >= 2 && settings.AvoidOverlaps, level, clusterSettings); vsSolver.structuralConstraints = verticalConstraints; vsSolver.Initialize(dblHpad, dblVpad, dblCHpad, dblCVpad, v => v.Center); vsSolver.SetDesiredPositions(); vsSolver.Solve(); ResetPositions(nodes); } }
/// <summary> /// Obtain a starting configuration that is feasible with respect to the structural /// constraints. This is necessary to avoid e.g. cycles in the constraint graph; /// for example, dragging the root of a downward-pointing tree downward below other /// nodes of the tree can result in auto-generation of constraints generating some /// constraints with the root on the right-hand side, and the structural constraints /// have it on the left-hand side. /// /// When AvoidOverlaps==true and we reach ConstraintLevel>=2 then we also need to remove /// overlaps... prior to this we need to force horizontal resolving of overlaps /// between *all* nodes involved in vertical equality constraints (i.e. no skipping), /// and then vertical overlap resolution of all nodes involved in horizontal equality /// constraints /// </summary> static internal void Enforce(FastIncrementalLayoutSettings settings, int currentConstraintLevel, IEnumerable<FiNode> nodes, List<IConstraint> horizontalConstraints, List<IConstraint> verticalConstraints, IEnumerable<Cluster> clusterHierarchies, Func<Cluster, LayoutAlgorithmSettings> clusterSettings) { foreach (LockPosition l in settings.locks) { l.Project(); } ResetPositions(nodes); double dblVpad = settings.NodeSeparation + Pad; double dblHpad = settings.NodeSeparation; double dblCVpad = settings.ClusterMargin + Pad; double dblCHpad = settings.ClusterMargin; for (int level = settings.MinConstraintLevel; level <= currentConstraintLevel; ++level) { // to obtain a feasible solution when equality constraints are present we need to be extra careful // but the solution below is a little bit crummy, is not currently optimized when there are no // equality constraints and we do not really have any scenarios involving equality constraints at // the moment, and also the fact that it turns off DeferToVertical causes it to resolve too // many overlaps horizontally, so let's skip it for now. //if (level >= 2 && settings.AvoidOverlaps) //{ // RemoveOverlapsOnEqualityConstraints(dblVpad, dblHpad, horizontalConstraints, verticalConstraints); //} var hsSolver = new AxisSolver(true, nodes, clusterHierarchies, level >= 2 && settings.AvoidOverlaps, level, clusterSettings); hsSolver.structuralConstraints = horizontalConstraints; hsSolver.OverlapRemovalParameters = new Core.Geometry.OverlapRemovalParameters { AllowDeferToVertical = true, ConsiderProportionalOverlap = settings.IdealEdgeLength.EdgeDirectionConstraints != Core.Geometry.Directions.None }; hsSolver.Initialize(dblHpad, dblVpad, dblCHpad, dblCVpad, v => v.Center); hsSolver.SetDesiredPositions(); hsSolver.Solve(); ResetPositions(nodes); var vsSolver = new AxisSolver(false, nodes, clusterHierarchies, level >= 2 && settings.AvoidOverlaps, level, clusterSettings); vsSolver.structuralConstraints = verticalConstraints; vsSolver.Initialize(dblHpad, dblVpad, dblCHpad, dblCVpad, v => v.Center); vsSolver.SetDesiredPositions(); vsSolver.Solve(); ResetPositions(nodes); } }
/// <summary> /// Creates a VerticalSeparationConstraint for each edge in the given set to structural constraints, /// to require these edges to be downward pointing. Also checks for cycles, and edges involved /// in a cycle receive no VerticalSeparationConstraint, but can optionally receive a circle constraint. /// </summary> /// <param name="edges"></param> /// <param name="settings"></param> /// <param name="horizontalSolver"></param> /// <param name="verticalSolver"></param> internal static void GenerateEdgeConstraints( IEnumerable<Edge> edges, IdealEdgeLengthSettings settings, AxisSolver horizontalSolver, AxisSolver verticalSolver) { if (settings.EdgeDirectionConstraints == Directions.None) { return; } EdgeConstraintGenerator g = new EdgeConstraintGenerator(edges, settings, horizontalSolver, verticalSolver); g.GenerateSeparationConstraints(); // we have in the past used Circular constraints to better show cycles... it's a little experimental though // so it's not currently enabled //foreach (var c in g.CyclicComponents) { // constraints.Add(new ProcrustesCircleConstraint(c)); //} }
/// <summary> /// Creates a VerticalSeparationConstraint for each edge in the given set to structural constraints, /// to require these edges to be downward pointing. Also checks for cycles, and edges involved /// in a cycle receive no VerticalSeparationConstraint, but can optionally receive a circle constraint. /// </summary> /// <param name="edges"></param> /// <param name="settings"></param> /// <param name="horizontalSolver"></param> /// <param name="verticalSolver"></param> internal static void GenerateEdgeConstraints( IEnumerable <Edge> edges, IdealEdgeLengthSettings settings, AxisSolver horizontalSolver, AxisSolver verticalSolver) { if (settings.EdgeDirectionConstraints == Directions.None) { return; } EdgeConstraintGenerator g = new EdgeConstraintGenerator(edges, settings, horizontalSolver, verticalSolver); g.GenerateSeparationConstraints(); // we have in the past used Circular constraints to better show cycles... it's a little experimental though // so it's not currently enabled //foreach (var c in g.CyclicComponents) { // constraints.Add(new ProcrustesCircleConstraint(c)); //} }
internal EdgeConstraintGenerator( IEnumerable <Edge> edges, IdealEdgeLengthSettings settings, AxisSolver horizontalSolver, AxisSolver verticalSolver) { // filter out self edges this.edges = edges.Where(e => e.Source != e.Target); this.settings = settings; this.horizontalSolver = horizontalSolver; this.verticalSolver = verticalSolver; foreach (var e in this.edges) { TNode u = CreateTNode(e.Source), v = CreateTNode(e.Target); u.outNeighbours.Add(v); v.inNeighbours.Add(u); } foreach (var v in nodeMap.Values) { if (v.stackNode == null) { DFS(v); } } while (stack.Count > 0) { component = new List <TNode>(); RDFS(stack.Last.Value); if (component.Count > 1) { var cyclicComponent = new Set <Node>(); foreach (var v in component) { cyclicComponent.Insert(v.v); } cyclicComponents.Add(cyclicComponent); } } switch (settings.EdgeDirectionConstraints) { case Directions.South: this.addConstraint = this.AddSConstraint; break; case Directions.North: this.addConstraint = this.AddNConstraint; break; case Directions.West: this.addConstraint = this.AddWConstraint; break; case Directions.East: this.addConstraint = this.AddEConstraint; break; } }
/// <summary> /// Create the graph data structures. /// </summary> /// <param name="geometryGraph"></param> /// <param name="settings">The settings for the algorithm.</param> /// <param name="initialConstraintLevel">initialize at this constraint level</param> /// <param name="clusterSettings">settings by cluster</param> internal FastIncrementalLayout(GeometryGraph geometryGraph, FastIncrementalLayoutSettings settings, int initialConstraintLevel, Func <Cluster, LayoutAlgorithmSettings> clusterSettings) { graph = geometryGraph; this.settings = settings; this.clusterSettings = clusterSettings; int i = 0; ICollection <Node> allNodes = graph.Nodes; nodes = new FiNode[allNodes.Count]; foreach (Node v in allNodes) { v.AlgorithmData = nodes[i] = new FiNode(i, v); i++; } clusterEdges.Clear(); edges.Clear(); foreach (Edge e in graph.Edges) { if (e.Source is Cluster || e.Target is Cluster) { clusterEdges.Add(e); } else { edges.Add(new FiEdge(e)); } foreach (var l in e.Labels) { l.InnerPoints = l.OuterPoints = null; } } SetLockNodeWeights(); components = new List <FiNode[]>(); if (!settings.InterComponentForces) { basicGraph = new BasicGraph <FiEdge>(edges, nodes.Length); foreach (var componentNodes in ConnectedComponentCalculator <FiEdge> .GetComponents(basicGraph)) { var vs = new FiNode[componentNodes.Count()]; int vi = 0; foreach (int v in componentNodes) { vs[vi++] = nodes[v]; } components.Add(vs); } } else // just one big component (regardless of actual edges) { components.Add(nodes); } horizontalSolver = new AxisSolver(true, nodes, new[] { geometryGraph.RootCluster }, settings.AvoidOverlaps, settings.MinConstraintLevel, clusterSettings) { OverlapRemovalParameters = new OverlapRemovalParameters { AllowDeferToVertical = true, // use "ProportionalOverlap" mode only when iterative apply forces layout is being used. // it is not necessary otherwise. ConsiderProportionalOverlap = settings.ApplyForces } }; verticalSolver = new AxisSolver(false, nodes, new[] { geometryGraph.RootCluster }, settings.AvoidOverlaps, settings.MinConstraintLevel, clusterSettings); SetupConstraints(); geometryGraph.RootCluster.ComputeWeight(); foreach ( Cluster c in geometryGraph.RootCluster.AllClustersDepthFirst().Where(c => c.RectangularBoundary == null) ) { c.RectangularBoundary = new RectangularClusterBoundary(); } CurrentConstraintLevel = initialConstraintLevel; }
/// <summary> /// Create the graph data structures. /// </summary> /// <param name="geometryGraph"></param> /// <param name="settings">The settings for the algorithm.</param> /// <param name="initialConstraintLevel">initialize at this constraint level</param> /// <param name="clusterSettings">settings by cluster</param> internal FastIncrementalLayout(GeometryGraph geometryGraph, FastIncrementalLayoutSettings settings, int initialConstraintLevel, Func<Cluster, LayoutAlgorithmSettings> clusterSettings) { graph = geometryGraph; this.settings = settings; this.clusterSettings = clusterSettings; int i = 0; ICollection<Node> allNodes = graph.Nodes; nodes = new FiNode[allNodes.Count]; foreach (Node v in allNodes) { v.AlgorithmData = nodes[i] = new FiNode(i, v); i++; } clusterEdges.Clear(); edges.Clear(); foreach (Edge e in graph.Edges) { if (e.Source is Cluster || e.Target is Cluster) clusterEdges.Add(e); else edges.Add(new FiEdge(e)); foreach (var l in e.Labels) l.InnerPoints = l.OuterPoints = null; } SetLockNodeWeights(); components = new List<FiNode[]>(); if (!settings.InterComponentForces) { basicGraph = new BasicGraph<FiEdge>(edges, nodes.Length); foreach (var componentNodes in ConnectedComponentCalculator<FiEdge>.GetComponents(basicGraph)) { var vs = new FiNode[componentNodes.Count()]; int vi = 0; foreach (int v in componentNodes) { vs[vi++] = nodes[v]; } components.Add(vs); } } else // just one big component (regardless of actual edges) components.Add(nodes); horizontalSolver = new AxisSolver(true, nodes, new[] {geometryGraph.RootCluster}, settings.AvoidOverlaps, settings.MinConstraintLevel, clusterSettings) { OverlapRemovalParameters = new OverlapRemovalParameters { AllowDeferToVertical = true, // use "ProportionalOverlap" mode only when iterative apply forces layout is being used. // it is not necessary otherwise. ConsiderProportionalOverlap = settings.ApplyForces } }; verticalSolver = new AxisSolver(false, nodes, new[] {geometryGraph.RootCluster}, settings.AvoidOverlaps, settings.MinConstraintLevel, clusterSettings); SetupConstraints(); geometryGraph.RootCluster.ComputeWeight(); foreach ( Cluster c in geometryGraph.RootCluster.AllClustersDepthFirst().Where(c => c.RectangularBoundary == null) ) { c.RectangularBoundary = new RectangularClusterBoundary(); } CurrentConstraintLevel = initialConstraintLevel; }