Example #1
0
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>();
	}
}
Example #2
0
// 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	
		}
	}
Example #3
0
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
Example #4
0
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
Example #5
0
	// 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;
		}
	}
Example #6
0
	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;
	}
Example #7
0
	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;
	}
Example #8
0
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] ) );
	}
}
Example #9
0
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);
	}
}
Example #10
0
//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();
}
Example #12
0
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;
		}
	}
}
Example #14
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
Example #15
0
// 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
	}
Example #17
0
// 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;
}
Example #20
0
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);
		}
	}
}
Example #21
0
	// 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;
}
Example #25
0
// 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);
	}
}
Example #26
0
	// 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);
					}
				}
			}
		}
	}
Example #27
0
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;
	}