static IEnumerable<Node> Children(Cluster parent) { #if SILVERLIGHT return parent.Clusters.Cast<Node>().Concat(parent.Nodes); #else return parent.Clusters.Concat(parent.Nodes); #endif }
HashSet<Node> CreateOrGetAddedChildrenOfParent(Cluster parent) { HashSet<Node> addedChildren; addedNodesByCluster.TryGetValue(parent, out addedChildren); if (addedChildren == null) addedNodesByCluster[parent] = addedChildren = new HashSet<Node>(); return addedChildren; }
public void IsDescendantOf_BasicTest() { Cluster cluster = new Cluster(); Node node = new Node(); Node node2 = new Node(); cluster.AddChild(node); Assert.IsTrue(node.IsDescendantOf(cluster), "Node is a descendant of cluster but IsDescendantOf returns false."); Assert.IsFalse(node2.IsDescendantOf(cluster), "Node2 is not a descendant of cluster but IsDescendantOf returns true."); Assert.IsFalse(cluster.IsDescendantOf(cluster), "A cluster should not be considered a descendant of itself."); }
static void ProcessAncestorDescendantCouple(Cluster ancestor, Node node, Dictionary<Node, Shape> nodesToShapes) { Cluster parent=Parent(node); do { foreach (var n in Children(parent)) CreateShapeIfNeeeded(n, nodesToShapes); if (parent == ancestor) break; parent = Parent(parent); } while (true); CreateShapeIfNeeeded(parent, nodesToShapes); }
public void IsDescendantOf_ManyParents() { Cluster grandMother = new Cluster(); Cluster grandFather = new Cluster(); Cluster parent = new Cluster(); Cluster uncle = new Cluster(); grandMother.AddChild(parent); grandMother.AddChild(uncle); grandFather.AddChild(parent); grandFather.AddChild(uncle); Node child = new Node(); parent.AddChild(child); Assert.IsTrue(child.IsDescendantOf(parent), "The child node should be considered a descendant of its parent."); Assert.IsTrue(child.IsDescendantOf(grandMother), "The child node should be considered a descendant of its grandmother."); Assert.IsTrue(child.IsDescendantOf(grandFather), "The child node should be considered a descendant of its grandfather."); Assert.IsFalse(child.IsDescendantOf(uncle), "The child node should not be considered a descendant of its uncle."); }
/// <summary> /// Copy the cluster's child Nodes and Clusters as nodes and return a mapping of original to copy. /// The reverse mapping (copy to original) is available via the copy's UserData /// </summary> /// <param name="cluster">Cluster whose contents will be copied</param> /// <returns>the mapping from original to copy</returns> internal static Dictionary<Node, Node> ShallowNodeCopyDictionary(Cluster cluster) { var originalNodeToCopy = new Dictionary<Node, Node>(); foreach (var v in cluster.Nodes) originalNodeToCopy[v] = new Node(v.BoundaryCurve.Clone()) {UserData = v}; foreach (var cl in cluster.Clusters) { if (cl.IsCollapsed) originalNodeToCopy[cl] = new Node(cl.CollapsedBoundary.Clone()) {UserData = cl}; else { if (cl.BoundaryCurve == null) cl.BoundaryCurve = cl.RectangularBoundary.RectangularHull(); originalNodeToCopy[cl] = new Node(cl.BoundaryCurve.Clone()) {UserData = cl}; } } return originalNodeToCopy; }
/// <summary> /// Creates a shallow copy of the root cluster and divides into GeometryGraphs each of which is a connected component with /// respect to edges internal to root. /// </summary> /// <param name="cluster">cluster to break into components</param> /// <returns>GeometryGraphs that are each a connected component</returns> static IEnumerable<GeometryGraph> GetComponents(Cluster cluster) { Dictionary<Node, Node> originalToCopyNodeMap = ShallowNodeCopyDictionary(cluster); var copiedEdges = new List<Edge>(); foreach (var target in originalToCopyNodeMap.Keys) { foreach (var e in target.InEdges) { var sourceAncestorUnderRoot = Ancestor(e.Source, cluster); if (sourceAncestorUnderRoot == e.Source) //it is a flat edge and we are only interested in flat edges copiedEdges.Add(CopyEdge(originalToCopyNodeMap, e, sourceAncestorUnderRoot, target)); } copiedEdges.AddRange(target.SelfEdges.Select(e => CopyEdge(originalToCopyNodeMap, e))); } return GraphConnectedComponents.CreateComponents(originalToCopyNodeMap.Values.ToArray(), copiedEdges); }
/// <summary> /// Check if root is an ancestor of node /// </summary> /// <param name="node"></param> /// <param name="root"></param> /// <returns>true if the node is a descendant of root</returns> static bool IsDescendant(Node node, Cluster root) { return Ancestor(node, root) != null; }
/// <summary> /// Apply the appropriate layout to the specified cluster and its children (bottom up) /// </summary> /// <param name="cluster">the root of the cluster hierarchy to lay out</param> /// <returns>list of edges external to the cluster</returns> void LayoutCluster(Cluster cluster) { if (cluster.IsCollapsed) return; LayoutAlgorithmSettings settings = clusterSettings(cluster); cluster.UnsetInitialLayoutState(); if (runInParallel && cluster.Clusters.Count() > 1) Parallel.ForEach(cluster.Clusters, parallelOptions, LayoutCluster); else foreach (var cl in cluster.Clusters) LayoutCluster(cl); List<GeometryGraph> components = (List<GeometryGraph>) GetComponents(cluster); //currentComponentFraction = (1.0 / clusterCount) / components.Count; // if (runInParallel) // Parallel.ForEach(components, parallelOptions, comp => LayoutComponent(settings, comp)); // else // debug!!!!!! components.ForEach(c => LayoutComponent(settings, c)); var bounds = MdsGraphLayout.PackGraphs(components, settings); foreach (var g in components) FixOriginalGraph(g, true); cluster.UpdateBoundary(bounds); cluster.SetInitialLayoutState(settings.ClusterMargin); cluster.RaiseLayoutDoneEvent(); // var l = new List<DebugCurve>(); // foreach (var node in cluster.Nodes) { // l.Add(new DebugCurve(node.BoundaryCurve)); // } // foreach (var cl in cluster.AllClustersDepthFirstExcludingSelf()) { // l.Add(new DebugCurve(cl.BoundaryCurve)); // l.AddRange(cl.Nodes.Select(n=>new DebugCurve(n.BoundaryCurve))); // } // LayoutAlgorithmSettings.ShowDebugCurves(l.ToArray()); }
static void FixClusterBoundariesWithNoRectBoundaries(Cluster cluster, double padding) { foreach (Cluster cl in cluster.Clusters) FixClusterBoundariesWithNoRectBoundaries(cl, padding); var box = Rectangle.CreateAnEmptyBox(); var clusterPoints = cluster.Clusters.SelectMany(c => ClusterPoints(c)).Concat( cluster.Nodes.SelectMany(n => NodePerimeterPoints(n))); foreach (var clusterPoint in clusterPoints) box.Add(clusterPoint); box.Pad(padding); cluster.BoundaryCurve = box.Perimeter(); cluster.RectangularBoundary = new RectangularClusterBoundary(); }
/// <summary> /// Add the parent cluster to this node's list of parents /// </summary> /// <param name="parent"></param> public void AddClusterParent(Cluster parent) { ValidateArg.IsNotNull(parent, "parent"); Debug.Assert(parent != this); clusterParents.Add(parent); }
Cluster ProcessSubGraphs(Subgraph subgraph) { var geomCluster = new Cluster(subgraph.Nodes.Select(n => nodeMapping[n]), subgraph.Subgraphs.Select(ProcessSubGraphs)); foreach (Cluster sub in geomCluster.Clusters) sub.GeometryParent = geomCluster; subgraph.GeometryNode = geomCluster; geomCluster.UserData = subgraph; nodeMapping[subgraph] = geomCluster; return geomCluster; }
void WriteCluster(Cluster cluster, string clusterId) { WriteStartElement(GeometryToken.Cluster); WriteAttribute(GeometryToken.Id, clusterId); WriteAttribute(GeometryToken.Barycenter, cluster.Barycenter); WriteChildClusters(cluster); WriteChildNodes(cluster); if (cluster.BoundaryCurve != null) WriteICurve(cluster.BoundaryCurve); WriteClusterRectBoundary(cluster.RectangularBoundary); WriteEndElement(); }
static IEnumerable<Point> ClusterPoints(Cluster cluster) { return cluster.BoundaryCurve as Polyline; }
private void AddOlapClusters(ConstraintGenerator generator, OverlapRemovalCluster olapParentCluster, Cluster incClus, InitialCenterDelegateType nodeCenter) { LayoutAlgorithmSettings settings = clusterSettings(incClus); double nodeSeparationH = settings.NodeSeparation; double nodeSeparationV = settings.NodeSeparation + 1e-4; double innerPaddingH = settings.ClusterMargin; double innerPaddingV = settings.ClusterMargin + 1e-4; // Creates the OverlapRemoval (Olap) Cluster/Node objects for our FastIncrementalLayout (FIL) objects. // If !isHorizontal this overwrites the Olap members of the Incremental.Clusters and Msagl.Nodes. // First create the olapCluster for the current incCluster. If olapParentCluster is null, then // incCluster is the root of a new hierarchy. RectangularClusterBoundary rb = incClus.RectangularBoundary; if (IsHorizontal) { rb.olapCluster = generator.AddCluster( olapParentCluster, incClus /* userData */, rb.MinWidth, rb.MinHeight, rb.LeftBorderInfo, rb.RightBorderInfo, rb.BottomBorderInfo, rb.TopBorderInfo); rb.olapCluster.NodePadding = nodeSeparationH; rb.olapCluster.NodePaddingP = nodeSeparationV; rb.olapCluster.ClusterPadding = innerPaddingH; rb.olapCluster.ClusterPaddingP = innerPaddingV; } else { var postXLeftBorderInfo = new BorderInfo(rb.LeftBorderInfo.InnerMargin, rb.Rect.Left, rb.LeftBorderInfo.Weight); var postXRightBorderInfo = new BorderInfo(rb.RightBorderInfo.InnerMargin, rb.Rect.Right, rb.RightBorderInfo.Weight); rb.olapCluster = generator.AddCluster( olapParentCluster, incClus /* userData */, rb.MinHeight, rb.MinWidth, rb.BottomBorderInfo, rb.TopBorderInfo, postXLeftBorderInfo, postXRightBorderInfo); rb.olapCluster.NodePadding = nodeSeparationV; rb.olapCluster.NodePaddingP = nodeSeparationH; rb.olapCluster.ClusterPadding = innerPaddingV; rb.olapCluster.ClusterPaddingP = innerPaddingH; } rb.olapCluster.TranslateChildren = rb.GenerateFixedConstraints; // Note: Incremental.Cluster always creates child List<Cluster|Node> so we don't have to check for null here. // Add our child nodes. foreach (var filNode in incClus.Nodes) { AddOlapNode(generator, rb.olapCluster, (FiNode)filNode.AlgorithmData, nodeCenter); } // Now recurse through all child clusters. foreach (var incChildClus in incClus.Clusters) { AddOlapClusters(generator, rb.olapCluster, incChildClus, nodeCenter); } }
public DCluster AddCluster(DCluster parent, string id) { if (parent == null) { Graph.LayoutAlgorithmSettings = new FastIncrementalLayoutSettings(); Graph.LayoutAlgorithmSettings.EdgeRoutingSettings.KeepOriginalSpline = true; var settings = Graph.LayoutAlgorithmSettings as FastIncrementalLayoutSettings; settings.AvoidOverlaps = true; settings.NodeSeparation = 30; settings.RouteEdges = true; } Subgraph cluster = new Subgraph(id == null ? GetNewNodeID() : id); Cluster geometryCluster = new Cluster() { GeometryParent = parent == null ? Graph.GeometryGraph : (GeometryObject)parent.GeometryNode }; cluster.GeometryNode = geometryCluster; geometryCluster.UserData = cluster; DCluster ret = new DCluster((DObject)parent ?? this, cluster); cluster.LabelText = null; SetNodeBoundaryCurve(Graph, cluster); if (parent == null) m_RootCluster = ret; else parent.AddCluster(ret); if (UpdateGraphBoundingBoxPreservingCenter()) Invalidate(); AddCluster(ret, false); return ret; }
void AddRootCluster(GeometryGraph graph) { var c = new Cluster(graph.Nodes); graph.RootCluster = c; }
static bool Ancestor(Cluster root, Node node) { if (node.ClusterParents.Contains(root)) return true; var parents = new Queue<Cluster>(node.ClusterParents); while (parents.Count > 0) { Cluster parent = parents.Dequeue(); if (root.Clusters.Contains(parent)) return true; foreach (Cluster grandParent in parent.ClusterParents) parents.Enqueue(grandParent); } return false; }
static GeometryGraph GenerateGraphWithGroups(Random random, int multiplier) { int clusterCount = (2 + random.Next(5))*multiplier; int nodeCount = (clusterCount + random.Next(20))*multiplier; int edgeCount = (10 + random.Next(20))*multiplier; //tree of clusters var parent = GenerateClusterTree(clusterCount, random); GeometryGraph graph = new GeometryGraph(); //create nodes for (int i = 0; i < nodeCount; i++) { Node node = GraphGenerator.CreateNode(i); graph.Nodes.Add(node); } //create clusters var clusters = new Cluster[clusterCount]; for (int i = 0; i < clusterCount; i++) { clusters[i] = new Cluster(); clusters[i].BoundaryCurve = CurveFactory.CreateRectangle(30, 30, new Point(15, 15)); clusters[i].RectangularBoundary = new RectangularClusterBoundary { LeftMargin = 5, RightMargin = 5, BottomMargin = 5, TopMargin = 5 }; } //set cluster hiearchy graph.RootCluster = clusters[0]; for (int i = 1; i < clusterCount; i++) { clusters[parent[i]].AddChild(clusters[i]); } //put nodes to clusters for (int i = 0; i < nodeCount; i++) { clusters[random.Next(clusterCount)].AddChild(graph.Nodes[i]); } for (int i = 0; i < clusterCount; i++) { if (clusters[i].Nodes.Count() == 0 && clusters[i].Clusters.Count() == 0) { Node node = GraphGenerator.CreateNode(i); graph.Nodes.Add(node); clusters[i].AddChild(node); nodeCount++; } } //adding edges for (int i = 0; i < edgeCount; i++) { int s = random.Next(nodeCount + clusterCount - 1); Node snode = (s < nodeCount ? graph.Nodes[s] : clusters[s - nodeCount + 1]); int t = random.Next(nodeCount + clusterCount - 1); Node tnode = (t < nodeCount ? graph.Nodes[t] : clusters[t - nodeCount + 1]); if (EdgeIsValid(snode, tnode)) { var edge = new Edge(snode, tnode); edge.LineWidth = 0.5 + 3 * random.NextDouble(); graph.Edges.Add(edge); } else i--; } SetupPorts(graph); return graph; }
/// <summary> /// find ancestor of node that is immediate child of root, or node itself if node is a direct child of root /// null if none /// </summary> /// <param name="node"></param> /// <param name="root"></param> /// <returns>returns highest ancestor of node (or node itself) that is a direct child of root, null if not /// a descendent of root</returns> internal static Node Ancestor(Node node, Cluster root) { if (node.ClusterParents.Contains(root)) return node; var parents = new Queue<Cluster>(node.ClusterParents); while (parents.Count > 0) { Cluster parent = parents.Dequeue(); if (root.Clusters.Contains(parent)) return parent; foreach (Cluster grandParent in parent.ClusterParents) parents.Enqueue(grandParent); } return null; }
private void DebugVerifyClusters(ConstraintGenerator generator, Cluster incCluster, Cluster root) { double dblEpsilon = 0.0001; // First verify that all nodes are within the cluster. Rectangle clusRect = incCluster.RectangularBoundary.rectangle; foreach (var v in incCluster.Nodes) { FiNode iiFilNode = (FiNode)v.AlgorithmData; Rectangle iiNodeRect = iiFilNode.mNode.BoundaryCurve.BoundingBox; if (IsHorizontal) { // Don't check containment for the root ClusterHierarchy as there is no border for it. if (incCluster != root) { // This is horizontal so we've not yet calculated the Y-axis stuff. The only thing we // can do is verify we're within cluster X bounds. If *Space is negative, there's overlap. // Generator primary axis is horizontal so use its Padding. double dblLboundSpace = iiNodeRect.Left - clusRect.Left - generator.Padding; double dblRboundSpace = clusRect.Right - iiNodeRect.Right - generator.Padding; Debug.Assert((dblLboundSpace >= -dblEpsilon) && (dblRboundSpace >= -dblEpsilon) , "Node is not within parent Cluster"); } } else { // Don't check containment for the root ClusterHierarchy as there is no border for it. if (incCluster != root) { // This is vertical so we've calculated the Y-axis stuff and horizontal is Perpendicular. DebugVerifyRectContains(clusRect, iiNodeRect , generator.PaddingP, generator.Padding, dblEpsilon); } // Make sure the node doesn't intersect any following nodes, or any clusters. foreach (var u in incCluster.Nodes) { if (u == v) continue; FiNode jjFilNode = (FiNode)u.AlgorithmData; Rectangle jjNodeRect = jjFilNode.mNode.BoundaryCurve.BoundingBox; // We've already added the padding for the node so don't add it for the jjNode/Cluster. DebugVerifyRectsDisjoint(iiNodeRect, jjNodeRect , generator.PaddingP, generator.Padding, dblEpsilon); } foreach (Cluster incClusComp in incCluster.Clusters) { DebugVerifyRectsDisjoint(iiNodeRect, incClusComp.RectangularBoundary.rectangle , generator.PaddingP, generator.Padding, dblEpsilon); } } // endif isHorizontal } // endfor iiNode // Now verify the clusters are contained and don't overlap. foreach (var iiIncClus in incCluster.Clusters) { Rectangle iiClusRect = iiIncClus.RectangularBoundary.rectangle; if (IsHorizontal) { // Don't check containment for the root ClusterHierarchy as there is no border for it. if (incCluster != root) { // This is horizontal so we've not yet calculated the Y-axis stuff. The only thing we // can do is verify we're within cluster X bounds. If *Space is negative, there's overlap. // Generator primary axis is horizontal so use its Padding. double dblLboundSpace = iiClusRect.Left - clusRect.Left - generator.Padding; double dblRboundSpace = clusRect.Right - iiClusRect.Right - generator.Padding; Debug.Assert((dblLboundSpace >= -dblEpsilon) && (dblRboundSpace >= -dblEpsilon) , "Cluster is not within parent Cluster"); } } else { // Don't check containment for the root ClusterHierarchy as there is no border for it. if (incCluster != root) { // This is vertical so we've calculated the Y-axis stuff and horizontal is Perpendicular. DebugVerifyRectContains(clusRect, iiClusRect , generator.PaddingP, generator.Padding, dblEpsilon); } // Make sure the cluster doesn't intersect any following clusters. foreach (var jjIncClus in incCluster.Clusters) { if (jjIncClus == iiIncClus) continue; Rectangle jjClusRect = jjIncClus.RectangularBoundary.rectangle; DebugVerifyRectsDisjoint(iiClusRect, jjClusRect , generator.PaddingP, generator.Padding, dblEpsilon); } } // endif isHorizontal // Now recurse. DebugVerifyClusters(generator, iiIncClus, root); } // endfor iiCluster }
void WriteChildClusters(Cluster cluster) { WriteAttribute(GeometryToken.ChildClusters, String.Join(" ", cluster.Clusters.Select(child => NodeToIds[child]))); }
private static bool SetOfActiveNodesIsLargerThanThreshold(Cluster ancestor, Node node, Set<Node> usedNodeSet, int threshold) { Cluster parent = Parent(node); do { foreach (var n in Children(parent)) { usedNodeSet.Insert(n); if (usedNodeSet.Count > threshold) return true; } if (parent == ancestor) break; parent = Parent(parent); } while (true); usedNodeSet.Insert(parent); return usedNodeSet.Count > threshold; }
void ProcessCluster(Cluster cluster) { if (cluster.IsCollapsed) return; Rectangle oldBounds = cluster.BoundingBox; cluster.UnsetInitialLayoutStateIncludingAncestors(); LayoutCluster(cluster); if (cluster != graph.RootCluster) { Rectangle newBounds = cluster.BoundingBox; cluster.DeepTranslation(oldBounds.Center - newBounds.Center, true); } #if DEBUG // ValidateLayout(cluster); #endif }
void WriteChildNodes(Cluster cluster) { WriteAttribute(GeometryToken.ChildNodes, string.Join(" ", cluster.nodes.Select(child => nodeIds[child].ToString(CultureInfo.InvariantCulture)))); }
static void RouteSimplHooksAndFillTheLists(Cluster rootCluster, List<Edge> inParentEdges, List<Edge> outParentEdges, EdgeRoutingSettings edgeRoutingSettings) { var padding = edgeRoutingSettings.Padding + edgeRoutingSettings.PolylinePadding; foreach (var cluster in rootCluster.AllClustersWidthFirstExcludingSelfAvoidingChildrenOfCollapsed().Where(c=>!c.IsCollapsed)) { RouteClusterParentInEdges(inParentEdges, edgeRoutingSettings, cluster, padding); RouteClusterParentOutEdges(outParentEdges, edgeRoutingSettings, cluster, padding); } }
void MapClustersToIds(Cluster cluster) { string id; var setOfIds = new Set<string>(nodeIds.Values); foreach (Cluster child in cluster.AllClustersDepthFirst()) { if (!nodeIds.TryGetValue(child, out id)) { id = FindNewId(setOfIds); nodeIds[child] = id; setOfIds.Insert(id); } } }
static void RouteClusterParentInEdges(List<Edge> inParentEdges, EdgeRoutingSettings edgeRoutingSettings, Cluster cluster, double padding) { foreach (var e in cluster.InEdges.Where(e => IsDescendant(e.Source, cluster))) { double ePadding = Math.Max(padding, 1.5*ArrowlengthAtTarget(e)); var hookPort = e.TargetPort as HookUpAnywhereFromInsidePort; if (hookPort == null) e.TargetPort = hookPort = new HookUpAnywhereFromInsidePort(() => cluster.BoundaryCurve); hookPort.HookSize = ePadding; e.Curve = StraightLineEdges.CreateLoop(e.Source.BoundingBox, cluster.BoundingBox, ePadding, false); Arrowheads.TrimSplineAndCalculateArrowheads(e, e.Curve, false, edgeRoutingSettings.KeepOriginalSpline); inParentEdges.Add(e); } }
/// <summary> /// Determines if this node is a descendant of the given cluster. /// </summary> /// <returns>True if the node is a descendant of the cluster. False otherwise.</returns> public bool IsDescendantOf(Cluster cluster) { Queue<Cluster> parents = new Queue<Cluster>(this.ClusterParents); while (parents.Count > 0) { Cluster parent = parents.Dequeue(); if (parent == cluster) { return true; } foreach (Cluster grandParent in parent.ClusterParents) { parents.Enqueue(grandParent); } } return false; }
// void CheckEdges() { // foreach (var edge in graph.Edges) // CheckEdge(edge); // } // // static void CheckEdge(Edge edge) { // var s = edge.Source; // var t = edge.Target; // var sParents = new Set<Node>(s.ClusterParents); // var tParents = new Set<Node>(t.ClusterParents); // if (sParents == tParents) // the edge is between of the nodes of the same cluster, in a simple case // return; // var cluster = t as Cluster; // if (cluster != null && IsDescendant(s, cluster)) // return; // cluster = s as Cluster; // if (cluster != null && IsDescendant(t, cluster)) // return; // Debug.Assert(false, "an edge can be flat or connecting with an ancestor"); // } /// <summary> /// Ensures that containment is preserved /// </summary> /// <param name="cluster">check is applied to specified cluster and below</param> static void ValidateLayout(Cluster cluster) { foreach (var c in cluster.AllClustersDepthFirst()) foreach (var v in c.nodes.Concat(c.Clusters.Cast<Node>())) Debug.Assert(c.BoundingBox.Contains(v.BoundingBox)); }