void ComponentSplitterLayout::call(GraphAttributes &GA) { // Only do preparations and call if layout is valid if (m_secondaryLayout.valid()) { //first we split the graph into its components const Graph& G = GA.constGraph(); NodeArray<int> componentNumber(G); m_numberOfComponents = connectedComponents(G, componentNumber); if (m_numberOfComponents == 0) { return; } //std::vector< std::vector<node> > componentArray; //componentArray.resize(numComponents); //Array<GraphAttributes *> components(numComponents); // // intialize the array of lists of nodes contained in a CC nodesInCC.init(m_numberOfComponents); node v; forall_nodes(v,G) nodesInCC[componentNumber[v]].pushBack(v); // Create copies of the connected components and corresponding // GraphAttributes GraphCopy GC; GC.createEmpty(G); EdgeArray<edge> auxCopy(G); for (int i = 0; i < m_numberOfComponents; i++) { GC.initByNodes(nodesInCC[i],auxCopy); GraphAttributes cGA(GC); //copy information into copy GA forall_nodes(v, GC) { cGA.width(v) = GA.width(GC.original(v)); cGA.height(v) = GA.height(GC.original(v)); cGA.x(v) = GA.x(GC.original(v)); cGA.y(v) = GA.y(GC.original(v)); } m_secondaryLayout.get().call(cGA); //copy layout information back into GA forall_nodes(v, GC) { node w = GC.original(v); if (w != 0) GA.x(w) = cGA.x(v); GA.y(w) = cGA.y(v); } }
void ComponentSplitterLayout::call(GraphAttributes &GA) { // Only do preparations and call if layout is valid if (m_secondaryLayout.valid()) { //first we split the graph into its components const Graph& G = GA.constGraph(); NodeArray<int> componentNumber(G); int numberOfComponents = connectedComponents(G, componentNumber); if (numberOfComponents == 0) { return; } // intialize the array of lists of nodes contained in a CC Array<List<node> > nodesInCC(numberOfComponents); for(node v : G.nodes) nodesInCC[componentNumber[v]].pushBack(v); // Create copies of the connected components and corresponding // GraphAttributes GraphCopy GC; GC.createEmpty(G); EdgeArray<edge> auxCopy(G); for (int i = 0; i < numberOfComponents; i++) { GC.initByNodes(nodesInCC[i],auxCopy); GraphAttributes cGA(GC, GA.attributes()); //copy information into copy GA for(node v : GC.nodes) { cGA.width(v) = GA.width(GC.original(v)); cGA.height(v) = GA.height(GC.original(v)); cGA.x(v) = GA.x(GC.original(v)); cGA.y(v) = GA.y(GC.original(v)); } // copy information on edges if (GA.attributes() & GraphAttributes::edgeDoubleWeight) { for (edge e : GC.edges) { cGA.doubleWeight(e) = GA.doubleWeight(GC.original(e)); } } m_secondaryLayout.get().call(cGA); //copy layout information back into GA for(node v : GC.nodes) { node w = GC.original(v); if (w != nullptr) { GA.x(w) = cGA.x(v); GA.y(w) = cGA.y(v); if (GA.attributes() & GraphAttributes::threeD) { GA.z(w) = cGA.z(v); } } } } // rotate component drawings and call the packer reassembleDrawings(GA, nodesInCC); }//if valid }
//Connect parts of partial active current CC. //Note that this only makes sense when the CC parts are //already correctly embedded. Crossings are already replaced //by nodes bool PlanRepInc::makeTreeConnected(adjEntry /* adjExternal */) { //we compute node numbers for the partial CCs in order to //identify the treeConnect Edges that can be deleted later m_component.init(*this, -1); //if there is only one CC, we don't need to connect if (isConnected(*this)) return false; //We have to insert edges connnecting nodes lying on //the "external" face of the active parts //First, we activate the CC's parts one by one and compute //the 'external' face from the layout information //If the PlanRepInc is not embedded corresponding to //this layout, we may introduce edges that are non-planar //in the drawing, leading to problems when we compute paths //in the dual graph List<node> isolatedNodes; const int numPartialCC = connectedIsolatedComponents(*this, isolatedNodes, m_component); //CombinatorialEmbedding can cope with unconnected graphs //but does not provide faces for isolated nodes CombinatorialEmbedding E(*this); TopologyModule tm; List<adjEntry> extAdjs; //we run through all faces searching for all outer faces for(face f : E.faces) { //TODO: check if we should select special adjEntry instead of first if (tm.faceSum(*this, *m_pGraphAttributes, f) < 0) extAdjs.pushBack(f->firstAdj()); #ifdef OGDF_DEBUG cout << "FaceSum in Face " << f->index() << " Groesse " << f->size() << " ist: " << tm.faceSum(*this, *m_pGraphAttributes, f) <<"\n" << flush; #endif }//forallfaces //now we have faces for all partial CCs that are not isolated nodes OGDF_ASSERT(extAdjs.size() + isolatedNodes.size() == numPartialCC) //OGDF_ASSERT(extAdjs.size() > 1) //eigentlich: = #partial CCs //OGDF_ASSERT(extAdjs.size() == numPartialCC) const int n1 = numPartialCC-1; m_eTreeArray.init(0, n1, 0, n1, nullptr); m_treeInit = true; //Three cases: only CCs, only isolated nodes, and both (where //only one CC + isolated nodes is possible //now we connect all partial CCs by inserting edges at the adjEntries //in extAdjs and adding all isolated nodes adjEntry lastAdj = nullptr; ListIterator<adjEntry> it = extAdjs.begin(); while(it.valid()) { //for the case: one external face, multiple isolated nodes lastAdj = (*it); adjEntry adj = (*it); ListIterator<adjEntry> it2 = it.succ(); if (it2.valid()) { adjEntry adj2 = (*it2); edge eTree = newEdge(adj, adj2); m_treeEdge[eTree] = true; lastAdj = eTree->adjTarget(); //save the connection edge by CC number index //this is used when deleting obsolete edges later //after edge reinsertion m_eTreeArray(componentNumber(adj->theNode()), componentNumber(adj2->theNode())) = m_eTreeArray(m_component[adj2->theNode()], m_component[adj->theNode()]) = eTree; }//if CCs left to connect ++it; }//while while (!isolatedNodes.empty()) { node uvw = isolatedNodes.popFrontRet(); if (lastAdj) { //same block as above edge eTree = newEdge(uvw, lastAdj); m_treeEdge[eTree] = true; //save the connection edge by CC number index //this is used when deleting obsolete edges later //after edge reinsertion m_eTreeArray(componentNumber(lastAdj->theNode()), componentNumber(uvw)) = m_eTreeArray(m_component[uvw], m_component[lastAdj->theNode()]) = eTree; lastAdj = eTree->adjSource(); } else //connect the first two isolated nodes / only iso nodes exist { //MUST BE #isonodes>1, else we returned already because CC connected OGDF_ASSERT(!isolatedNodes.empty()) node secv = isolatedNodes.popFrontRet(); //same block as above edge eTree = newEdge(uvw, secv); m_treeEdge[eTree] = true; //save the connection edge by CC number index //this is used when deleting obsolete edges later //after edge reinsertion m_eTreeArray(componentNumber(secv), componentNumber(uvw)) = m_eTreeArray(m_component[uvw], m_component[secv]) = eTree; lastAdj = eTree->adjSource(); } }//while isolated nodes OGDF_ASSERT(isConnected(*this)); //List<adjEntry> extAdjs; //getExtAdjs(extAdjs); return true; }//makeStarConnected