void PlanarStraightLayout::doCall( const Graph &G, adjEntry adjExternal, GridLayout &gridLayout, IPoint &boundingBox, bool fixEmbedding) { // require to have a planar graph without multi-edges and self-loops; // planarity is checked below OGDF_ASSERT(isSimple(G) && isLoopFree(G)); // handle special case of graphs with less than 3 nodes if(G.numberOfNodes() < 3) { node v1, v2; switch(G.numberOfNodes()) { case 0: boundingBox = IPoint(0,0); return; case 1: v1 = G.firstNode(); gridLayout.x(v1) = gridLayout.y(v1) = 0; boundingBox = IPoint(0,0); return; case 2: v1 = G.firstNode(); v2 = G.lastNode (); gridLayout.x(v1) = gridLayout.y(v1) = gridLayout.y(v2) = 0; gridLayout.x(v2) = 1; boundingBox = IPoint(1,0); return; } } // we make a copy of G since we use planar biconnected augmentation GraphCopySimple GC(G); if(fixEmbedding) { // determine adjacency entry on external face of GC (if required) if(adjExternal != 0) { edge eG = adjExternal->theEdge(); edge eGC = GC.copy(eG); adjExternal = (adjExternal == eG->adjSource()) ? eGC->adjSource() : eGC->adjTarget(); } PlanarAugmentationFix augmenter; augmenter.call(GC); } else { adjExternal = 0; // augment graph planar biconnected m_augmenter.get().call(GC); // embed augmented graph m_embedder.get().call(GC,adjExternal); } // compute shelling order with shelling order module m_computeOrder.get().baseRatio(m_baseRatio); ShellingOrder order; m_computeOrder.get().callLeftmost(GC,order,adjExternal); // compute grid coordinates for GC NodeArray<int> x(GC), y(GC); computeCoordinates(GC,order,x,y); boundingBox.m_x = x[order(1,order.len(1))]; boundingBox.m_y = 0; node v; forall_nodes(v,GC) if(y[v] > boundingBox.m_y) boundingBox.m_y = y[v]; // copy coordinates from GC to G forall_nodes(v,G) { node vCopy = GC.copy(v); gridLayout.x(v) = x[vCopy]; gridLayout.y(v) = y[vCopy]; }
void PlanarDrawLayout::doCall( const Graph &G, adjEntry adjExternal, GridLayout &gridLayout, IPoint &boundingBox, bool fixEmbedding) { // require to have a planar graph without multi-edges and self-loops; // planarity is checked below OGDF_ASSERT(isSimple(G) && isLoopFree(G)); // handle special case of graphs with less than 3 nodes if(G.numberOfNodes() < 3) { node v1, v2; switch(G.numberOfNodes()) { case 0: boundingBox = IPoint(0,0); return; case 1: v1 = G.firstNode(); gridLayout.x(v1) = gridLayout.y(v1) = 0; boundingBox = IPoint(0,0); return; case 2: v1 = G.firstNode(); v2 = G.lastNode (); gridLayout.x(v1) = gridLayout.y(v1) = gridLayout.y(v2) = 0; gridLayout.x(v2) = 1; boundingBox = IPoint(1,0); return; } } // we make a copy of G since we use planar biconnected augmentation GraphCopySimple GC(G); if(fixEmbedding) { PlanarAugmentationFix augmenter; augmenter.call(GC); } else { // augment graph planar biconnected m_augmenter.get().call(GC); // embed augmented graph PlanarModule pm; bool isPlanar = pm.planarEmbed(GC); if(isPlanar == false) OGDF_THROW_PARAM(PreconditionViolatedException, pvcPlanar); } // compute shelling order m_computeOrder.get().baseRatio(m_baseRatio); ShellingOrder order; m_computeOrder.get().call(GC,order,adjExternal); // compute grid coordinates for GC NodeArray<int> x(GC), y(GC); computeCoordinates(GC,order,x,y); boundingBox.m_x = x[order(1,order.len(1))]; boundingBox.m_y = 0; node v; forall_nodes(v,GC) if(y[v] > boundingBox.m_y) boundingBox.m_y = y[v]; // copy coordinates from GC to G forall_nodes(v,G) { node vCopy = GC.copy(v); gridLayout.x(v) = x[vCopy]; gridLayout.y(v) = y[vCopy]; }
void TriconnectedShellingOrder::doCall( const Graph& G, adjEntry adj, List<ShellingOrderSet>& partition) { // prefer nodes to faces? bool preferNodes = false; #ifdef OUTPUT_TSO cout << "Graph G is planar == " << isPlanar(G) << endl; cout << "Graph G has no self loops == " << isLoopFree(G) << endl; cout << "Graph G is connected == " << isConnected(G) << endl; cout << "Graph G is triconnected == " << isTriconnected(G) << endl; #endif OGDF_ASSERT(isPlanar(G) == true); OGDF_ASSERT(isLoopFree(G) == true); OGDF_ASSERT(isTriconnected(G) == true); // crate an embedding for G ConstCombinatorialEmbedding E(G); // set outerFace so adj is on it or to face with maximal size face outerFace = (adj != nullptr) ? E.rightFace(adj) : E.maximalFace(); #ifdef OUTPUT_TSO cout << "faces:" << endl; for(face fh : E.faces) { if (fh == outerFace) cout << " face *" << fh->index() << ":"; else cout << " face " << fh->index() << ":"; for(adjEntry adj : fh->entries) cout << " " << adj; cout << endl; } cout << "adjacency lists:" << endl; for(node vh : G.nodes) { cout << " node " << vh << ":"; for(adjEntry adj : vh->adjEntries) cout << " " << adj; cout << endl; } #endif adjEntry firstAdj = outerFace->firstAdj(); // set firstAdj that the outer face is on the left of firstAdj if (E.rightFace(firstAdj) == outerFace) firstAdj = firstAdj->cyclicSucc(); // set "base" nodes v1, v2 on outer face with edge [v1,v2] node v1 = firstAdj->theNode(); node v2 = firstAdj->cyclicPred()->twinNode(); ComputeTricOrder cto(G, E, outerFace, m_baseRatio, preferNodes); // if outerFace == {v_1,...,v_q} // adjPred(v_i) == v_i -> v_{i-1} // adjSucc(v_i) == v_1 -> v_{i+1} // these arrays will be updated during the algo so they define the outer face NodeArray<adjEntry> adjPred(G), adjSucc(G); // init adjPred and adjSucc for the nodes of the outer face adjSucc[v1] = firstAdj; adjEntry adjRun = firstAdj->twin()->cyclicSucc(); do { adjPred[adjRun->theNode()] = adjRun->cyclicPred(); adjSucc[adjRun->theNode()] = adjRun; adjRun = adjRun->twin()->cyclicSucc(); } while (adjRun != firstAdj); adjPred[v1] = adjSucc[v2] = nullptr; // init outer nodes and outer edges cto.initOuterNodes(v1, v2); cto.initOuterEdges(); // init the first possible node as the node in the middle of v_1 // and v_2 on the outer face int l = (outerFace->size() -2)/2; if (l == 0) l = 1; adjRun = firstAdj; for (int i=1; i <= l; i++) adjRun = adjRun->twin()->cyclicSucc(); cto.initPossible(adjRun->theNode()); // node and face that are selected during the algorithm node vk; face Fk; // left and right node of current nodeset node cl, cr; // the actual nodeset V in the shelling order ShellingOrderSet V; // further auxiliary variables #ifdef OUTPUT_TSO cout << "finished initialization of cto, adjSucc, adjPred." << endl << flush; cout << "v1 = " << v1 << ", v2 = " << v2 << ", first possible node = " << adjRun->theNode() << endl; for(adjEntry adj1 : outerFace->entries) { cout << " node " << adj1->theNode() << ": adjPred=(" << adjPred[adj1->theNode()] << "), adjSucc=" << adjSucc[adj1->theNode()] << endl; } cto.output(); cout << "starting main loop" << endl; #endif // main loop while (cto.isPossible()){ // get the next possible nodeset for the order cto.getNextPossible(vk, Fk); // check if the current selection is a node or a face if (cto.isNode()){ #ifdef OUTPUT_TSO cout << " nextPossible is node " << vk << endl << flush; #endif // current item is a node V = ShellingOrderSet(1, adjPred[vk], adjSucc[vk]); V[1] = vk; cl = (adjPred[vk])->twinNode(); cr = (adjSucc[vk])->twinNode(); // insert actual nodeset to the front of the shelling order partition.pushFront(V); } else{ #ifdef OUTPUT_TSO cout << " nextPossible is face " << Fk->index() << endl << flush; #endif // current item is a face // create set with chain {z_1,...,z_l} V = ShellingOrderSet(cto.getOutv(Fk)-2); // now find node v on Fk with degree 2 cl = cto.getOuterNodeDeg2(Fk, adjPred, adjSucc); // find end of chain cl and cr // traverse to left while degree == 2 while ((cl != v1) && (adjPred[cl] == adjSucc[cl]->cyclicSucc())) cl = (adjPred[cl])->twinNode(); // traverse to the right while degree == 2 // and insert nodes into the ShellingOrderSet cr = adjSucc[cl]->twinNode(); int i = 1; while ((cr != v2) && (adjPred[cr] == adjSucc[cr]->cyclicSucc())){ V[i] = cr; cr = (adjSucc[cr])->twinNode(); i++; } cto.decSepf(cl); cto.decSepf(cr); // set left and right node in the shelling order set V.left(cl); V.right(cr); // set left and right adjacency entry V.leftAdj((adjPred[cr])->twin()); V.rightAdj((adjSucc[cl])->twin()); // insert actual nodeset to the front of the shelling order partition.pushFront(V); }// current item is a face #ifdef OUTPUT_TSO cout << " set cl = " << cl << endl; cout << " set cr = " << cr << endl; #endif // update adjSucc[cl] and adjPred[cr] adjSucc[cl] = adjSucc[cl]->cyclicSucc(); adjPred[cr] = adjPred[cr]->cyclicPred(); // increase number of outer edges of face left of adjPred[cr] cto.incOute(E.leftFace(adjPred[cr])); cto.incVisited(cl); cto.incVisited(cr); // traverse from cl to cr on the new outer face // and update adjSucc[] and adjPred[] adjEntry adj1 = adjSucc[cl]->twin(); for (node u = adj1->theNode(); u != cr; u = adj1->theNode()){ // increase oute for the right face of adj1 cto.incOute(E.leftFace(adj1)); // set new predecessor adjPred[u] = adj1; // go to next adj-entry adj1 = adj1->cyclicSucc(); // if the actual node has an edge to the deleted node // increase the visited value for the actual node... if (adj1->twinNode() == vk){ cto.incVisited(u); // ... and skip the actual adjEntry adj1 = adj1->cyclicSucc(); } adjSucc[u] = adj1; // add actual node to outerNodes[f] for (adjEntry adj2 = adjPred[u]; adj2 != adjSucc[u]; adj2 = adj2->cyclicPred()){ cto.addOuterNode(u, E.leftFace(adj2)); } adj1 = adj1->twin(); } if (!cto.isNode()){ if ( ((adjSucc[cl])->twinNode() == cr) && ( cto.isOnlyEdge(E.rightFace(adjSucc[cl])) ) ){ cto.decSepf(cl); cto.decSepf(cr); } } // update cto cto.doUpdate(); #ifdef OUTPUT_TSO cto.output(); #endif }// while (cto.isPossible()) // finally push the base (v1,v2) to the order V = ShellingOrderSet(2); V[1] = v1; V[2] = v2; partition.pushFront(V); #ifdef OUTPUT_TSO cout << "output of the computed partition:" << endl; int k = 1; for (const ShellingOrderSet &S : partition) { int size = S.len(); cout << "nodeset with nr " << k << ":" << endl; for (int j=1; j<=size; j++) cout << " node " << S[j] <<", "; cout << "." << endl; } #endif }// void TriconnectedShellingOrder::doCall