GleeCurveToPointFArray ( Microsoft.Glee.Splines.Curve oCurve ) { Debug.Assert(oCurve != null); AssertValid(); // This is based on code in Microsoft.Glee.GraphViewerGdi.GViewer. // TransferGeometryFromGleeGraphToGraph() in the GLEE source code, System.Collections.Generic.List<PointF> oPointFList = new System.Collections.Generic.List<PointF>(); Microsoft.Glee.Splines.Point oGleePoint = (oCurve.Segs[0] as Microsoft.Glee.Splines.CubicBezierSeg).B(0); oPointFList.Add( GleePointToPointF(oGleePoint) ); foreach (Microsoft.Glee.Splines.CubicBezierSeg oCubicBezierSeg in oCurve.Segs) { oPointFList.Add( GleePointToPointF(oCubicBezierSeg.B(1) ) ); oPointFList.Add( GleePointToPointF(oCubicBezierSeg.B(2) ) ); oPointFList.Add( GleePointToPointF(oCubicBezierSeg.B(3) ) ); } return ( oPointFList.ToArray() ); }
GleeCurveToTransformedPointFArray ( Microsoft.Glee.Splines.Curve oCurve, Matrix oTransformationMatrix ) { Debug.Assert(oCurve != null); Debug.Assert(oTransformationMatrix != null); AssertValid(); // Load the curve points into a list. System.Collections.Generic.List<PointF> oPointFList = new System.Collections.Generic.List<PointF>(); Microsoft.Glee.Splines.Point oGleePoint = (oCurve.Segs[0] as Microsoft.Glee.Splines.CubicBezierSeg).B(0); oPointFList.Add( GleePointToPointF(oGleePoint) ); foreach (Microsoft.Glee.Splines.CubicBezierSeg oCubicBezierSeg in oCurve.Segs) { oPointFList.Add( GleePointToPointF(oCubicBezierSeg.B(1) ) ); oPointFList.Add( GleePointToPointF(oCubicBezierSeg.B(2) ) ); oPointFList.Add( GleePointToPointF(oCubicBezierSeg.B(3) ) ); } // Convert the list to an array and transform it to NodeXL coordinates. PointF [] aoCurvePoints = oPointFList.ToArray(); oTransformationMatrix.TransformPoints(aoCurvePoints); return (aoCurvePoints); }
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(); // This class does not incorporate GLEE source code to implement the // layout. Instead, it transfers the NodeXL graph to a GLEE graph, // tells GLEE to lay out the GLEE graph, then reads the resulting // layout geometry from the GLEE graph and stores it as metadata in the // NodeXL graph. Although this involves several copy operations, it // uses only the GLEE public interfaces and bypasses all the // maintenance headaches that would arise if the GLEE source code were // used. // // Note: // // This class was written when the NodeXL visualization layer used // GDI+. There were complex families of vertex and edge drawers, // including SugiyamaVertexDrawer and EdgeVertexDrawer. This class // stores vertex and edge metadata meant for use by those // Sugiyama-specific drawers, which were eliminated when the // visualization layer was changed to WPF and the vertex- and edge- // drawing code was vastly simplified. Therefore, the vertex radius // and edge curve information stored by this class is currently ignored // in the WPF layer. Vertices are of constant size, and edges are // drawn as straight lines. // // This could be fixed by modifying the WPF layer to be Sugiyama-aware, // but as of June 2009 no one has complained about these problems and // so the fix is postponed. // Create a GLEE graph. Microsoft.Glee.GleeGraph oGleeGraph = new Microsoft.Glee.GleeGraph(); // Get the vertex radius specified by the SugiyamaVertexDrawer. Single fNodeXLVertexRadius = GetNodeXLVertexRadius(layoutContext); // Loop through the NodeXL vertices. foreach (IVertex oVertex in verticesToLayOut) { // Create a circle that defines the GLEE node's boundary. GLEE's // layout code does not modify the node's boundary, it just shifts // the node's center. Microsoft.Glee.Splines.ICurve oCurve = Microsoft.Glee.Splines.CurveFactory.CreateEllipse( fNodeXLVertexRadius, fNodeXLVertexRadius, new Microsoft.Glee.Splines.Point(0, 0) ); // Create a GLEE node that corresponds to the NodeXL node. Microsoft.Glee.Node oGleeNode = new Microsoft.Glee.Node(oVertex.ID.ToString(), oCurve); oGleeGraph.AddNode(oGleeNode); // Store the GLEE node as temporary metadata in the NodeXL node. oVertex.SetValue(ReservedMetadataKeys.SugiyamaGleeNode, oGleeNode); } // Loop through the NodeXL edges. ICollection<IEdge> oEdgesToLayOut = GetEdgesToLayOut(graph, verticesToLayOut); foreach (IEdge oEdge in oEdgesToLayOut) { // Retrieve the NodeXL edge's vertices. IVertex [] aoVertices = oEdge.Vertices; // Retrieve the corresponding GLEE node for the NodeXL edge's // vertices. Microsoft.Glee.Node oGleeNode0 = NodeXLVertexToGleeNode( aoVertices[0] ); Microsoft.Glee.Node oGleeNode1 = NodeXLVertexToGleeNode( aoVertices[1] ); // Create a GLEE edge using the two GLEE nodes. Microsoft.Glee.Edge oGleeEdge = new Microsoft.Glee.Edge(oGleeNode0, oGleeNode1); oGleeGraph.AddEdge(oGleeEdge); // Store the GLEE edge as temporary metadata in the NodeXL edge. oEdge.SetValue( ReservedMetadataKeys.SugiyamaGleeEdge, oGleeEdge); } // Tell GLEE to lay out the GLEE graph. This shifts the node centers, // connects the nodes with lines, and computes the smallest rectangle // that contains all the nodes and edges. oGleeGraph.CalculateLayout(); // The rectangle computed by GLEE does not have the dimensions // specified by layoutContext.GraphRectangle. Get a transformation // that will map coordinates in the GLEE rectangle to coordinates in // the specified NodeXL rectangle. Matrix oTransformationMatrix = GetTransformationMatrix(oGleeGraph, layoutContext.GraphRectangle); // Because of the transformation, the radius of the vertices is no // longer the original fNodeXLVertexRadius. Compute the transformed // radius. PointF [] aoRadiusPoints = new PointF [] { PointF.Empty, new PointF(0, fNodeXLVertexRadius) }; oTransformationMatrix.TransformPoints(aoRadiusPoints); Double dX = aoRadiusPoints[1].X - aoRadiusPoints[0].X; Double dY = aoRadiusPoints[1].Y - aoRadiusPoints[0].Y; Single fTransformedNodeXLVertexRadius = (Single)Math.Sqrt(dX * dX + dY * dY); // Store the computed radius as metadata on the graph, to be retrieved // by SugiyamaVertexDrawer.DrawVertex(). graph.SetValue(ReservedMetadataKeys.SugiyamaComputedRadius, fTransformedNodeXLVertexRadius); // Loop through the NodeXL vertices again. foreach (IVertex oVertex in verticesToLayOut) { // Retrieve the corresponding GLEE node. Microsoft.Glee.Node oGleeNode = NodeXLVertexToGleeNode(oVertex); oVertex.RemoveKey(ReservedMetadataKeys.SugiyamaGleeNode); // Get the shifted node center and transform it to NodeXL // coordinates. if ( !VertexIsLocked(oVertex) ) { oVertex.Location = GleePointToTransformedPointF( oGleeNode.Center, oTransformationMatrix); } } // Loop through the NodeXL edges again. foreach (IEdge oEdge in oEdgesToLayOut) { // Retrieve the corresponding GLEE edge. Microsoft.Glee.Edge oGleeEdge = NodeXLEdgeToGleeEdge(oEdge); oEdge.RemoveKey(ReservedMetadataKeys.SugiyamaGleeEdge); // Get the GLEE curve that describes most (but not all) of the // edge. Microsoft.Glee.Splines.Curve oCurve = (Microsoft.Glee.Splines.Curve)oGleeEdge.Curve; // TODO: In Microsoft.Glee.GraphViewerGdi.GViewer. // TransferGeometryFromGleeGraphToGraph() in the GLEE source code, // oCurve can apparently be null. Can it be null here? For now, // assume that the answer is no. Debug.Assert(oCurve != null); // Convert the curve to an array of PointF objects in NodeXL // coordinates. PointF [] aoCurvePoints = GleeCurveToTransformedPointFArray( oCurve, oTransformationMatrix); // Store the array as metadata in the edge, to be retrieved by // SugiyamaEdgeDrawer.DrawEdge(). oEdge.SetValue( ReservedMetadataKeys.SugiyamaCurvePoints, aoCurvePoints); // Get the endpoint of the curve and transform it to NodeXL // coordinates. PointF oEndpoint = GleePointToTransformedPointF( oGleeEdge.ArrowHeadAtTargetPosition, oTransformationMatrix); // Store the endpoint as metadata in the edge, to be retrieved by // SugiyamaEdgeDrawer.DrawEdge(). oEdge.SetValue(ReservedMetadataKeys.SugiyamaEndpoint, oEndpoint); } return (true); }