Beispiel #1
0
//read all cluster tree information
bool GmlParser::clusterRead(GmlObject* rootCluster, 
							ClusterGraph& CG)
{

	//the root cluster is only allowed to hold child clusters and
	//nodes in a list

	if (rootCluster->m_valueType != gmlListBegin) return false;

	// read all clusters and nodes
	GmlObject *rootClusterSon = rootCluster->m_pFirstSon;

	for(; rootClusterSon; rootClusterSon = rootClusterSon->m_pBrother) 
	{
		switch(id(rootClusterSon)) 
		{
			case clusterPredefKey:
				{
					//we could delete this, but we aviod the call
					if (rootClusterSon->m_valueType != gmlListBegin) return false;
					// set attributes to default values
					//we currently do not set any values
					cluster c = CG.newCluster(CG.rootCluster());

					//recursively read cluster
					recursiveClusterRead(rootClusterSon, CG, c);

				} //case cluster
				break;
			case vertexPredefKey: //direct root vertices 
				{
					if (rootClusterSon->m_valueType != gmlStringValue) return false;
					String vIDString = rootClusterSon->m_stringValue;

					//we only allow a vertex id as string identification
					if ((vIDString[0] != 'v') &&
						(!isdigit(vIDString[0])))return false; //do not allow labels
					//if old style entry "v"i
					if (!isdigit(vIDString[0])) //should check prefix?
						vIDString[0] = '0'; //leading zero to allow conversion
					int vID = atoi(vIDString.cstr());

					OGDF_ASSERT(m_mapToNode[vID] != 0)

					//we assume that no node is already assigned ! Changed:
					//all new nodes are assigned to root
					//CG.reassignNode(mapToNode[vID], CG.rootCluster());
					//it seems that this may be unnessecary, TODO check
					CG.reassignNode(m_mapToNode[vID], CG.rootCluster());
					//char* vIDChar = new char[vIDString.length()+1];
					//for (int ind = 1; ind < vIDString.length(); ind++)
					//	vIDChar

				}//case vertex
		}//switch
	}//for all rootcluster sons

	return true;
				
}//clusterread
Beispiel #2
0
ClusterGraphCopy::ClusterGraphCopy(const ExtendedNestingGraph &H, const ClusterGraph &CG)
: ClusterGraph(H), m_pCG(&CG), m_pH(&H), m_copy(CG,0)
{
	m_original.init(*this,0);
	m_copy    [CG.rootCluster()] = rootCluster();
	m_original[rootCluster()]    = CG.rootCluster();

	createClusterTree(CG.rootCluster());
}
Beispiel #3
0
void ClusterGraphCopy::init(const ExtendedNestingGraph &H, const ClusterGraph &CG)
{
	ClusterGraph::init(H);
	m_pCG = &CG;
	m_pH  = &H;
	m_copy    .init(CG,0);
	m_original.init(*this,0);

	m_copy    [CG.rootCluster()] = rootCluster();
	m_original[rootCluster()]    = CG.rootCluster();

	createClusterTree(CG.rootCluster());
}
// Copy Function
void ClusterGraph::shallowCopy(const ClusterGraph &C)
{
	const Graph &G = C;
	m_pGraph = &G;

	m_nClusters = 0;

	//m_clusterDepth.init(*this, 0);

	initGraph(G);
	
	m_updateDepth = C.m_updateDepth;
	m_depthUpToDate = C.m_depthUpToDate;

	// Construct cluster tree
	ClusterArray<cluster> originalClusterTable(C);
	cluster c = 0;
	forall_clusters(c,C)
	{
		if (c == C.m_rootCluster)
		{
			originalClusterTable[c] = m_rootCluster;
			//does not really need to be assigned HERE in for
			m_rootCluster->depth() = 1;
			OGDF_ASSERT(C.rootCluster()->depth() == 1)
			continue;
		}
		originalClusterTable[c] = newCluster();
		originalClusterTable[c]->depth() = c->depth();
	}
bool CconnectClusterPlanar::preProcess(ClusterGraph &C,Graph &G)
{
	if (!isCConnected(C))
	{
		m_errorCode = nonCConnected;
		return false;
	}

	if (!isPlanar(C))
	{
		m_errorCode = nonPlanar;
		return false;
	}

	cluster c;

	SListPure<node> selfLoops;
	makeLoopFree(G,selfLoops);

	c = C.rootCluster();

	bool cPlanar = planarityTest(C,c,G);

	return cPlanar;
}
Beispiel #6
0
bool CconnectClusterPlanar::preProcess(ClusterGraph &C,Graph &G)
{
	if (!isCConnected(C))
	{
		ogdf::sprintf(errorCode,124,"Graph is not C-connected \n"); 
		m_errorCode = nonCConnected;
		return false;
	}

	PlanarModule Pm;
	if (!Pm.planarityTest(C))
	{
		ogdf::sprintf(errorCode,124,"Graph is not planar\n"); 
		m_errorCode = nonPlanar;
		return false;
	}

	cluster c;

	SListPure<node> selfLoops;
	makeLoopFree(G,selfLoops);

	c = C.rootCluster();

	bool cPlanar = planarityTest(C,c,G);

	return cPlanar;
}
Beispiel #7
0
bool GraphIO::writeGEXF(const ClusterGraph &C, std::ostream &out)
{
	gexf::writeHeader(out);
	gexf::writeCluster(out, 1, C, nullptr, C.rootCluster());
	gexf::writeFooter(out);

	return true;
}
Beispiel #8
0
// write cluster graph structure with clusters
static void write_ogml_graph(const ClusterGraph &C, ostream &os)
{
	GraphIO::indent(os,2) << "<structure>\n";

	write_ogml_graph(C.rootCluster(), 0, os);
	write_ogml_graph_edges(C.constGraph(), os);

	GraphIO::indent(os,2) << "</structure>\n";
}
Beispiel #9
0
static void writeCluster(
	std::ostream &out, int depth,
	const ClusterArray < std::vector<edge> > &edgeMap,
	const ClusterGraph &C, const ClusterGraphAttributes *CA, const cluster &c,
	int &clusterId)
{
	if(C.rootCluster() == c) {
		writeHeader(out, depth++, CA);
	} else {
		GraphIO::indent(out, depth++) << "subgraph cluster" << clusterId
		                              << " {\n";
	}
	clusterId++;

	bool whitespace; // True if a whitespace should printed (readability).

	whitespace = false;
	if(CA) {
		writeAttributes(out, depth, *CA, c);
		whitespace = true;
	}

	if(whitespace) {
		out << "\n";
	}

	// Recursively export all subclusters.
	whitespace = false;
	for(ListConstIterator<cluster> cit = c->cBegin(); cit.valid(); ++cit) {
		writeCluster(out, depth, edgeMap, C, CA, *cit, clusterId);
		whitespace = true;
	}

	if(whitespace) {
		out << "\n";
	}

	// Then, print all nodes whithout an adjacent edge.
	whitespace = false;
	for(ListConstIterator<node> nit = c->nBegin(); nit.valid(); ++nit) {
		whitespace |= writeNode(out, depth, CA, *nit);
	}

	if(whitespace) {
		out << "\n";
	}

	// Finally, we print all edges for this cluster (ugly version for now).
	const std::vector<edge> &edges = edgeMap[c];
	whitespace = false;
	for(size_t i = 0; i < edges.size(); i++) {
		whitespace |= writeEdge(out, depth, CA, edges[i]);
	}

	GraphIO::indent(out, --depth) << "}\n";
}
Beispiel #10
0
bool GraphMLParser::read(Graph &G, ClusterGraph &C, ClusterGraphAttributes &CA)
{
	if(m_error) {
		return false;
	}

	G.clear();
	m_nodeId.clear();

	return readClusters(G, C, &CA, C.rootCluster(), m_graphTag);
}
Beispiel #11
0
bool GraphMLParser::read(Graph &G, ClusterGraph &C)
{
	if(m_error) {
		return false;
	}

	G.clear();
	m_nodeId.clear();

	return readClusters(G, C, nullptr, C.rootCluster(), m_graphTag);
}
Beispiel #12
0
static void writeCluster(
	std::ostream &out, int depth,
	const ClusterGraph &C, const ClusterGraphAttributes *CA, cluster c)
{
	if(C.rootCluster() != c) {
		GraphIO::indent(out, depth) << "<node "
		                            << "id=\"cluster" << c->index() << "\""
		                            << ">\n";
	} else {
		const std::string dir =
			(CA && !CA->directed()) ? "undirected" : "directed";
		GraphIO::indent(out, depth) << "<graph "
		                            << "mode=\"static\""
		                            << "defaultedgetype=\"" << dir << "\""
		                            << ">\n";

		if(CA) {
			defineAttributes(out, depth + 1, *CA);
		}
	}

	GraphIO::indent(out, depth + 1) << "<nodes>\n";

	for(ListConstIterator<cluster> cit = c->cBegin(); cit.valid(); ++cit) {
		writeCluster(out, depth + 2, C, CA, *cit);
	}

	for(ListConstIterator<node> nit = c->nBegin(); nit.valid(); ++nit) {
		writeNode(out, depth + 2, CA, *nit);
	}

	GraphIO::indent(out, depth + 1) << "</nodes>\n";

	if(C.rootCluster() != c) {
		GraphIO::indent(out, depth) << "</node>\n";
	} else {
		writeEdges(out, C.constGraph(), CA);
		GraphIO::indent(out, depth) << "</graph>\n";
	}
}
Beispiel #13
0
bool GraphIO::writeDOT(const ClusterGraph &C, std::ostream &out)
{
	const Graph &G = C.constGraph();
	int id = 1;

	// Assign a list of edges for each cluster. Perhaps usage of std::vector
	// here needs reconsideration - vector is fast but usage of STL iterators
	// is ugly without C++11 for-each loop.
	ClusterArray< std::vector<edge> > edgeMap(C);
	for(edge e : G.edges) {
		const node s = e->source(), t = e->target();
		edgeMap[C.commonCluster(s, t)].push_back(e);
	}

	return dot::writeCluster(out, 0, edgeMap, C, nullptr, C.rootCluster(), id);
}
//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;
}
Beispiel #15
0
bool ClusterPlanarity::isClusterPlanar(const ClusterGraph &CG, List<nodePair> &addedEdges) {
	m_optStatus = Master::Optimal;
	// We first check if there is more to do then just checking planarity on the
	// input graph.
	// Simple shortcut: With < 5 vertices, no non-planarity is possible...
	bool result = isPlanar(CG.constGraph());
	if (!result || (CG.numberOfClusters() == 1))
	{
		// Either non-planar or only root cluster exists, which does not restrict c-planarity.
		return result;
	}
	// We first create a copy of input G, and work solely on the copy

	// In case of the sm_new solution method, we partition the graph in
	// independent parts and test them separately
	// For all parts we test until non-c-planar or all tested.
	if (m_solmeth==sm_new)
	{
		// We use the ClusterAnalysis to search for independent bags
		// Here is the idea: We detect all bags that are minimum wrt
		// cluster inclusion (i.e. if a cluster contains a cluster c with
		// a single bag, we don't add the cluster c itself) but do
		// not contain an outeractive vertex wrt to smallest containing cluster
		// (i.e. the cluster used in the definition of bag).
		// The clustered subgraphs induced by these bags can be tested independently,
		// as we can move them freely in the drawing area of their enclosing parent cluster.

		ClusterAnalysis ca(CG, true); //Compute all structures, indyBags too
		// We can solve the c-planarity testing for all indyBags independently,
		// and in case all are c-planar, also our input c-graph is c-planar.
		const int numIndyBags = ca.numberOfIndyBags();
		GraphCopy** theGraphs = new GraphCopy * [numIndyBags]; //Stores copies for the bag graphs.
#ifdef OGDF_DEBUG
		cout << "Number of IndyBags "<<numIndyBags<<"\n";
#endif
		Logger::slout() << "Number of IndyBags "<<numIndyBags<<"\n";
		Array<List<node> > nodesInBag(numIndyBags); //Stores vertices for the bag graphs.
		const Graph & G = CG.constGraph();
		for(node v : G.nodes){
		   nodesInBag[ca.indyBagIndex(v)].pushBack(v);
		}

		for (int i = 0; i < numIndyBags && m_optStatus == Master::Optimal; i++)
		{
			// Create underlying graph
			theGraphs[i] = new GraphCopy();
			theGraphs[i]->createEmpty(G);
			// Judging from the interface and the description, there are two
			// methods in GraphCopy that allow to construct parts based on a
			// set of vertices, initByNodes and initByActiveNodes, where the
			// latter one seems to be appropriate and can be used with an
			// additional 3n work to initialize the NodeArray and mark the vertices.
			// However, even though the former is meant to be used for connected
			// components, it also works for set of connected components, and
			// an independent bag is such a creature.
			EdgeArray<edge> eCopy(G);
			theGraphs[i]->initByNodes(nodesInBag[i], eCopy);
			ClusterGraph bagCG(*theGraphs[i]);
			//node v;
			ClusterArray<List<node> > cNodes(CG);
			ClusterArray<List<cluster> > cChildren(CG);
			ClusterArray<cluster> cCopy(CG);
			// Run through all original vertices and store
			// lists of copies at each cluster that is part of the bag.
			// Note: We should not add an enclosing parent cluster below
			// root, i.e., when the root does only have a single child
			// and no vertices, we delete the child again.
			for(node u : nodesInBag[i])
			{
				cluster ct = CG.clusterOf(u);
				cNodes[ct].pushBack(theGraphs[i]->copy(u));
				// Check if we need to store the parent relation on the path
				// to the root. Indicator is: We have just added the first element.

				while ((ct != CG.rootCluster()) &&
						(cNodes[ct].size() + cChildren[ct].size() == 1))
				{
					cChildren[ct->parent()].pushBack(ct);
					ct = ct->parent();
				}
			}

			// Create cluster structure
			// For each vertex in the indyBag we create the cluster path
			// to the bag root if necessary.

			// Now build the cluster structure top down
			// Lists of root are never both empty
			List<cluster> queue;
			queue.pushBack(ca.indyBagRoot(i));
			cCopy[queue.front()] = bagCG.rootCluster();
			while (!queue.empty())
			{
				cluster c = queue.popFrontRet();

				//vertices are assigned to root by construction
				if (cCopy[c] != bagCG.rootCluster())
				{
					for(node u : cNodes[c])
						bagCG.reassignNode(u, cCopy[c]);
				}

				for(cluster ci : cChildren[c])
				{
					cCopy[ci] = bagCG.newCluster(cCopy[c]);
					queue.pushBack(ci);
				}

			}
#ifdef OGDF_DEBUG
			Logger::slout() << "Created clustered graph for indy bag with "<<theGraphs[i]->numberOfNodes()<< " nodes and "<<
					bagCG.numberOfClusters()<<" clusters\n";
			// Make sure the cluster structure is a rooted tree
			cluster t = bagCG.rootCluster();
			int ccnt = 0;
			List<cluster> cqueue;
			cqueue.pushBack(t);
			while (!cqueue.empty())
			{
				t = cqueue.popFrontRet();
				for(cluster c : t->children) {
					cqueue.pushBack(c);
				}
				ccnt++;
			}
			OGDF_ASSERT(ccnt == bagCG.numberOfClusters());
			string filename = string("IndySubcgraph") + to_string(i) + ".gml";

			ClusterGraphAttributes CGA(bagCG);
			GraphIO::writeGML(CGA, filename);
#endif
			//now the actual test, similar to the one below...
			if (theGraphs[i]->numberOfNodes() > 2) //could even start at 4...
			{
				makeParallelFreeUndirected(*theGraphs[i]); //Could do this while creating copy
				Logger::slout()<< "IndyBag of size n m c"<<theGraphs[i]->numberOfNodes()<< " "<<
						theGraphs[i]->numberOfEdges()<< " "<< bagCG.numberOfClusters()<<"\n";
				List<nodePair> ae;
				//Todo: Add an interface here that allows to transfer bag and
				// activity information to the master, otherwise we have to
				// compute this info twice.
				bool imresult = doTest(bagCG, ae);
	#ifdef OGDF_DEBUG
				Logger::slout() << "IndyBag number "<<i<<" is "<< (imresult ? "" : "non-") <<"c-planar\n";
				Logger::slout() << "Number of edges added for IndyBag: "<<ae.size()<<"\n";
	#endif
				result = result && imresult;
				if (!result) return result;
				for(const nodePair &np : ae) {
					addedEdges.emplaceBack(theGraphs[i]->original(np.v1), theGraphs[i]->original(np.v2));
				}
			}
#ifdef OGDF_DEBUG
			else
			{
				Logger::slout() << "IndyBag number "<<i<<" skipped due to size\n";
			}
#endif
		}//for indy bags

		for (int i = 0; i < numIndyBags; i++)
		{
			delete theGraphs[i];
		}
		delete [] theGraphs;

		// We test consistency by summing up the number of vertices.
	}
	else { //todo: can be joined again, as it is a special case wo cluster analysis
		//otherwise we just make a copy of the whole graph
		Graph G;
		ClusterArray<cluster> clusterCopy(CG);
		NodeArray<node> nodeCopy(CG.constGraph());
		EdgeArray<edge> edgeCopy(CG.constGraph());
		ClusterGraph C(CG,G,clusterCopy, nodeCopy, edgeCopy);
		makeParallelFreeUndirected(G);
		NodeArray<node> nodeOrig(G);
		for(node v : CG.constGraph().nodes)
		{
			nodeOrig[nodeCopy[v]] = v;
		}

		//Could use same list here for both graphs.
		addedEdges.clear();
		List<nodePair> ae;
		result = doTest(C,ae);
		//nodepairs are for the copy, store original nodes here
		for (const nodePair &np : ae) {
			addedEdges.emplaceBack(nodeOrig[np.v1], nodeOrig[np.v2]);
		}
	}

	return result;
}
Beispiel #16
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;
	for (node s : act->nodes)
		subGraphNodes.pushBack(s);

	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,nullptr);


	for (node w : act->nodes)
	{
		//adjEntry adj = w->firstAdj();
		for(adjEntry adj : w->adjEntries)
		{
			edge e = adj->theEdge();
			edge cor = nullptr;
			if (table[e->source()] == nullptr) // 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()] == nullptr) // dito
			{
				cor = subGraph.newEdge(table[e->source()],superSink);
				outgoingTable[cor] = e->target();
			}

			// else edge connects two nodes of the cluster
		}
	}
	if (superSink->degree() == 0) // root cluster is not connected to outside clusters
	{
		subGraph.delNode(superSink);
		superSink = nullptr;
	}


	bool cPlanar = preparation(subGraph,act,superSink);


	if (cPlanar && act != C.rootCluster())
	{
		// Remove induced subgraph and the cluster act.
		// Replace it by a wheel graph
		while (!subGraphNodes.empty())
		{
			node w = subGraphNodes.popFrontRet();
//			C.unassignNode(w);
			G.delNode(w);
		}

		cluster parent = act->parent();

		if (superSink && m_clusterPQTree[act])
			constructWheelGraph(C,G,parent,m_clusterPQTree[act],outgoingTable);

		C.delCluster(act);
		if (m_clusterPQTree[act] != nullptr) // if query necessary for clusters with just one child
		{
			m_clusterPQTree[act]->emptyAllPertinentNodes();
			delete m_clusterPQTree[act];
		}

	}
	else if (!cPlanar)
		{
			m_errorCode = nonCPlanar;
		}//if not cplanar

	return cPlanar;

}
Beispiel #17
0
static bool writeCluster(
	std::ostream &out, int depth,
	const ClusterArray < std::vector<edge> > &edgeMap,
	const ClusterGraph &C, const ClusterGraphAttributes *CA, const cluster &c,
	int &clusterId)
{
	std::ios_base::fmtflags currentFlags = out.flags();
	out.flags(currentFlags | std::ios::fixed);
	bool result = out.good();

	if(result) {
		if (C.rootCluster() == c) {
			writeHeader(out, depth++, CA);
		} else {
			GraphIO::indent(out, depth++) << "subgraph cluster" << clusterId << " {\n";
		}
		clusterId++;

		bool whitespace; // True if a whitespace should printed (readability).

		whitespace = false;
		if (CA) {
			writeAttributes(out, depth, *CA, c);
			whitespace = true;
		}

		if (whitespace) {
			out << "\n";
		}

		// Recursively export all subclusters.
		whitespace = false;
		for (cluster child : c->children) {
			writeCluster(out, depth, edgeMap, C, CA, child, clusterId);
			whitespace = true;
		}

		if (whitespace) {
			out << "\n";
		}

		// Then, print all nodes whithout an adjacent edge.
		whitespace = false;
		for (node v : c->nodes) {
			whitespace |= writeNode(out, depth, CA, v);
		}

		if (whitespace) {
			out << "\n";
		}

		// Finally, we print all edges for this cluster (ugly version for now).
		const std::vector<edge> &edges = edgeMap[c];
		whitespace = false;
		for (auto &e : edges) {
			whitespace |= writeEdge(out, depth, CA, e);
		}

		GraphIO::indent(out, --depth) << "}\n";
	}

	out.flags(currentFlags);

	return result;
}
//the call function that lets ClusterPlanarizationLayout compute a layout
//for the input using \a weight for the computation of the cluster planar subgraph
void ClusterPlanarizationLayout::call(
	Graph& G,
	ClusterGraphAttributes& acGraph,
	ClusterGraph& cGraph,
	EdgeArray<double>& edgeWeight,
	bool simpleCConnect) //default true
{
	m_nCrossings = 0;
	bool subGraph = false; // c-planar subgraph computed?

	//check some simple cases
	if (G.numberOfNodes() == 0) return;

//-------------------------------------------------------------
//we set pointers and arrays to the working graph, which can be
//the original or, in the case of non-c-planar input, a copy

	Graph* workGraph = &G;
	ClusterGraph* workCG = &cGraph;
	ClusterGraphAttributes* workACG = &acGraph;

	//potential copy of original if non c-planar
	Graph GW;
	//list of non c-planarity causing edges
	List<edge> leftEdges;

	//list of nodepairs to be connected (deleted edges)
	List<NodePair> leftWNodes;

	//store some information
	//original to copy
	NodeArray<node> resultNode(G);
	EdgeArray<edge> resultEdge(G);
	ClusterArray<cluster> resultCluster(cGraph);
	//copy to original
	NodeArray<node> orNode(G);
	EdgeArray<edge> orEdge(G);
	ClusterArray<cluster> orCluster(cGraph);

	for(node workv : G.nodes) {
		resultNode[workv] = workv; //will be set to copy if non-c-planar
		orNode[workv] = workv;
	}
	for(edge worke : G.edges) {
		resultEdge[worke] = worke; //will be set to copy if non-c-planar
		orEdge[worke] = worke;
	}
	for (cluster workc : cGraph.clusters) {
		resultCluster[workc] = workc; //will be set to copy if non-c-planar
		orCluster[workc] = workc;
	}


	//-----------------------------------------------
	//check if instance is clusterplanar and embed it
	CconnectClusterPlanarEmbed CCPE; //cccp

	bool cplanar = CCPE.embed(cGraph, G);

	List<edge> connectEdges;

	//if the graph is not c-planar, we have to check the reason and to
	//correct the problem by planarising or inserting connection edges
	if (!cplanar)
	{
		bool connect = false;

		if ( (CCPE.errCode() == CconnectClusterPlanarEmbed::nonConnected) ||
				(CCPE.errCode() == CconnectClusterPlanarEmbed::nonCConnected) )
		{
			//we insert edges to make the input c-connected
			makeCConnected(cGraph, G, connectEdges, simpleCConnect);

			//save edgearray info for inserted edges
			for(edge e : connectEdges)
			{
				resultEdge[e] = e;
				orEdge[e]     = e;
			}

			connect = true;

			CCPE.embed(cGraph, G);

			if ( (CCPE.errCode() == CconnectClusterPlanarEmbed::nonConnected) ||
				(CCPE.errCode() == CconnectClusterPlanarEmbed::nonCConnected) )
			{
				cerr << "no correct connection made\n"<<flush;
				OGDF_THROW(AlgorithmFailureException);
			}
		}//if not cconnected
		if ((CCPE.errCode() == CconnectClusterPlanarEmbed::nonPlanar) ||
			(CCPE.errCode() == CconnectClusterPlanarEmbed::nonCPlanar))
		{
			subGraph = true;
			EdgeArray<bool> inSubGraph(G, false);

			CPlanarSubClusteredGraph cps;
			if (edgeWeight.valid())
				cps.call(cGraph, inSubGraph, leftEdges, edgeWeight);
			else
				cps.call(cGraph, inSubGraph, leftEdges);
#ifdef OGDF_DEBUG
			//			for(edge worke : G.edges) {
			//				if (inSubGraph[worke])
			//					acGraph.strokeColor(worke) = "#FF0000";
			//			}
#endif
			//---------------------------------------------------------------
			//now we delete the copies of all edges not in subgraph and embed
			//the subgraph (use a new copy)

			//construct copy

			workGraph = &GW;
			workCG = new ClusterGraph(cGraph, GW, resultCluster, resultNode, resultEdge);

			//----------------------
			//reinit original arrays
			orNode.init(GW, nullptr);
			orEdge.init(GW, nullptr);
			orCluster.init(*workCG, nullptr);

			//set array entries to the appropriate values
			for (node workv : G.nodes)
				orNode[resultNode[workv]] = workv;
			for (edge worke : G.edges)
				orEdge[resultEdge[worke]] = worke;
			for (cluster workc : cGraph.clusters)
				orCluster[resultCluster[workc]] = workc;

			//----------------------------------------------------
			//create new ACG and copy values (width, height, type)

			workACG = new ClusterGraphAttributes(*workCG, workACG->attributes());
			for (node workv : GW.nodes)
			{
				//should set same attributes in construction!!!
				if (acGraph.attributes() & GraphAttributes::nodeType)
					workACG->type(workv) = acGraph.type(orNode[workv]);
				workACG->height(workv) = acGraph.height(orNode[workv]);
				workACG->width(workv) = acGraph.width(orNode[workv]);
			}
			if (acGraph.attributes() & GraphAttributes::edgeType) {
				for (edge worke : GW.edges) {
					workACG->type(worke) = acGraph.type(orEdge[worke]);
					//all other attributes are not needed or will be set
				}
			}

			for(edge ei : leftEdges)
			{
				edge e = resultEdge[ei];
				NodePair np;
				np.m_src = e->source();
				np.m_tgt = e->target();

				leftWNodes.pushBack(np);

				GW.delEdge(e);
			}

			CconnectClusterPlanarEmbed CCP;

#ifdef OGDF_DEBUG
			bool subPlanar =
#endif
				CCP.embed(*workCG, GW);
			OGDF_ASSERT(subPlanar);
		}//if not planar
		else
		{
			if (!connect)
			OGDF_THROW_PARAM(PreconditionViolatedException, pvcClusterPlanar);
		}

	}//if

	//if multiple CCs are handled, the connectedges (their copies resp.)
	//can be deleted here

	//now CCPE should give us the external face

	ClusterPlanRep CP(*workACG, *workCG);

	OGDF_ASSERT(CP.representsCombEmbedding());

	const int numCC = CP.numberOfCCs(); //equal to one
	//preliminary
	OGDF_ASSERT(numCC == 1);

	// (width,height) of the layout of each connected component
	Array<DPoint> boundingBox(numCC);

	for (int ikl = 0; ikl < numCC; ikl++)
	{

			CP.initCC(ikl);
			CP.setOriginalEmbedding();

			OGDF_ASSERT(CP.representsCombEmbedding())

			Layout drawing(CP);

			//m_planarLayouter.get().setOptions(4);//progressive

			adjEntry ae = nullptr;

			//internally compute adjEntry for outer face

			//edges that are reinserted in workGraph (in the same
			//order as leftWNodes)
			List<edge> newEdges;
			m_planarLayouter.get().call(CP, ae, drawing, leftWNodes, newEdges, *workGraph);

			OGDF_ASSERT(leftWNodes.size()==newEdges.size())
			OGDF_ASSERT(leftEdges.size()==newEdges.size())

			ListConstIterator<edge> itE = newEdges.begin();
			ListConstIterator<edge> itEor = leftEdges.begin();
			while (itE.valid())
			{
				orEdge[*itE] = *itEor;
				++itE;
				++itEor;
			}

			//hash index over cluster ids
			HashArray<int, ClusterPosition> CA;

			computeClusterPositions(CP, drawing, CA);

			// copy layout into acGraph
			// Later, we move nodes and edges in each connected component, such
			// that no two overlap.

			for(int i = CP.startNode(); i < CP.stopNode(); ++i) {
				node vG = CP.v(i);

				acGraph.x(orNode[vG]) = drawing.x(CP.copy(vG));
				acGraph.y(orNode[vG]) = drawing.y(CP.copy(vG));

				for(adjEntry adj : vG->adjEdges)
				{
					if ((adj->index() & 1) == 0) continue;
					edge eG = adj->theEdge();

					edge orE = orEdge[eG];
					if (orE)
						drawing.computePolylineClear(CP,eG,acGraph.bends(orE));
				}

			}//for

			//even assignment for all nodes is not enough, we need all clusters
			for(cluster c : workCG->clusters)
			{
				int clNumber = c->index();
				//int orNumber = originalClId[c];
				cluster orCl = orCluster[c];

				if (c != workCG->rootCluster())
				{
					OGDF_ASSERT(CA.isDefined(clNumber));
					acGraph.height(orCl) = CA[clNumber].m_height;
					acGraph.width(orCl) = CA[clNumber].m_width;
					acGraph.y(orCl) = CA[clNumber].m_miny;
					acGraph.x(orCl) = CA[clNumber].m_minx;
				}//if real cluster
			}

			// the width/height of the layout has been computed by the planar
			// layout algorithm; required as input to packing algorithm
			boundingBox[ikl] = m_planarLayouter.get().getBoundingBox();

	}//for connected components

	//postProcess(acGraph);
	//
	// arrange layouts of connected components
	//

	Array<DPoint> offset(numCC);

	m_packer.get().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, edge and cluster by the offset
	// of its connected component.

	const Graph::CCsInfo &ccInfo = CP.ccInfo();
	for(int i = 0; i < numCC; ++i)
	{
		const double dx = offset[i].m_x;
		const double dy = offset[i].m_y;

		HashArray<int, bool> shifted(false);

		// iterate over all nodes in ith CC
		for(int j = ccInfo.startNode(i); j < ccInfo.stopNode(i); ++j)
		{
			node v = ccInfo.v(j);

			acGraph.x(orNode[v]) += dx;
			acGraph.y(orNode[v]) += dy;

			// update cluster positions accordingly
			//int clNumber = cGraph.clusterOf(orNode[v])->index();
			cluster cl = cGraph.clusterOf(orNode[v]);

			if ((cl->index() > 0) && !shifted[cl->index()])
			{
				acGraph.y(cl) += dy;
				acGraph.x(cl) += dx;
				shifted[cl->index()] = true;
			}//if real cluster

			for(adjEntry adj : v->adjEdges) {
				if ((adj->index() & 1) == 0) continue;
				edge e = adj->theEdge();

				//edge eOr = orEdge[e];
				if (orEdge[e])
				{
					DPolyline &dpl = acGraph.bends(orEdge[e]);
					for(DPoint &p : dpl) {
						p.m_x += dx;
						p.m_y += dy;
					}
				}
			}
		}//for nodes
	}//for numcc


	while (!connectEdges.empty()) {
		G.delEdge(connectEdges.popFrontRet());
	}

	if (subGraph)
	{
		//originalClId.init();
		orCluster.init();
		orNode.init();
		orEdge.init();
		delete workCG;
		delete workACG;
	}//if subgraph created

	acGraph.removeUnnecessaryBendsHV();

}//call