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(); // 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> /// Compute ideal edge lengths for the given graph component based on the given settings /// </summary> /// <param name="settings">settings for calculating ideal edge length</param> /// <param name="component">a graph component</param> public static void ComputeDesiredEdgeLengths(IdealEdgeLengthSettings settings, GeometryGraph component) { if (component == null) { return; } foreach (var e in component.Edges) { e.SourcePort = null; e.TargetPort = null; // use larger of DefaultLength and // minimum of the diagonal of a square of area equal to the bounding box of the source and that of the target e.Length = Math.Max(settings.DefaultLength, Math.Sqrt(2d * Math.Min(e.Source.BoundingBox.Width * e.Source.BoundingBox.Height, e.Target.BoundingBox.Width * e.Target.BoundingBox.Height))); } if (settings.ProportionalToSymmetricDifference) { SetEdgeLengthsProportionalToSymmetricDifference(component, settings.ProportionalEdgeLengthOffset, settings.ProportionalEdgeLengthAdjustment); } }