//************************************************************************* // Method: LayOutGraphCore() // /// <summary> /// Lays out a graph synchronously or asynchronously. /// </summary> /// /// <param name="graph"> /// Graph to lay out. The graph is guaranteed to have at least one vertex. /// </param> /// /// <param name="verticesToLayOut"> /// Vertices to lay out. The collection is guaranteed to have at least one /// vertex. /// </param> /// /// <param name="layoutContext"> /// Provides access to objects needed to lay out the graph. The <see /// cref="LayoutContext.GraphRectangle" /> is guaranteed to have non-zero /// width and height. /// </param> /// /// <param name="backgroundWorker"> /// <see cref="BackgroundWorker" /> whose worker thread called this method /// if the graph is being laid out asynchronously, or null if the graph is /// being laid out synchronously. /// </param> /// /// <returns> /// true if the layout was successfully completed, false if the layout was /// cancelled. The layout can be cancelled only if the graph is being laid /// out asynchronously. /// </returns> /// /// <remarks> /// This method lays out the graph <paramref name="graph" /> either /// synchronously (if <paramref name="backgroundWorker" /> is null) or /// asynchronously (if (<paramref name="backgroundWorker" /> is not null) /// by setting the the <see cref="IVertex.Location" /> property on the /// vertices in <paramref name="verticesToLayOut" /> and optionally adding /// geometry metadata to the graph, vertices, or edges. /// /// <para> /// In the asynchronous case, the <see /// cref="BackgroundWorker.CancellationPending" /> property on the /// <paramref name="backgroundWorker" /> object should be checked before /// each layout iteration. If it's true, the method should immediately /// return false. Also, <see /// cref="AsyncLayoutBase.FireLayOutGraphIterationCompleted()" /> should be /// called after each iteration. /// </para> /// /// <para> /// The arguments have already been checked for validity. /// </para> /// /// </remarks> //************************************************************************* protected override Boolean LayOutGraphCore( IGraph graph, ICollection<IVertex> verticesToLayOut, LayoutContext layoutContext, BackgroundWorker backgroundWorker ) { Debug.Assert(graph != null); Debug.Assert(verticesToLayOut != null); Debug.Assert(verticesToLayOut.Count > 0); Debug.Assert(layoutContext != null); AssertValid(); if (backgroundWorker != null && backgroundWorker.CancellationPending) { return (false); } ICollection<IEdge> oEdgesToLayOut = GetEdgesToLayOut(graph, verticesToLayOut); Int32 iVertices = verticesToLayOut.Count; // The MultiScaleLayout class uses a simple scheme where the graph's // vertices consist of the integers 0 through N-1, where N is the // number of vertices in the graph. NodeXL uses IVertex objects and // the vertex collection isn't indexed. Work around this // incompatibility by creating a dictionary that maps vertices to // zero-based vertex indexes. Dictionary<IVertex, Int32> oVertexDictionary = new Dictionary<IVertex, Int32>(iVertices); Int32 iVertexIndex = 0; foreach (IVertex oVertex in verticesToLayOut) { oVertexDictionary.Add(oVertex, iVertexIndex); iVertexIndex++; } // Create and populate a MultiScaleLayout graph. MultiScaleLayout.Graph oMultiScaleLayoutGraph = new MultiScaleLayout.Graph(iVertices, String.Empty); foreach (IEdge oEdge in oEdgesToLayOut) { IVertex [] aoEdgeVertices = oEdge.Vertices; oMultiScaleLayoutGraph.AddEdge( oVertexDictionary[ aoEdgeVertices[0] ], oVertexDictionary[ aoEdgeVertices[1] ] ); } // Lay it out. oMultiScaleLayoutGraph.PrepareForUse(); MultiScaleLayout.GraphLayoutSettings oGraphLayoutSettings = new MultiScaleLayout.GraphLayoutSettings(m_iRad, m_iLocalIterations, m_iRatio, m_iMinSize); oMultiScaleLayoutGraph.MultiScaleLayout( ( new Random() ).Next(), oGraphLayoutSettings); // Retrieve the laid out vertex coordinates, which are normalized to // fall within the range [0,1]. MultiScaleLayout.PointD [] oMultiScaleLayoutLocations = oMultiScaleLayoutGraph.vertexCoords; Rectangle oRectangle = layoutContext.GraphRectangle; Debug.Assert(oRectangle.Width > 0); Debug.Assert(oRectangle.Height > 0); Int32 iLeft = oRectangle.Left; Int32 iTop = oRectangle.Top; Int32 iWidth = oRectangle.Width; Int32 iHeight = oRectangle.Height; foreach (IVertex oVertex in verticesToLayOut) { if ( !VertexIsLocked(oVertex) ) { // Convert the normalized coordinates to coordinates within the // layout rectangle. MultiScaleLayout.PointD oMultiScaleLayoutLocation = oMultiScaleLayoutLocations[ oVertexDictionary[oVertex] ]; oVertex.Location = new PointF( iLeft + (Single)(oMultiScaleLayoutLocation.x * iWidth), iTop + (Single)(oMultiScaleLayoutLocation.y * iHeight) ); } } return (true); }
LayOutGraphCore ( IGraph graph, ICollection <IVertex> verticesToLayOut, LayoutContext layoutContext, BackgroundWorker backgroundWorker ) { Debug.Assert(graph != null); Debug.Assert(verticesToLayOut != null); Debug.Assert(verticesToLayOut.Count > 0); Debug.Assert(layoutContext != null); AssertValid(); if (backgroundWorker != null && backgroundWorker.CancellationPending) { return(false); } ICollection <IEdge> oEdgesToLayOut = GetEdgesToLayOut(graph, verticesToLayOut); Int32 iVertices = verticesToLayOut.Count; // The MultiScaleLayout class uses a simple scheme where the graph's // vertices consist of the integers 0 through N-1, where N is the // number of vertices in the graph. NodeXL uses IVertex objects and // the vertex collection isn't indexed. Work around this // incompatibility by creating a dictionary that maps vertices to // zero-based vertex indexes. Dictionary <IVertex, Int32> oVertexDictionary = new Dictionary <IVertex, Int32>(iVertices); Int32 iVertexIndex = 0; foreach (IVertex oVertex in verticesToLayOut) { oVertexDictionary.Add(oVertex, iVertexIndex); iVertexIndex++; } // Create and populate a MultiScaleLayout graph. MultiScaleLayout.Graph oMultiScaleLayoutGraph = new MultiScaleLayout.Graph(iVertices, String.Empty); foreach (IEdge oEdge in oEdgesToLayOut) { IVertex [] aoEdgeVertices = oEdge.Vertices; oMultiScaleLayoutGraph.AddEdge( oVertexDictionary[aoEdgeVertices[0]], oVertexDictionary[aoEdgeVertices[1]] ); } // Lay it out. oMultiScaleLayoutGraph.PrepareForUse(); MultiScaleLayout.GraphLayoutSettings oGraphLayoutSettings = new MultiScaleLayout.GraphLayoutSettings(m_iRad, m_iLocalIterations, m_iRatio, m_iMinSize); oMultiScaleLayoutGraph.MultiScaleLayout( (new Random()).Next(), oGraphLayoutSettings); // Retrieve the laid out vertex coordinates, which are normalized to // fall within the range [0,1]. MultiScaleLayout.PointD [] oMultiScaleLayoutLocations = oMultiScaleLayoutGraph.vertexCoords; Rectangle oRectangle = layoutContext.GraphRectangle; Debug.Assert(oRectangle.Width > 0); Debug.Assert(oRectangle.Height > 0); Int32 iLeft = oRectangle.Left; Int32 iTop = oRectangle.Top; Int32 iWidth = oRectangle.Width; Int32 iHeight = oRectangle.Height; foreach (IVertex oVertex in verticesToLayOut) { if (!VertexIsLocked(oVertex)) { // Convert the normalized coordinates to coordinates within the // layout rectangle. MultiScaleLayout.PointD oMultiScaleLayoutLocation = oMultiScaleLayoutLocations[oVertexDictionary[oVertex]]; oVertex.Location = new PointF( iLeft + (Single)(oMultiScaleLayoutLocation.x * iWidth), iTop + (Single)(oMultiScaleLayoutLocation.y * iHeight) ); } } return(true); }