void SubgraphUpwardPlanarizer::constructComponentGraphs(BCTree &BC, NodeArray<GraphCopy> &biComps) { NodeArray<int> constructed(BC.originalGraph(), -1); const Graph &bcTree = BC.bcTree(); int i = 0; // comp. number for(node v : bcTree.nodes) { if (BC.typeOfBNode(v) == BCTree::CComp) continue; const SList<edge> &edges_comp = BC.hEdges(v); //bicomp edges List<edge> edges_orig; for(edge e : edges_comp) edges_orig.pushBack(BC.original(e)); GraphCopy GC; GC.createEmpty(BC.originalGraph()); // construct i-th component graph for(edge eOrig : edges_orig) { node srcOrig = eOrig->source(); node tgtOrig = eOrig->target(); if (constructed[srcOrig] != i) { constructed[srcOrig] = i; GC.newNode(srcOrig); } if (constructed[tgtOrig] != i) { constructed[tgtOrig] = i; GC.newNode(tgtOrig); } GC.newEdge(eOrig); } biComps[v] = GC; i++; } }
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); } }
//use the layout information in the umlgraph to find nodes in //unconnected active parts of a CC that can be connected without //crossings in the given embedding void PlanRepInc::getExtAdjs(List<adjEntry> & /* extAdjs */) { //in order not to change the current CC initialization, //we construct a copy of the active parts (one by one) //and use the layout information to compute a external //face for that part. An (original) adjEntry on this face //is then inserted into the extAdjs list. //derive the unconnected parts by a run through the current //copy //compute connected component of current CC NodeArray<int> component(*this); int numPartialCC = connectedComponents(*this, component); EdgeArray<edge> copyEdge;//copy edges in partial CC copy //now we compute a copy for every CC //initialize an array of lists of nodes contained in a CC Array<List<node> > nodesInPartialCC; nodesInPartialCC.init(numPartialCC); for(node v : nodes) nodesInPartialCC[component[v]].pushBack(v); int i = 0; for (i = 0; i < numPartialCC; i++) { List<node> &theNodes = nodesInPartialCC[i]; GraphCopy GC; GC.createEmpty(*this); GC.initByNodes(theNodes, copyEdge); //now we derive an outer face of GC by using the //layout information on it's original //TODO: Insert the bend points into the copy //CombinatorialEmbedding E(GC); //run through the faces and compute angles to //derive outer face //we dont care about the original structure of //the graph, i.e., if crossings are inserted aso //we only take the given partial CC and its layout //adjEntry extAdj = getExtAdj(GC, E); //for(node v : GC.nodes) //{ // //} }//for }//getextadj
void OptimalRanking::doCall( const Graph& G, NodeArray<int> &rank, EdgeArray<bool> &reversed, const EdgeArray<int> &length, const EdgeArray<int> &costOrig) { MinCostFlowReinelt<int> mcf; // construct min-cost flow problem GraphCopy GC; GC.createEmpty(G); // compute connected component of G NodeArray<int> component(G); int numCC = connectedComponents(G,component); // intialize the array of lists of nodes contained in a CC Array<List<node> > nodesInCC(numCC); for(node v : G.nodes) nodesInCC[component[v]].pushBack(v); EdgeArray<edge> auxCopy(G); rank.init(G); for(int i = 0; i < numCC; ++i) { GC.initByNodes(nodesInCC[i], auxCopy); makeLoopFree(GC); for(edge e : GC.edges) if(reversed[GC.original(e)]) GC.reverseEdge(e); // special cases: if(GC.numberOfNodes() == 1) { rank[GC.original(GC.firstNode())] = 0; continue; } else if(GC.numberOfEdges() == 1) { edge e = GC.original(GC.firstEdge()); rank[e->source()] = 0; rank[e->target()] = length[e]; continue; } EdgeArray<int> lowerBound(GC,0); EdgeArray<int> upperBound(GC,mcf.infinity()); EdgeArray<int> cost(GC); NodeArray<int> supply(GC); for(edge e : GC.edges) cost[e] = -length[GC.original(e)]; for(node v : GC.nodes) { int s = 0; edge e; forall_adj_edges(e,v) { if(v == e->source()) s += costOrig[GC.original(e)]; else s -= costOrig[GC.original(e)]; } supply[v] = s; } OGDF_ASSERT(isAcyclic(GC) == true); // find min-cost flow EdgeArray<int> flow(GC); NodeArray<int> dual(GC); #ifdef OGDF_DEBUG bool feasible = #endif mcf.call(GC, lowerBound, upperBound, cost, supply, flow, dual); OGDF_ASSERT(feasible); for(node v : GC.nodes) rank[GC.original(v)] = dual[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 }
void SpringEmbedderFR::call(GraphAttributes &AG) { const Graph &G = AG.constGraph(); if(G.empty()) return; // all edges straight-line AG.clearAllBends(); GraphCopy GC; GC.createEmpty(G); // compute connected component of G NodeArray<int> component(G); int numCC = connectedComponents(G,component); // intialize the array of lists of nodes contained in a CC Array<List<node> > nodesInCC(numCC); node v; forall_nodes(v,G) nodesInCC[component[v]].pushBack(v); EdgeArray<edge> auxCopy(G); Array<DPoint> boundingBox(numCC); int i; for(i = 0; i < numCC; ++i) { GC.initByNodes(nodesInCC[i],auxCopy); GraphCopyAttributes AGC(GC,AG); node vCopy; forall_nodes(vCopy, GC) { node vOrig = GC.original(vCopy); AGC.x(vCopy) = AG.x(vOrig); AGC.y(vCopy) = AG.y(vOrig); } // original if (initialize(GC, AGC) == true) { for(int i = 1; i <= m_iterations; i++) mainStep(GC, AGC); } cleanup(); // end original node vFirst = GC.firstNode(); double minX = AGC.x(vFirst), maxX = AGC.x(vFirst), minY = AGC.y(vFirst), maxY = AGC.y(vFirst); forall_nodes(vCopy,GC) { node v = GC.original(vCopy); AG.x(v) = AGC.x(vCopy); AG.y(v) = AGC.y(vCopy); if(AG.x(v)-AG.width (v)/2 < minX) minX = AG.x(v)-AG.width(v) /2; if(AG.x(v)+AG.width (v)/2 > maxX) maxX = AG.x(v)+AG.width(v) /2; if(AG.y(v)-AG.height(v)/2 < minY) minY = AG.y(v)-AG.height(v)/2; if(AG.y(v)+AG.height(v)/2 > maxY) maxY = AG.y(v)+AG.height(v)/2; }
//outputs the set of feasible solutions void ClusterPlanarity::writeFeasible(const char *filename, CP_MasterBase &master, Master::STATUS &status) { const ClusterGraph& CG = *(master.getClusterGraph()); const Graph& G = CG.constGraph(); //first compute the nodepairs that are potential candidates to connect //chunks in a cluster //potential connection edges NodeArray< NodeArray<bool> > potConn(G); for(node v : G.nodes) { potConn[v].init(G, false); } //we perform a bottom up cluster tree traversal List< cluster > clist; getBottomUpClusterList(CG.rootCluster(), clist); //could use postordertraversal instead List< nodePair > connPairs; //holds all connection node pairs //counts the number of potential connectivity edges //int potCount = 0; //equal to number of true values in potConn //we run through the clusters and check connected components //we consider all possible edges connecting CCs in a cluster, //even if they may be connected by edges in a child cluster //(to get the set of all feasible solutions) for(cluster c : clist) { //we compute the subgraph induced by vertices in c GraphCopy gcopy; gcopy.createEmpty(G); List<node> clusterNodes; //would be more efficient if we would just merge the childrens' vertices //and add c's c->getClusterNodes(clusterNodes); NodeArray<bool> activeNodes(G, false); //true for all cluster nodes EdgeArray<edge> copyEdge(G); //holds the edge copy for(node v : clusterNodes) activeNodes[v] = true; gcopy.initByActiveNodes(clusterNodes, activeNodes, copyEdge); //gcopy now represents the cluster induced subgraph //we compute the connected components and store all nodepairs //that connect two of them NodeArray<int> component(gcopy); connectedComponents(gcopy, component); //now we run over all vertices and compare the component //number of adjacent vertices. If they differ, we found a //potential connection edge. We do not care if we find them twice. for(node v : gcopy.nodes) { for(node w : gcopy.nodes) { if (component[v] != component[w]) { cout <<"Indizes: "<<v->index()<<":"<<w->index()<<"\n"; node vg = gcopy.original(v); node wg = gcopy.original(w); bool newConn = !((vg->index() < wg->index()) ? potConn[vg][wg] : potConn[wg][vg]); if (newConn) { nodePair np; np.v1 = vg; np.v2 = wg; connPairs.pushBack(np); if (vg->index() < wg->index()) potConn[vg][wg] = true; else potConn[wg][vg] = true; } } }//nodes }//nodes } cout << "Number of potential connection edges: "<< connPairs.size()<<"\n"; //we run through our candidates and save them in an array //that can be used for dynamic graph updates int i = 0; connStruct *cons = new connStruct[connPairs.size()]; for(const nodePair &np : connPairs) { connStruct cs; cs.connected = false; cs.v1 = np.v1; cs.v2 = np.v2; cs.e = nullptr; cons[i] = cs; i++; } //------------------------------------------------------------------------- // WARNING: this is extremely slow for graphs with a large number of cluster // chunks now we test all possible connection edge combinations for c-planarity Graph G2; NodeArray<node> origNodes(CG.constGraph()); ClusterArray<cluster> origCluster(CG); EdgeArray<edge> origEdges(CG.constGraph()); ClusterGraph testCopy(CG, G2, origCluster, origNodes, origEdges); ofstream os(filename); // Output dimension of the LP (number of variables) os << "DIM = " << connPairs.size() << "\n"; os << "COMMENT\n"; switch (status) { case Master::Optimal: os << "Optimal \n\n"; break; case Master::Error: os << "Error \n\n"; break; default: os << "unknown \n\n"; } for (i = 0; i < connPairs.size(); i++) { os << "Var " << i << ": " << origNodes[cons[i].v1]->index() << "->" << origNodes[cons[i].v2] << "\n"; } os << "CONV_SECTION\n"; #ifdef writefeasiblegraphs int j = 0; //debug #endif if (connPairs.size() > 0) while (true) { //we create the next test configuration by incrementing the edge selection array //we create the corresponding graph dynamically on the fly i = 0; while ( (i < connPairs.size()) && (cons[i].connected == true) ) { cons[i].connected = false; OGDF_ASSERT(cons[i].e != 0); G2.delEdge(cons[i].e); i++; }//while if (i >= connPairs.size()) break; //cout<<"v1graph: "<<&(*(cons[i].v1->graphOf()))<<"\n"; //cout<<"origNodesgraph: "<<&(*(origNodes.graphOf()))<<"\n"; cons[i].connected = true; //i.e., (false) will never be a feasible solution cons[i].e = G2.newEdge(origNodes[cons[i].v1], origNodes[cons[i].v2]); //and test it for c-planarity CconnectClusterPlanar CCCP; bool cplanar = CCCP.call(testCopy); //c-planar graphs define a feasible solution if (cplanar) { #ifdef OGDF_DEBUG cout << "Feasible solution found\n"; #endif for (int j = 0; j < connPairs.size(); j++) { char ch = (cons[j].connected ? '1' : '0'); cout << ch; os << ch << " "; } cout << "\n"; os << "\n"; #ifdef writefeasiblegraphs string fn = "cGraph"; fn += to_string(j++) + ".gml"; GraphIO::writeGML(testCopy, fn); #endif } }//while counting delete[] cons; os << "\nEND" <<"\n"; os.close(); //return; os.open(getIeqFileName()); os << "DIM = " << m_numVars << "\n"; // Output the status as a comment os << "COMMENT\n"; switch (status) { case Master::Optimal: os << "Optimal \n\n"; break; case Master::Error: os << "Error \n\n"; break; default: os << "unknown \n\n"; } // In case 0 is not a valid solution, some PORTA functions need //a valid solution in the ieq file os << "VALID\n"; os << "\nLOWER_BOUNDS\n"; for (i = 0; i < m_numVars; i++) os << "0 "; os << "\n"; os << "\nHIGHER_BOUNDS\n"; for (i = 0; i < m_numVars; i++) os << "1 "; os << "\n"; os << "\nINEQUALITIES_SECTION\n"; //we first read the standard constraint that are written //into a text file by the optimization master ifstream isf(master.getStdConstraintsFileName()); if (!isf) { cerr << "Could not open optimization master's standard constraint file\n"; os << "#No standard constraints read\n"; } else { char* fileLine = new char[maxConLength()]; while (isf.getline(fileLine, maxConLength())) { //skip commment lines if (fileLine[0] == '#') continue; int count = 1; std::istringstream iss(fileLine); char d; bool rhs = false; while (iss >> d) { if ( rhs || ( (d == '<') || (d == '>') || (d == '=') ) ) { os << d; rhs = true; } else { if (d != '0') { os <<"+"<< d <<"x"<<count; } count++; } }//while chars os <<"\n"; } delete[] fileLine; }//ifstream //now we read the cut pools from the master if (master.useDefaultCutPool()) { os << "#No cut constraints read from master\n"; //StandardPool<Constraint, Variable> *connCon = master.cutPool(); } else { StandardPool<Constraint, Variable> *connCon = master.getCutConnPool(); StandardPool<Constraint, Variable> *kuraCon = master.getCutKuraPool(); StandardPool<Variable, Constraint> *stdVar = master.varPool(); OGDF_ASSERT(connCon != 0); OGDF_ASSERT(kuraCon != 0); cout << connCon->number() << " Constraints im MasterConnpool \n"; cout << kuraCon->number() << " Constraints im MasterKurapool \n"; cout << connCon->size() << " Größe ConnPool"<<"\n"; outputCons(os, connCon, stdVar); outputCons(os, kuraCon, stdVar); }//else os << "\nEND" <<"\n"; os.close(); cout << "Cutting is set: "<<master.cutting()<<"\n"; //cout <<"Bounds for the variables:\n"; //Sub &theSub = *(master.firstSub()); //for ( i = 0; i < theSub.nVar(); i++) //{ // cout << i << ": " << theSub.lBound(i) << " - " << theSub.uBound(i) << "\n"; //} /*// OLD CRAP cout << "Constraints: \n"; StandardPool< Constraint, Variable > *spool = master.conPool(); StandardPool< Constraint, Variable > *cpool = master.cutPool(); cout << spool->size() << " Constraints im Masterpool \n"; cout << cpool->size() << " Constraints im Mastercutpool \n"; cout << "ConPool Constraints \n"; for ( i = 0; i < spool->size(); i++) { PoolSlot< Constraint, Variable > * sloty = spool->slot(i); Constraint *mycon = sloty->conVar(); switch (mycon->sense()->sense()) { case CSense::Less: cout << "<" << "\n"; break; case CSense::Greater: cout << ">" << "\n"; break; case CSense::Equal: cout << "=" << "\n"; break; default: cout << "Inequality sense doesn't make any sense \n"; break; }//switch } cout << "CutPool Constraints \n"; for ( i = 0; i < cpool->size(); i++) { PoolSlot< Constraint, Variable > * sloty = cpool->slot(i); Constraint *mycon = sloty->conVar(); switch (mycon->sense()->sense()) { case CSense::Less: cout << "<" << "\n"; break; case CSense::Greater: cout << ">" << "\n"; break; case CSense::Equal: cout << "=" << "\n"; break; default: cout << "Inequality sense doesn't make any sense \n"; break; }//switch } */ /* for ( i = 0; i < theSub.nCon(); i++) { Constraint &theCon = *(theSub.constraint(i)); for ( i = 0; i < theSub.nVar(); i++) { double c = theCon.coeff(theSub.variable(i)); if (c != 0) cout << c; else cout << " "; } switch (theCon.sense()->sense()) { case CSense::Less: cout << "<" << "\n"; break; case CSense::Greater: cout << ">" << "\n"; break; case CSense::Equal: cout << "=" << "\n"; break; default: cout << "doesn't make any sense \n"; break; }//switch float fl; while(!(std::cin >> fl)) { std::cin.clear(); std::cin.ignore(numeric_limits<streamsize>::max(),'\n'); } }*/ }//writeportaieq
void GEMLayout::call(GraphAttributes &AG) { const Graph &G = AG.constGraph(); if(G.empty()) return; OGDF_ASSERT(m_numberOfRounds >= 0); OGDF_ASSERT(DIsGreaterEqual(m_minimalTemperature,0)); OGDF_ASSERT(DIsGreaterEqual(m_initialTemperature,m_minimalTemperature)); OGDF_ASSERT(DIsGreaterEqual(m_gravitationalConstant,0)); OGDF_ASSERT(DIsGreaterEqual(m_desiredLength,0)); OGDF_ASSERT(DIsGreaterEqual(m_maximalDisturbance,0)); OGDF_ASSERT(DIsGreaterEqual(m_rotationAngle,0)); OGDF_ASSERT(DIsLessEqual(m_rotationAngle,pi / 2)); OGDF_ASSERT(DIsGreaterEqual(m_oscillationAngle,0)); OGDF_ASSERT(DIsLessEqual(m_oscillationAngle,pi / 2)); OGDF_ASSERT(DIsGreaterEqual(m_rotationSensitivity,0)); OGDF_ASSERT(DIsLessEqual(m_rotationSensitivity,1)); OGDF_ASSERT(DIsGreaterEqual(m_oscillationSensitivity,0)); OGDF_ASSERT(DIsLessEqual(m_oscillationSensitivity,1)); OGDF_ASSERT(m_attractionFormula == 1 || m_attractionFormula == 2); // all edges straight-line AG.clearAllBends(); GraphCopy GC; GC.createEmpty(G); // compute connected component of G NodeArray<int> component(G); int numCC = connectedComponents(G,component); // intialize the array of lists of nodes contained in a CC Array<List<node> > nodesInCC(numCC); node v; forall_nodes(v,G) nodesInCC[component[v]].pushBack(v); EdgeArray<edge> auxCopy(G); Array<DPoint> boundingBox(numCC); int i; for(i = 0; i < numCC; ++i) { GC.initByNodes(nodesInCC[i],auxCopy); GraphCopyAttributes AGC(GC,AG); node vCopy; forall_nodes(vCopy, GC) { node vOrig = GC.original(vCopy); AGC.x(vCopy) = AG.x(vOrig); AGC.y(vCopy) = AG.y(vOrig); } SList<node> permutation; node v; // initialize node data m_impulseX.init(GC,0); m_impulseY.init(GC,0); m_skewGauge.init(GC,0); m_localTemperature.init(GC,m_initialTemperature); // initialize other data m_globalTemperature = m_initialTemperature; m_barycenterX = 0; m_barycenterY = 0; forall_nodes(v,GC) { m_barycenterX += weight(v) * AGC.x(v); m_barycenterY += weight(v) * AGC.y(v); }
void GEMLayout::call(GraphAttributes &AG) { const Graph &G = AG.constGraph(); if(G.empty()) return; // all edges straight-line AG.clearAllBends(); GraphCopy GC; GC.createEmpty(G); // compute connected component of G NodeArray<int> component(G); int numCC = connectedComponents(G,component); // intialize the array of lists of nodes contained in a CC Array<List<node> > nodesInCC(numCC); for(node v : G.nodes) nodesInCC[component[v]].pushBack(v); EdgeArray<edge> auxCopy(G); Array<DPoint> boundingBox(numCC); int i; for(i = 0; i < numCC; ++i) { GC.initByNodes(nodesInCC[i],auxCopy); GraphCopyAttributes AGC(GC,AG); for(node vCopy : GC.nodes) { node vOrig = GC.original(vCopy); AGC.x(vCopy) = AG.x(vOrig); AGC.y(vCopy) = AG.y(vOrig); } SList<node> permutation; // initialize node data m_impulseX.init(GC,0); m_impulseY.init(GC,0); m_skewGauge.init(GC,0); m_localTemperature.init(GC,m_initialTemperature); // initialize other data m_globalTemperature = m_initialTemperature; m_barycenterX = 0; m_barycenterY = 0; for(node v : GC.nodes) { m_barycenterX += weight(v) * AGC.x(v); m_barycenterY += weight(v) * AGC.y(v); } m_cos = cos(m_oscillationAngle / 2.0); m_sin = sin(Math::pi / 2 + m_rotationAngle / 2.0); // main loop int counter = m_numberOfRounds; while(OGDF_GEOM_ET.greater(m_globalTemperature,m_minimalTemperature) && counter--) { // choose nodes by random permutations if(permutation.empty()) { for(node v : GC.nodes) permutation.pushBack(v); permutation.permute(m_rng); } node v = permutation.popFrontRet(); // compute the impulse of node v computeImpulse(GC,AGC,v); // update node v updateNode(GC,AGC,v); } node vFirst = GC.firstNode(); double minX = AGC.x(vFirst), maxX = AGC.x(vFirst), minY = AGC.y(vFirst), maxY = AGC.y(vFirst); for(node vCopy : GC.nodes) { node v = GC.original(vCopy); AG.x(v) = AGC.x(vCopy); AG.y(v) = AGC.y(vCopy); if(AG.x(v)-AG.width (v)/2 < minX) minX = AG.x(v)-AG.width(v) /2; if(AG.x(v)+AG.width (v)/2 > maxX) maxX = AG.x(v)+AG.width(v) /2; if(AG.y(v)-AG.height(v)/2 < minY) minY = AG.y(v)-AG.height(v)/2; if(AG.y(v)+AG.height(v)/2 > maxY) maxY = AG.y(v)+AG.height(v)/2; } minX -= m_minDistCC; minY -= m_minDistCC; for(node vCopy : GC.nodes) { node v = GC.original(vCopy); AG.x(v) -= minX; AG.y(v) -= minY; } boundingBox[i] = DPoint(maxX - minX, maxY - minY); } Array<DPoint> offset(numCC); TileToRowsCCPacker packer; packer.call(boundingBox,offset,m_pageRatio); // The arrangement is given by offset to the origin of the coordinate // system. We still have to shift each node and edge by the offset // of its connected component. for(i = 0; i < numCC; ++i) { const List<node> &nodes = nodesInCC[i]; const double dx = offset[i].m_x; const double dy = offset[i].m_y; // iterate over all nodes in ith CC ListConstIterator<node> it; for(node v : nodes) { AG.x(v) += dx; AG.y(v) += dy; } } // free node data m_impulseX.init(); m_impulseY.init(); m_skewGauge.init(); m_localTemperature.init(); }