//************************************************************************* // Method: getConnectivityBetween() // /// <summary> /// Get whether there is connectivity between two groups in a graph. /// </summary> /// /// <param name="g1"> /// Group 1 /// </param> /// /// <param name="g2"> /// Group 2 /// </param> /// /// <param name="oGraph"> /// The graph these groups are in /// </param> /// /// <returns> /// True if there is connectivity, false otherwise. /// </returns> //************************************************************************* public bool getConnectivityBetween(GroupInfo g1, GroupInfo g2, IGraph oGraph) { IntergroupEdgeCalculator m_oIntergroupEdgeCalculator = new IntergroupEdgeCalculator(); // Currently uses undirected graph IList<IntergroupEdgeInfo> oIntergroupEdges = m_oIntergroupEdgeCalculator.CalculateGraphMetrics(oGraph, new GroupInfo[] { g1, g2 }, false); foreach (IntergroupEdgeInfo e in oIntergroupEdges) { if (e.Group1Index == 0 && e.Group2Index == 1) { return e.Edges > 0; } } return false; }
CreateGraph ( Boolean bIsDirected ) { m_oIntergroupEdgeCalculator = new IntergroupEdgeCalculator(); m_oGraph = new Graph(bIsDirected ? GraphDirectedness.Directed : GraphDirectedness.Undirected); m_oVertices = m_oGraph.Vertices; m_oEdges = m_oGraph.Edges; }
LayOutGraphUsingForceDirectedGroups ( IGraph oGraph, List<GroupInfo> oGroupsToLayout, LayoutContext oAdjustedLayoutContext, BackgroundWorker oBackgroundWorker ) { Debug.Assert(oGraph != null); Debug.Assert(oAdjustedLayoutContext != null); ///////////////////////////////////////////////////// // Get the initial position for the group vertices // ///////////////////////////////////////////////////// // Determine how much of the screen to use for groups Single areaPerVertex = (Single)GroupRectangleCalculator.GetAreaPerVertex(oAdjustedLayoutContext.GraphRectangle, oGroupsToLayout); areaPerVertex /= 2f; // Customise this value if necessary to have more whitespace between groups -- expose as a user control? // Create a graph for the groups Graph groupInitialGraph = new Graph(GraphDirectedness.Undirected); // Convert GroupInfos into vertices that can be laid out //IEnumerable<GroupVertex> groupInitialGraphVertices = groupsToLayOut.Select<GroupInfo, GroupVertex>(g => new GroupVertex(g, groupInitialGraph)); List<GroupVertex> groupInitialGraphVertices = new List<GroupVertex>(); foreach (GroupInfo groupInfo in oGroupsToLayout) { GroupVertex groupVertex = new GroupVertex(groupInfo, areaPerVertex); groupInitialGraphVertices.Add(groupVertex); groupInitialGraph.Vertices.Add(groupVertex); } Int32 iGroups = oGroupsToLayout.Count; // Add edges between groups IVertexCollection oVertices = groupInitialGraph.Vertices; IEdgeCollection oEdges = groupInitialGraph.Edges; IList<IntergroupEdgeInfo> intergroupEdgeInfos = new IntergroupEdgeCalculator().CalculateGraphMetrics(oGraph, oGroupsToLayout, false); foreach (IntergroupEdgeInfo interGroupEdgeInfo in intergroupEdgeInfos) { IVertex groupVertex1 = oVertices.ElementAt<IVertex>(interGroupEdgeInfo.Group1Index); IVertex groupVertex2 = oVertices.ElementAt<IVertex>(interGroupEdgeInfo.Group2Index); if (groupVertex1 != groupVertex2) { oEdges.Add(groupVertex1, groupVertex2, false); } } // Use Harel-Koren to compute initial group positions, stored in groupVertex.Location ILayout oLayout = new HarelKorenFastMultiscaleLayout(); oLayout.LayOutGraph(groupInitialGraph, oAdjustedLayoutContext); foreach (GroupVertex groupVertex in groupInitialGraph.Vertices) { // If we have too few groups, automatically push them into the AdjustedLayoutContext if (groupInitialGraph.Vertices.Count <= 3) { groupVertex.updateGroupInfoRectangleFromLocation(); groupVertex.groupInfo.Rectangle = GraphicsUtil.RectangleFToRectangle(GraphicsUtil.MoveRectangleWithinBounds(groupVertex.groupInfo.Rectangle, oAdjustedLayoutContext.GraphRectangle, false), 1); RectangleF r = groupVertex.groupInfo.Rectangle; groupVertex.Location = new PointF(r.X + r.Width / 2, r.Y + r.Height / 2); } // Update the meta-node locations groupVertex.updateGroupInfoLocationFromLocation(); } ///////////////////////// // Remove node overlap // ///////////////////////// // while ( overlaps exist along proximity graph edges ) // { // Create proximity graph (delaunay triangulation) // Minimize proxmity graph overlap stress function // } // // while ( any overlaps exist ) // { // Create proximity graph (delaunay triangulation) // Find overlaps (scan-line alg) // Augment proximity graph with edges for overlaps // Minimize proxmity graph overlap stress function // } Boolean initialOverlapInProximityGraph; Int32 iteration = 0; Graph lastGraphAfterFirstPhase = groupInitialGraph; do { Graph groupProximityGraph = createGroupVertexProximityGraph(lastGraphAfterFirstPhase); lastGraphAfterFirstPhase = groupProximityGraph; initialOverlapInProximityGraph = proximityGraphStressMajorization(groupProximityGraph, 1000); iteration++; } while (initialOverlapInProximityGraph == true); /////////////////////////////////////////////////////////////////// // Transform the layout to get the boxes within the LayoutContext// /////////////////////////////////////////////////////////////////// Rectangle boundingRectangle = oAdjustedLayoutContext.GraphRectangle; Int32 overallLeftOffset = Int32.MinValue; Int32 overallTopOffset = Int32.MinValue; Int32 overallRightOffset = Int32.MinValue; Int32 overallBottomOffset = Int32.MinValue; foreach (GroupInfo groupInfo in oGroupsToLayout) { Rectangle groupRectangle = groupInfo.Rectangle; Int32 rectangleLeftOffset = boundingRectangle.Left - groupRectangle.Left; if (rectangleLeftOffset > overallLeftOffset) { overallLeftOffset = rectangleLeftOffset; } Int32 rectangleTopOffset = boundingRectangle.Top - groupRectangle.Top; if (rectangleTopOffset > overallTopOffset) { overallTopOffset = rectangleTopOffset; } Int32 rectangleRightOffset = groupRectangle.Right - boundingRectangle.Right; if (rectangleRightOffset > overallRightOffset) { overallRightOffset = rectangleRightOffset; } Int32 rectangleBottomOffset = groupRectangle.Bottom - boundingRectangle.Bottom; if (rectangleBottomOffset > overallBottomOffset) { overallBottomOffset = rectangleBottomOffset; } } Rectangle newBoundingRectangle = new Rectangle( boundingRectangle.X - overallLeftOffset, boundingRectangle.Y - overallTopOffset, boundingRectangle.Width + overallLeftOffset + overallRightOffset, boundingRectangle.Height + overallTopOffset + overallBottomOffset); // Perform metadata-related tasks. GroupMetadataManager.OnLayoutUsingGroupsEnd(lastGraphAfterFirstPhase, oGroupsToLayout, this.GroupRectanglePenWidth, this.IntergroupEdgeStyle); lastGraphAfterFirstPhase.SetValue(ReservedMetadataKeys.GroupLayoutDrawingInfo, new GroupLayoutDrawingInfo(oGroupsToLayout, this.GroupRectanglePenWidth, null)); //TransformLayout(oGraph, new LayoutContext(newBoundingRectangle), oAdjustedLayoutContext); TransformLayout(lastGraphAfterFirstPhase, new LayoutContext(newBoundingRectangle), oAdjustedLayoutContext); /////////////////////////////////// // Finish with individual groups // /////////////////////////////////// for (Int32 i = 0; i < iGroups; i++) { GroupInfo oGroupInfo = oGroupsToLayout[i]; Rectangle oGroupRectangle = oGroupInfo.Rectangle; oGroupRectangle.Inflate(-m_iMargin, -m_iMargin); // Do not use Rectangle.IsEmpty() here. It returns true only if // the rectangle is at the origin and has zero width and height. if (oGroupRectangle.Width > 0 && oGroupRectangle.Height > 0) { ICollection<IVertex> oVerticesInGroup = oGroupInfo.Vertices; if ( !GetLayoutToUseForGroup(oGraph, oVerticesInGroup). LayOutGraphCore(oGraph, oVerticesInGroup, new LayoutContext(oGroupRectangle), oBackgroundWorker) ) { // LayOutGraphAsyncCancel() was called. return (false); } } else { oGroupRectangle = oGroupInfo.Rectangle; PointF oVertexLocation; if (oGroupRectangle.Width > 0 && oGroupRectangle.Height > 0) { // Put the group's vertices at the rectangle's center. oVertexLocation = new PointF( oGroupRectangle.Left + oGroupRectangle.Width / 2, oGroupRectangle.Top + oGroupRectangle.Height / 2 ); } else { // Put the group's vertices at the rectangle's upper-left // corner. oVertexLocation = new PointF( oGroupRectangle.X, oGroupRectangle.Y ); } foreach (IVertex oVertex in oGroupInfo.Vertices) { oVertex.Location = oVertexLocation; } } } // Perform metadata-related tasks. GroupMetadataManager.OnLayoutUsingGroupsEnd(oGraph, oGroupsToLayout, this.GroupRectanglePenWidth, this.IntergroupEdgeStyle); return (true); }