Graph::~Graph() { ListIterator<NodeArrayBase*> itVNext; for(ListIterator<NodeArrayBase*> itV = m_regNodeArrays.begin(); itV.valid(); itV = itVNext) { itVNext = itV.succ(); (*itV)->disconnect(); } ListIterator<EdgeArrayBase*> itENext; for(ListIterator<EdgeArrayBase*> itE = m_regEdgeArrays.begin(); itE.valid(); itE = itENext) { itENext = itE.succ(); (*itE)->disconnect(); } ListIterator<AdjEntryArrayBase*> itAdjNext; for(ListIterator<AdjEntryArrayBase*> itAdj = m_regAdjArrays.begin(); itAdj.valid(); itAdj = itAdjNext) { itAdjNext = itAdj.succ(); (*itAdj)->disconnect(); } for (node v = m_nodes.begin(); v; v = v->succ()) { v->m_adjEdges.~GraphList<AdjElement>(); } }
// Recursive call for testing c-planarity of the clustered graph // that is induced by cluster act bool CconnectClusterPlanar::planarityTest(ClusterGraph &C, cluster &act, Graph &G) { // Test children first ListConstIterator<cluster> it; for (it = act->cBegin(); it.valid();) { ListConstIterator<cluster> succ = it.succ(); cluster next = (*it); if (!planarityTest(C,next,G)) return false; it = succ; } // Get induced subgraph of cluster act and test it for planarity List<node> subGraphNodes; ListIterator<node> its; for (its = act->nBegin(); its.valid(); its++) subGraphNodes.pushBack(*its); Graph subGraph; NodeArray<node> table; inducedSubGraph(G,subGraphNodes.begin(),subGraph,table); // Introduce super sink and add edges corresponding // to outgoing edges of the cluster node superSink = subGraph.newNode(); EdgeArray<node> outgoingTable(subGraph,0); for (its = act->nBegin(); its.valid(); its++) { node w = (*its); adjEntry adj = w->firstAdj(); forall_adj(adj,w) { edge e = adj->theEdge(); edge cor = 0; if (table[e->source()] == 0) // edge is connected to a node outside the cluster { cor = subGraph.newEdge(table[e->target()],superSink); outgoingTable[cor] = e->source(); } else if (table[e->target()] == 0) // dito { cor = subGraph.newEdge(table[e->source()],superSink); outgoingTable[cor] = e->target(); } // else edge connects two nodes of the cluster } }
node UMLGraph::replaceByStar(List<node> &clique, NodeArray<int> &cliqueNum) { if (clique.empty()) return nullptr; //insert an additional center node node center = m_pG->newNode(); width(center) = m_cliqueCenterSize; height(center) = m_cliqueCenterSize; #ifdef OGDF_DEBUG //should ask for attributes if(has(nodeStyle)) fillColor(center) = Color(0x55,0x55,0x55); #endif //we delete all edges inzident to two clique nodes //store all of them first in delEdges List<edge> delEdges; //TODO: Store edge type for all deleted edges ListIterator<node> it = clique.begin(); while (it.valid()) { node v = (*it); int numIt = cliqueNum[v]; for(adjEntry ad : v->adjEntries) { if (cliqueNum[ad->twinNode()] == numIt) { if (ad->theEdge()->source() == v) { //m_cliqueEdges[v].pushBack(new CliqueInfo(ad->theEdge()->target(), ad->theEdge()->index())); delEdges.pushBack(ad->theEdge()); } }//if } //connect center node to clique node edge inserted = m_pG->newEdge(center, v); this->type(inserted) = Graph::association; m_replacementEdge[inserted] = true; ++it; }//while //now delete all edges ListIterator<edge> itEdge = delEdges.begin(); while (itEdge.valid()) { //m_pG->delEdge((*itEdge)); m_hiddenEdges->hide(*itEdge); ++itEdge; }//while return center; }//replaceByStar
node CliqueReplacer::replaceByStar(List<node> &clique, NodeArray<int> &cliqueNum) { if (clique.empty()) return 0; //insert an additional center node node center = m_G.newNode(); m_ga.width(center) = m_cliqueCenterSize; m_ga.height(center) = m_cliqueCenterSize; #ifdef OGDF_DEBUG //should ask for attributes if(m_ga.attributes() & GraphAttributes::nodeStyle) m_ga.fillColor(center) = Color(0x55,0x55,0x55); #endif //we delete all edges inzident to two clique nodes //store all of them first in delEdges List<edge> delEdges; ListIterator<node> it = clique.begin(); while (it.valid()) { node v = (*it); adjEntry ad; int numIt = cliqueNum[v]; forall_adj(ad, v) { if (cliqueNum[ad->twinNode()] == numIt) { if (ad->theEdge()->source() == v) { //m_cliqueEdges[v].pushBack(new CliqueInfo(ad->theEdge()->target(), ad->theEdge()->index())); delEdges.pushBack(ad->theEdge()); } }//if }//foralladj //connect center node to clique node edge inserted = m_G.newEdge(center, v); m_replacementEdge[inserted] = true; it++; }//while //now delete all edges ListIterator<edge> itEdge = delEdges.begin(); while (itEdge.valid()) { //m_pG->delEdge((*itEdge)); m_G.hideEdge((*itEdge)); itEdge++; }//while return center; }//replaceByStar
// this functions sets the crossingMatrix according to candidateCrossings void Planarity::internalCandidateTaken() { ListIterator<ChangedCrossing> it; for(it = m_crossingChanges.begin(); it.valid(); ++ it) { ChangedCrossing cc = *(it); (*m_crossingMatrix)(cc.edgeNum1,cc.edgeNum2) = cc.cross; } }
List<string> DavidsonHarel::returnEnergyFunctionNames() { List<string> names; ListIterator<EnergyFunction*> it; for(it = m_energyFunctions.begin(); it.valid(); it = it.succ()) names.pushBack((*it)->getName()); return names; }
List<double> DavidsonHarel::returnEnergyFunctionWeights() { List<double> weights; ListIterator<double> it; for(it = m_weightsOfEnergyFunctions.begin(); it.valid(); it = it.succ()) weights.pushBack(*it); return weights; }
void MaxCPlanarMaster::generateVariablesForFeasibility(const List<ChunkConnection*>& ccons, List<EdgeVar*>& connectVars) { List<ChunkConnection*> cpy(ccons); #if 0 for(ChunkConnection *cc : cpy) { cc->printMe(); } #endif ArrayBuffer<ListIterator<NodePair> > creationBuffer(ccons.size()); for (ListIterator<NodePair> npit = m_inactiveVariables.begin(); npit.valid(); ++npit) { bool select = false; #if 0 (*npit).printMe(); #endif ListIterator<ChunkConnection*> ccit = cpy.begin(); while(ccit.valid()) { if((*ccit)->coeff(*npit)) { ListIterator<ChunkConnection*> delme = ccit; ++ccit; cpy.del(delme); select = true; } else ++ccit; } if(select) { #if 0 Logger::slout() << "<--CREATE"; #endif creationBuffer.push(npit); } if(cpy.size()==0) break; } #if 0 for(ChunkConnection *cc : cpy) { cc->printMe(); } #endif OGDF_ASSERT(cpy.size()==0); Logger::slout() << "Creating " << creationBuffer.size() << " Connect-Variables for feasibility\n"; m_varsInit = creationBuffer.size(); // realize creationList for(int i = creationBuffer.size(); i-- > 0;) { connectVars.pushBack( createVariable( creationBuffer[i] ) ); } }
void DPolyline::convertToInt() { ListIterator<DPoint> iter; for (iter = begin(); iter.valid(); ++iter) { DPoint &p = *iter; p.m_x = DRound(p.m_x * s_prec); p.m_y = DRound(p.m_y * s_prec); } }
//steps through all energy functions and adds the initial energy computed by each //function for the start layout void DavidsonHarel::computeInitialEnergy() { OGDF_ASSERT(!m_energyFunctions.empty()); ListIterator<EnergyFunction*> it; ListIterator<double> it2; it2 = m_weightsOfEnergyFunctions.begin(); for(it = m_energyFunctions.begin(); it.valid() && it2.valid(); it=it.succ(), it2 = it2.succ()) m_energy += (*it)->energy() * (*it2); }
ClusterGraph::~ClusterGraph() { for(ListIterator<ClusterArrayBase*> it = m_regClusterArrays.begin(); it.valid(); ++it) { (*it)->disconnect(); } clear(); }
void RadialTreeLayout::Grouping::computeAdd(double &D, double &W) { D = W = 0; ListIterator<Group> it; for(it = begin(); it.valid(); ++it) { Group &g = *it; D += g.m_sumD; if(g.m_leafGroup == true) continue; W += g.m_sumW; ListIterator<Group> itL; itL = it.pred(); if(itL.valid() == false) { g.m_leftAdd = 0.0; } else { ListIterator<Group> itR = itL.pred(); if(itR.valid() == false) g.m_leftAdd = (*itL).m_sumD; else g.m_leftAdd = (*itL).m_sumD * g.m_sumW / (*itR).m_sumW; } itL = it.succ(); if(itL.valid() == false) { g.m_leftAdd = 0.0; } else { ListIterator<Group> itR = itL.succ(); if(itR.valid() == false) g.m_leftAdd = (*itL).m_sumD; else g.m_leftAdd = (*itL).m_sumD * g.m_sumW / (*itR).m_sumW; } } }
void NodePairEnergy::internalCandidateTaken() { node v = testNode(); int candNum = (*m_nodeNums)[v]; ListIterator<node> it; for(it = m_nonIsolated.begin(); it.valid(); ++ it) { if((*it) != v) { int numit = (*m_nodeNums)[*it]; (*m_pairEnergy)(min(numit,candNum),max(numit,candNum)) = m_candPairEnergy[*it]; m_candPairEnergy[*it] = 0.0; } } }
// replace each node set in cliques by a star connecting // a new center node with all nodes in set, deletes all // edges between nodes in set, lists need to be disjoint // TODO: think about directly using the cliquenum array // output of findcliques here void UMLGraph::replaceByStar(List< List<node> > &cliques) { m_cliqueCircleSize.init(*m_pG); m_cliqueCirclePos.init(*m_pG); //m_cliqueEdges.init(*m_pG); m_replacementEdge.init(*m_pG, false); if (cliques.empty()) return; //we save membership of nodes in each list NodeArray<int> cliqueNum(*m_pG, -1); ListIterator< List<node> > it = cliques.begin(); int num = 0; while (it.valid()) { ListIterator<node> itNode = (*it).begin(); while (itNode.valid()) { cliqueNum[(*itNode)] = num; ++itNode; }//while num++; ++it; }//while //now replace each list it = cliques.begin(); while (it.valid()) { node newCenter = replaceByStar((*it), cliqueNum); OGDF_ASSERT(newCenter) m_centerNodes.pushBack(newCenter); //now we compute a circular drawing of the replacement //and save its size and the node positions m_cliqueCircleSize[newCenter] = circularBound(newCenter); ++it; }//while }//replacebystar
// The function [[emptyAllPertinentNodes]] has to be called after a reduction // has been processed. This overloaded function first destroys all full nodes // by marking them as TO_BE_DELETED and then calling the base class function // [[emptyAllPertinentNodes]]. void PlanarPQTree::emptyAllPertinentNodes() { ListIterator<PQNode<edge,indInfo*,bool>*> it; for (it = m_pertinentNodes->begin(); it.valid(); it++) { PQNode<edge,indInfo*,bool>* nodePtr = (*it); if (nodePtr->status() == FULL) destroyNode(nodePtr); } if (m_pertinentRoot) m_pertinentRoot->status(FULL); PQTree<edge,indInfo*,bool>::emptyAllPertinentNodes(); }
// this functions sets the crossingMatrix according to candidateCrossings void Planarity::internalCandidateTaken() { ListIterator<ChangedCrossing> it; for(it = m_crossingChanges.begin(); it.valid(); ++ it) { ChangedCrossing cc = *(it); (*m_crossingMatrix)(cc.edgeNum1,cc.edgeNum2) = cc.cross; } #ifdef OGDF_DEBUG int cros = 0; for(int i = 1; i < m_nonSelfLoops.size(); i++) for(int j = i+1; j <= m_nonSelfLoops.size(); j++) cros += (*m_crossingMatrix)(i,j); OGDF_ASSERT(cros == m_energy); #endif }
// The function [[emptyAllPertinentNodes]] has to be called after a reduction // has been processed. This overloaded function first destroys all full nodes // by marking them as TO_BE_DELETED and then calling the base class function // [[emptyAllPertinentNodes]]. void EmbedPQTree::emptyAllPertinentNodes() { ListIterator<PQNode<edge,indInfo*,bool>*> it; for (it = m_pertinentNodes->begin(); it.valid(); it++) { PQNode<edge,indInfo*,bool>* nodePtr = (*it); if (nodePtr->status() == FULL) destroyNode(nodePtr); } if (m_pertinentRoot) // Node was kept in the tree. Do not free it. m_pertinentRoot->status(FULL); PQTree<edge,indInfo*,bool>::emptyAllPertinentNodes(); }
void NodePairEnergy::computeEnergy() { int n_num = m_nonIsolated.size(); double energySum = 0.0; Array<node> numNodes(1,n_num); ListIterator<node> it; for(it = m_nonIsolated.begin(); it.valid(); ++it) { numNodes[(*m_nodeNums)[*it]] = *it; } for(int i = 1; i <= n_num-1 ; i++) { for(int j = i+1; j <= n_num; j++) { double E = computePairEnergy(numNodes[i],numNodes[j]); (*m_pairEnergy)(i,j) = E; energySum += E; } } m_energy = energySum; }
//in ClusterGraph?? //is not yet recursive!!! node collapseCluster(ClusterGraph& CG, cluster c, Graph& G) { OGDF_ASSERT(c->cCount() == 0) ListIterator<node> its; SListPure<node> collaps; //we should check here if not empty node robinson = (*(c->nBegin())); for (its = c->nBegin(); its.valid(); its++) collaps.pushBack(*its); CG.collaps(collaps, G); if (c != CG.rootCluster()) CG.delCluster(c); return robinson; }
void UpwardPlanRep::computeSinkSwitches() { OGDF_ASSERT(m_Gamma.externalFace() != nullptr); if (s_hat == nullptr) hasSingleSource(*this, s_hat); FaceSinkGraph fsg(m_Gamma, s_hat); List<adjEntry> dummyList; FaceArray< List<adjEntry> > sinkSwitches(m_Gamma, dummyList); fsg.sinkSwitches(sinkSwitches); m_sinkSwitchOf.init(*this, nullptr); for(face f : m_Gamma.faces) { List<adjEntry> switches = sinkSwitches[f]; ListIterator<adjEntry> it = switches.begin(); for (it = it.succ(); it.valid(); ++it) { m_sinkSwitchOf[(*it)->theNode()] = (*it); } } }
// computes energy of layout, stores it and sets the crossingMatrix void Planarity::computeEnergy() { int e_num = m_nonSelfLoops.size(); int energySum = 0; Array<edge> numEdge(1,e_num); edge e; ListIterator<edge> it; for(it = m_nonSelfLoops.begin(); it.valid(); ++it) numEdge[(*m_edgeNums)[*it]] = *it; for(int i = 1; i < e_num; i++) { e = numEdge[i]; for(int j = i+1; j <= e_num ; j++) { bool cross = intersect(e,numEdge[j]); (*m_crossingMatrix)(i,j) = cross; if(cross) energySum += 1; } } m_energy = energySum; }
void NodePairEnergy::compCandEnergy() { node v = testNode(); int numv = (*m_nodeNums)[v]; m_candidateEnergy = energy(); ListIterator<node> it; for(it = m_nonIsolated.begin(); it.valid(); ++ it) { if(*it != v) { int j = (*m_nodeNums)[*it]; m_candidateEnergy -= (*m_pairEnergy)(min(j,numv),max(j,numv)); m_candPairEnergy[*it] = computeCoordEnergy(v,*it,testPos(),currentPos(*it)); m_candidateEnergy += m_candPairEnergy[*it]; if(m_candidateEnergy < 0.0) { OGDF_ASSERT(m_candidateEnergy > -0.00001); m_candidateEnergy = 0.0; } } else m_candPairEnergy[*it] = 0.0; } OGDF_ASSERT(m_candidateEnergy >= -0.0001); }
void MoveCluster::generateNeighbouringLayout(double temp, Hashing<ogdf::node, ogdf::DPoint> &result) { if(m_clusters.size() <= 1) throw "There are no clusters to move"; int r = randomNumber(0, m_clusters.size() - 1); Cluster c = *(m_clusters.get(r)); double randomAngle = randomDouble(0, 1) * 2.0 * Math::pi; double diffX = cos(randomAngle) * diskRadius(temp); double diffY = sin(randomAngle) * diskRadius(temp); ListIterator<node> it = c.nodes.begin(); for(; it.valid(); it++) { DPoint newPos; newPos.m_x = m_GA.x(*it) + diffX; newPos.m_y = m_GA.y(*it) + diffY; result.insert(*it, newPos); } }
void FeasibleUpwardPlanarSubgraph::dfs_visit( const Graph &G, edge e, NodeArray<bool> &visited, EdgeArray<bool> &treeEdges, bool random) { treeEdges[e] = true; List<edge> elist; G.outEdges(e->target(), elist); if (!elist.empty()) { if (random) elist.permute(); ListIterator<edge> it; for (it = elist.begin(); it.valid(); ++it) { edge ee = *it; if (!visited[ee->target()]) dfs_visit(G, ee, visited, treeEdges, random); } } visited[e->target()] = true; }
// compute grouping for sons of nodes on level i void RadialTreeLayout::ComputeGrouping(int i) { SListConstIterator<node> it; for(it = m_nodes[i].begin(); it.valid(); ++it) { node v = *it; node p = m_parent[v]; Grouping &grouping = m_grouping[v]; ListIterator<Group> currentGroup; adjEntry adj = v->firstAdj(); adjEntry adjStop; if(p != nullptr) { while(adj->twinNode() != p) adj = adj->cyclicSucc(); adjStop = adj; adj = adj->cyclicSucc(); } else { adjStop = adj; } do { node u = adj->twinNode(); if(!currentGroup.valid() || (*currentGroup).isSameType(u) == false) { currentGroup = grouping.pushBack(Group(this,u)); } else { (*currentGroup).append(u); } adj = adj->cyclicSucc(); } while(adj != adjStop); } }
// computes the energy if the node returned by testNode() is moved // to position testPos(). void Planarity::compCandEnergy() { node v = testNode(); m_candidateEnergy = energy(); edge e; m_crossingChanges.clear(); forall_adj_edges(e,v) if(!e->isSelfLoop()) { // first we compute the two endpoints of e if v is on its new position node s = e->source(); node t = e->target(); DPoint p1 = testPos(); DPoint p2 = (s==v)? currentPos(t) : currentPos(s); int e_num = (*m_edgeNums)[e]; edge f; // now we compute the crossings of all other edges with e ListIterator<edge> it; for(it = m_nonSelfLoops.begin(); it.valid(); ++it) if(*it != e) { f = *it; node s2 = f->source(); node t2 = f->target(); if(s2 != s && s2 != t && t2 != s && t2 != t) { bool cross = lowLevelIntersect(p1,p2,currentPos(s2),currentPos(t2)); int f_num = (*m_edgeNums)[f]; bool priorIntersect = (*m_crossingMatrix)(min(e_num,f_num),max(e_num,f_num)); if(priorIntersect != cross) { if(priorIntersect) m_candidateEnergy --; // this intersection was saved else m_candidateEnergy ++; // produced a new intersection ChangedCrossing cc; cc.edgeNum1 = min(e_num,f_num); cc.edgeNum2 = max(e_num,f_num); cc.cross = cross; m_crossingChanges.pushBack(cc); } } } } }
void ENGLayer::simplifyAdjacencies(List<LHTreeNode::Adjacency> &adjs) { AdjacencyComparer cmp; if(!adjs.empty()) { adjs.quicksort(cmp); ListIterator<LHTreeNode::Adjacency> it = adjs.begin(); ListIterator<LHTreeNode::Adjacency> itNext = it.succ(); while(itNext.valid()) { if((*it).m_u == (*itNext).m_u && (*it).m_v == (*itNext).m_v) { (*it).m_weight += (*itNext).m_weight; adjs.del(itNext); itNext = it.succ(); } else { it = itNext; ++itNext; } } } }
void TSANodePairEnergy::compCandEnergy() { m_candPairEnergy->fill(-1); m_candidateEnergy = energy(); HashConstIterator<node, DPoint> it; for(it = m_layoutChanges->begin(); it.valid(); ++it) { node v = it.key(); int numv = (*m_nodeNums)[v]; ListIterator<node> it; for(it = m_nonIsolated.begin(); it.valid(); ++ it) { int j = (*m_nodeNums)[*it]; if(numv != j && (numv < j || !m_layoutChanges->member(*it))) { m_candidateEnergy -= (*m_pairEnergy)(min(j,numv),max(j,numv)); double candEnergy = computeCoordEnergy(v,*it); m_candidateEnergy += candEnergy; (*m_candPairEnergy)(min(j,numv),max(j,numv)) = candEnergy; } } } }
void PlanarizationGridLayout::doCall( const Graph &G, GridLayout &gridLayout, IPoint &bb) { m_nCrossings = 0; if(G.empty()) return; PlanRep PG(G); const int numCC = PG.numberOfCCs(); // (width,height) of the layout of each connected component Array<IPoint> boundingBox(numCC); int i; for(i = 0; i < numCC; ++i) { PG.initCC(i); const int nOrigVerticesPG = PG.numberOfNodes(); List<edge> deletedEdges; m_subgraph.get().callAndDelete(PG, deletedEdges); m_inserter.get().call(PG,deletedEdges); m_nCrossings += PG.numberOfNodes() - nOrigVerticesPG; GridLayout gridLayoutPG(PG); m_planarLayouter.get().callGrid(PG,gridLayoutPG); // copy grid layout of PG into grid layout of G ListConstIterator<node> itV; for(itV = PG.nodesInCC(i).begin(); itV.valid(); ++itV) { node vG = *itV; gridLayout.x(vG) = gridLayoutPG.x(PG.copy(vG)); gridLayout.y(vG) = gridLayoutPG.y(PG.copy(vG)); adjEntry adj; forall_adj(adj,vG) { if ((adj->index() & 1) == 0) continue; edge eG = adj->theEdge(); IPolyline &ipl = gridLayout.bends(eG); ipl.clear(); bool firstTime = true; ListConstIterator<edge> itE; for(itE = PG.chain(eG).begin(); itE.valid(); ++itE) { if(!firstTime) { node v = (*itE)->source(); ipl.pushBack(IPoint(gridLayoutPG.x(v),gridLayoutPG.y(v))); } else firstTime = false; ipl.conc(gridLayoutPG.bends(*itE)); } } } boundingBox[i] = m_planarLayouter.get().gridBoundingBox(); boundingBox[i].m_x += 1; // one row/column space between components boundingBox[i].m_y += 1; } Array<IPoint> offset(numCC); m_packer.get().call(boundingBox,offset,m_pageRatio); bb.m_x = bb.m_y = 0; for(i = 0; i < numCC; ++i) { const List<node> &nodes = PG.nodesInCC(i); const int dx = offset[i].m_x; const int dy = offset[i].m_y; if(boundingBox[i].m_x + dx > bb.m_x) bb.m_x = boundingBox[i].m_x + dx; if(boundingBox[i].m_y + dy > bb.m_y) bb.m_y = boundingBox[i].m_y + dy; // iterate over all nodes in i-th cc ListConstIterator<node> it; for(it = nodes.begin(); it.valid(); ++it) { node vG = *it; gridLayout.x(vG) += dx; gridLayout.y(vG) += dy; adjEntry adj; forall_adj(adj,vG) { if ((adj->index() & 1) == 0) continue; edge eG = adj->theEdge(); ListIterator<IPoint> it; for(it = gridLayout.bends(eG).begin(); it.valid(); ++it) { (*it).m_x += dx; (*it).m_y += dy; } } } } bb.m_x -= 1; // remove margin of topmost/rightmost box bb.m_y -= 1; }
void PlanarizationGridLayout::doCall( const Graph &G, GridLayout &gridLayout, IPoint &bb) { m_nCrossings = 0; if(G.empty()) return; PlanRep pr(G); const int numCC = pr.numberOfCCs(); // (width,height) of the layout of each connected component Array<IPoint> boundingBox(numCC); for(int cc = 0; cc < numCC; ++cc) { //-------------------------------------- // 1. crossing minimization //-------------------------------------- int cr; m_crossMin.get().call(pr, cc, cr); m_nCrossings += cr; OGDF_ASSERT(isPlanar(pr)); GridLayout gridLayoutPG(pr); m_planarLayouter.get().callGrid(pr,gridLayoutPG); // copy grid layout of PG into grid layout of G for(int j = pr.startNode(); j < pr.stopNode(); ++j) { node vG = pr.v(j); gridLayout.x(vG) = gridLayoutPG.x(pr.copy(vG)); gridLayout.y(vG) = gridLayoutPG.y(pr.copy(vG)); adjEntry adj; forall_adj(adj,vG) { if ((adj->index() & 1) == 0) continue; edge eG = adj->theEdge(); IPolyline &ipl = gridLayout.bends(eG); ipl.clear(); bool firstTime = true; ListConstIterator<edge> itE; for(itE = pr.chain(eG).begin(); itE.valid(); ++itE) { if(!firstTime) { node v = (*itE)->source(); ipl.pushBack(IPoint(gridLayoutPG.x(v),gridLayoutPG.y(v))); } else firstTime = false; ipl.conc(gridLayoutPG.bends(*itE)); } } } boundingBox[cc] = m_planarLayouter.get().gridBoundingBox(); boundingBox[cc].m_x += 1; // one row/column space between components boundingBox[cc].m_y += 1; } Array<IPoint> offset(numCC); m_packer.get().call(boundingBox,offset,m_pageRatio); bb.m_x = bb.m_y = 0; for(int cc = 0; cc < numCC; ++cc) { const int dx = offset[cc].m_x; const int dy = offset[cc].m_y; if(boundingBox[cc].m_x + dx > bb.m_x) bb.m_x = boundingBox[cc].m_x + dx; if(boundingBox[cc].m_y + dy > bb.m_y) bb.m_y = boundingBox[cc].m_y + dy; // iterate over all nodes in i-th cc for(int j = pr.startNode(cc); j < pr.stopNode(cc); ++j) { node vG = pr.v(j); gridLayout.x(vG) += dx; gridLayout.y(vG) += dy; adjEntry adj; forall_adj(adj,vG) { if ((adj->index() & 1) == 0) continue; edge eG = adj->theEdge(); ListIterator<IPoint> it; for(it = gridLayout.bends(eG).begin(); it.valid(); ++it) { (*it).m_x += dx; (*it).m_y += dy; } } } } bb.m_x -= 1; // remove margin of topmost/rightmost box bb.m_y -= 1; }