Пример #1
0
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;
}
Пример #2
0
void Planar::reloadFile(string configFile) throw(FileNotFound) {
	ifstream fin(configFile);

	if (!fin.is_open()) {
		throw FileNotFound(configFile + " is missing and cannot be loaded.");
	}

//Determine the number of vertices
	fin >> total;

//Gather each of the vertices
	for(int i = 0; i < total; ++i) { 
		glm::vec3 pt;
		fin >> pt.x;
		fin >> pt.y;
		fin >> pt.z;

		vertices.push_back(pt);
	}

//Check for convexity
	convex = isConvex();

//Check for planarity
	planar = isPlanar();

//Build the geometry
	buildGeometry();

//Put all of the vertices into an array
	mesh.dump();

//Allocate the buffer space
	Geometry::buildBuffer(mesh.total, mesh.colors, mesh.normals, mesh.points);
}
Пример #3
0
	void SimpleEmbedder::call(Graph& G, adjEntry& adjExternal)
	{
		OGDF_ASSERT(isPlanar(G));

		//----------------------------------------------------------
		//
		// determine embedding of G
		//

		// We currently compute any embedding and choose the maximal face
		// as external face

		// if we use FixedEmbeddingInserterOld, we have to re-use the computed
		// embedding, otherwise crossing nodes can turn into "touching points"
		// of edges (alternatively, we could compute a new embedding and
		// finally "remove" such unnecessary crossings).
		adjExternal = nullptr;
		if(!G.representsCombEmbedding())
			planarEmbed(G);

		if (G.numberOfEdges() > 0)
		{
			CombinatorialEmbedding E(G);
			//face fExternal = E.maximalFace();
			face fExternal = findBestExternalFace(G, E);
			adjExternal = fExternal->firstAdj();
		}
	}
Пример #4
0
void TriMeshPlanar::postInitialSetup()
{
    // works for non-parallel mesh only
    if(!isPlanar())
        error->all(FLERR,"Face defined as planar face is in fact not planar. You might want to check the 'curvature' setting");

    if(this->isParallel())
        error->all(FLERR,"internal error");

    buildEdgeLists();
}
Пример #5
0
bool InnerRegion::isValid(){
    
    // Is planar
    bool valid = isPlanar();
    
    // All vertices (internal and boundary) must be dominated by the two endpoints
    for (int i = 0; i < getSize(); i++) {
        if (i == endpoint1 || i == endpoint2) {
            continue;
        }
        valid &= isAdjacent(i, endpoint1) || isAdjacent(i, endpoint2);
    }
    return valid;
}
Пример #6
0
// sets the positions of the nodes in a largest face of G in the form
// of a regular k-gon. The corresponding nodes and their positions are
// stored in nodes and pos, respectively.
void TutteLayout::setFixedNodes(
	const Graph &G,
	List<node>& nodes,
	List<DPoint>& pos,
	double radius)
{
	// compute faces of a copy of G
	GraphCopy GC(G);

	// compute a planar embedding if \a G is planar
	if(isPlanar(G)) planarEmbed(GC);
	//FIXME this stuff above seems wrong!!

	CombinatorialEmbedding E(GC);
	E.computeFaces();

	// search for largest face
	face maxFace = E.maximalFace();

	// delete possible old entries in nodes and pos
	nodes.clear();
	pos.clear();

	// set nodes and pos
	NodeArray<bool> addMe(GC,true);

	List<node> maxNodes;
	for(adjEntry adj : maxFace->entries) {
		maxNodes.pushBack(adj->theNode());
	}

	for(node w : maxNodes) {
		if(addMe[w]) {
			nodes.pushBack(w);
			addMe[w] = false;
		}
	}

	double step  = 2.0 * Math::pi / (double)(nodes.size());
	double alpha = 0.0;
	for(int i = 0; i < nodes.size(); ++i) {
		pos.pushBack(DPoint(radius * cos(alpha), radius * sin(alpha)));
		alpha += step;
	}
}
Пример #7
0
void AudioResampleImpl::splitAudioData(AudioData & data, boost::scoped_array<char*> & split) const
{
	auto dataFormat = data.format();

	if (dataFormat.isPlanar())
	{
		/// Для планарного формата необходимо представить данные из result
		const int numChannels = dataFormat.channelCount();
		split.reset(new char*[numChannels]);
		split_ref(data.begin(), data.end(), data.numBytes() / numChannels, split.get());
	}
	else
	{
		/// Interleaved данные помещаются в один массив
		split.reset(new char*[1]);
		split[0] = data.data();
	}
}
Пример #8
0
void UpwardPlanarSubgraphSimple::call(GraphCopy &GC, List<edge> &delEdges)
{
	const Graph &G = GC.original();
	delEdges.clear();

	// We construct an auxiliary graph H which represents the current upward
	// planar subgraph.
	Graph H;
	NodeArray<node> mapToH(G,nullptr);
	NodeArray<node> mapToG(H,nullptr);

	for(node v : G.nodes)
		mapToG[ mapToH[v] = H.newNode() ] = v;


	// We currently support only single-source acyclic digraphs ...
	node s;
	hasSingleSource(G,s);

	OGDF_ASSERT(s != 0);
	OGDF_ASSERT(isAcyclic(G));

	// We start with a spanning tree of G rooted at the single source.
	NodeArray<bool> visitedNode(G,false);
	SListPure<edge> treeEdges;
	dfsBuildSpanningTree(s,treeEdges,visitedNode);


	// Mark all edges in the spanning tree so they can be skipped in the
	// loop below and add (copies of) them to H.
	EdgeArray<bool> visitedEdge(G,false);
	SListConstIterator<edge> it;
	for(it = treeEdges.begin(); it.valid(); ++it) {
		edge eG = *it;
		visitedEdge[eG] = true;
		H.newEdge(mapToH[eG->source()],mapToH[eG->target()]);
	}


	// Add subsequently the remaining edges to H and test if the resulting
	// graph is still upward planar. If not, remove the edge again from H
	// and add it to delEdges.

	SList<Tuple2<node,node> > augmented;
	GraphCopySimple graphAcyclicTest(G);

	for(edge eG : G.edges)
	{
		// already treated ?
		if(visitedEdge[eG] == true)
			continue;

		// insert edge into H
		edge eH = H.newEdge(mapToH[eG->source()],mapToH[eG->target()]);

		node superSink;
		SList<edge> augmentedEdges;
		if (UpwardPlanarity::upwardPlanarAugment_singleSource(H,superSink,augmentedEdges) == false) {
			// if H is no longer upward planar, remove eG from subgraph
			H.delEdge(eH);
			delEdges.pushBack(eG);

		} else {
			// add augmented edges as node-pair to tmpAugmented and remove
			// all augmented edges from H again
			SList<Tuple2<node,node> > tmpAugmented;
			SListConstIterator<edge> it;
			for(it = augmentedEdges.begin(); it.valid(); ++it) {
				node v = mapToG[(*it)->source()];
				node w = mapToG[(*it)->target()];

				if (v && w)
					tmpAugmented.pushBack(Tuple2<node,node>(v,w));

				H.delEdge(*it);
			}

			if (mapToG[superSink] == nullptr)
				H.delNode(superSink);

			//****************************************************************
			// The following is a simple workaround to assure the following
			// property of the upward planar subgraph:
			//   The st-augmented upward planar subgraph plus the edges not
			//   in the subgraph must be acyclic. (This is a special property
			//   of the embedding, not the augmentation.)
			// The upward-planar embedding function gives us ANY upward-planar
			// embedding. We check if the property above holds with this
			// embedding. If it doesn't, we have actually no idea if another
			// embedding would do.
			// The better solution would be to incorporate the acyclicity
			// property into the upward-planarity test, but this is compicated.
			//****************************************************************

			// test if original graph plus augmented edges is still acyclic
			if(checkAcyclic(graphAcyclicTest,tmpAugmented) == true) {
				augmented = tmpAugmented;

			} else {
				// if not, remove eG from subgraph
				H.delEdge(eH);
				delEdges.pushBack(eG);
			}
		}

	}

	// remove edges not in the subgraph from GC
	ListConstIterator<edge> itE;
	for(itE = delEdges.begin(); itE.valid(); ++itE)
		GC.delEdge(GC.copy(*itE));

	// add augmented edges to GC
	SListConstIterator<Tuple2<node,node> > itP;
	for(itP = augmented.begin(); itP.valid(); ++itP) {
		node v = (*itP).x1();
		node w = (*itP).x2();

		GC.newEdge(GC.copy(v),GC.copy(w));
	}

	// add super sink to GC
	node sGC = nullptr;
	SList<node> sinks;
	for(node v : GC.nodes) {
		if(v->indeg() == 0)
			sGC = v;
		if(v->outdeg() == 0)
			sinks.pushBack(v);
	}

	node superSinkGC = GC.newNode();
	SListConstIterator<node> itV;
	for(itV = sinks.begin(); itV.valid(); ++itV)
		GC.newEdge(*itV,superSinkGC);

	// add st-edge to GC, so that we now have a planar st-digraph
	GC.newEdge(sGC,superSinkGC);

	OGDF_ASSERT(isAcyclic(GC));
	OGDF_ASSERT(isPlanar(GC));
}
Пример #9
0
	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;
	}
Пример #10
0
/*!
    Returns the number fo planes for the pixel format \a format
*/
int QVideoFrame::planesCount( PixelFormat format )
{
    return isPlanar( format ) ? 3 : 1;
}
Пример #11
0
int AudioFormat::planeCount() const
{
    return isPlanar() ? 1 : channels();
}
Пример #12
0
void TriconnectedShellingOrder::doCall(
		const Graph& G,
		adjEntry adj,
		List<ShellingOrderSet>& partition)
{

	// prefer nodes to faces?
	bool preferNodes = false;

	#ifdef OUTPUT_TSO
		cout << "Graph G is planar         == " << isPlanar(G)  << endl;
		cout << "Graph G has no self loops == " << isLoopFree(G) 		<< endl;
		cout << "Graph G is connected      == " << isConnected(G) 		<< endl;
		cout << "Graph G is triconnected   == " << isTriconnected(G) 	<< endl;
	#endif

	OGDF_ASSERT(isPlanar(G) == true);
	OGDF_ASSERT(isLoopFree(G) 		== true);
	OGDF_ASSERT(isTriconnected(G) 	== true);

	// crate an embedding for G
	ConstCombinatorialEmbedding E(G);

	// set outerFace so adj is on it or to face with maximal size
	face outerFace = (adj != nullptr) ? E.rightFace(adj) : E.maximalFace();

	#ifdef OUTPUT_TSO
		cout << "faces:" << endl;
		for(face fh : E.faces) {
			if (fh == outerFace)
				cout << "  face *" << fh->index() << ":";
			else
				cout << "  face  " << fh->index() << ":";
			for(adjEntry adj : fh->entries)
				cout << " " << adj;
			cout << endl;
		}

		cout << "adjacency lists:" << endl;
		for(node vh : G.nodes) {
			cout << "  node " << vh << ":";
			for(adjEntry adj : vh->adjEntries)
				cout << " " << adj;
			cout << endl;
		}
	#endif

	adjEntry firstAdj = outerFace->firstAdj();
	// set firstAdj that the outer face is on the left of firstAdj
	if (E.rightFace(firstAdj) == outerFace)
		firstAdj = firstAdj->cyclicSucc();

	// set "base" nodes v1, v2 on outer face with edge [v1,v2]
	node v1 = firstAdj->theNode();
	node v2 = firstAdj->cyclicPred()->twinNode();

	ComputeTricOrder cto(G, E, outerFace, m_baseRatio, preferNodes);

	// if outerFace == {v_1,...,v_q}
	// 		adjPred(v_i) == v_i -> v_{i-1}
	// 		adjSucc(v_i) == v_1 -> v_{i+1}
	// these arrays will be updated during the algo so they define the outer face
	NodeArray<adjEntry> adjPred(G),
						adjSucc(G);

	// init adjPred and adjSucc for the nodes of the outer face
	adjSucc[v1] = firstAdj;
	adjEntry adjRun = firstAdj->twin()->cyclicSucc();
	do {
		adjPred[adjRun->theNode()] = adjRun->cyclicPred();
		adjSucc[adjRun->theNode()] = adjRun;
		adjRun = adjRun->twin()->cyclicSucc();
	} while (adjRun != firstAdj);
	adjPred[v1] = adjSucc[v2] = nullptr;

	// init outer nodes and outer edges
	cto.initOuterNodes(v1, v2);
	cto.initOuterEdges();

	// init the first possible node as the node in the middle of v_1
	//   and v_2 on the outer face
	int l = (outerFace->size() -2)/2;
	if (l == 0)
		l = 1;
	adjRun = firstAdj;
	for (int i=1; i <= l; i++)
		adjRun = adjRun->twin()->cyclicSucc();

	cto.initPossible(adjRun->theNode());

	// node and face that are selected during the algorithm
	node vk;
	face Fk;
	// left and right node of current nodeset
	node cl, cr;
	// the actual nodeset V in the shelling order
	ShellingOrderSet V;
	// further auxiliary variables

	#ifdef OUTPUT_TSO
		cout << "finished initialization of cto, adjSucc, adjPred." << endl << flush;
		cout << "v1 = " << v1 << ", v2 = " << v2 << ", first possible node = " << adjRun->theNode() << endl;

		for(adjEntry adj1 : outerFace->entries) {
			cout << " node " << adj1->theNode() << ": adjPred=(" << adjPred[adj1->theNode()]
				 << "), adjSucc=" << adjSucc[adj1->theNode()] << endl;
		}
		cto.output();
		cout << "starting main loop" << endl;
	#endif

	// main loop
	while (cto.isPossible()){

		// get the next possible nodeset for the order
		cto.getNextPossible(vk, Fk);

		// check if the current selection is a node or a face
		if (cto.isNode()){

			#ifdef OUTPUT_TSO
				cout << " nextPossible is node " << vk << endl << flush;
			#endif

			// current item is a node
			V = ShellingOrderSet(1, adjPred[vk], adjSucc[vk]);
			V[1] = vk;
			cl = (adjPred[vk])->twinNode();
			cr = (adjSucc[vk])->twinNode();
			// insert actual nodeset to the front of the shelling order
			partition.pushFront(V);
		}
		else{

			#ifdef OUTPUT_TSO
				cout << " nextPossible is face " << Fk->index() << endl << flush;
			#endif

			// current item is a face
			// create set with chain {z_1,...,z_l}
			V = ShellingOrderSet(cto.getOutv(Fk)-2);

			// now find node v on Fk with degree 2
			cl = cto.getOuterNodeDeg2(Fk, adjPred, adjSucc);
			// find end of chain cl and cr
			// traverse to left while degree == 2
			while ((cl != v1) && (adjPred[cl] == adjSucc[cl]->cyclicSucc()))
				cl = (adjPred[cl])->twinNode();

			// traverse to the right while degree == 2
			//  and insert nodes into the ShellingOrderSet
			cr = adjSucc[cl]->twinNode();
			int i = 1;
			while ((cr != v2) && (adjPred[cr] == adjSucc[cr]->cyclicSucc())){
				V[i] = cr;
				cr = (adjSucc[cr])->twinNode();
				i++;
			}
			cto.decSepf(cl);
			cto.decSepf(cr);
			// set left and right node in the shelling order set
			V.left(cl);
			V.right(cr);
			// set left and right adjacency entry
			V.leftAdj((adjPred[cr])->twin());
			V.rightAdj((adjSucc[cl])->twin());
			// insert actual nodeset to the front of the shelling order
			partition.pushFront(V);
		}// current item is a face

		#ifdef OUTPUT_TSO
			cout << "  set cl = " << cl << endl;
			cout << "  set cr = " << cr << endl;
		#endif

		// update adjSucc[cl] and adjPred[cr]
		adjSucc[cl] = adjSucc[cl]->cyclicSucc();
		adjPred[cr] = adjPred[cr]->cyclicPred();
		// increase number of outer edges of face left of adjPred[cr]
		cto.incOute(E.leftFace(adjPred[cr]));
		cto.incVisited(cl);
		cto.incVisited(cr);

		// traverse from cl to cr on the new outer face
		//  and update adjSucc[] and adjPred[]
		adjEntry adj1 = adjSucc[cl]->twin();

		for (node u = adj1->theNode(); u != cr; u = adj1->theNode()){
			// increase oute for the right face of adj1
			cto.incOute(E.leftFace(adj1));

			// set new predecessor
			adjPred[u] = adj1;

			// go to next adj-entry
			adj1 = adj1->cyclicSucc();

			// if the actual node has an edge to the deleted node
			//  increase the visited value for the actual node...
			if (adj1->twinNode() == vk){
				cto.incVisited(u);
				// ... and skip the actual adjEntry
				adj1 = adj1->cyclicSucc();
			}
			adjSucc[u] = adj1;

			// add actual node to outerNodes[f]
			for (adjEntry adj2 = adjPred[u]; adj2 != adjSucc[u]; adj2 = adj2->cyclicPred()){
				cto.addOuterNode(u, E.leftFace(adj2));
			}
			adj1 = adj1->twin();
		}

		if (!cto.isNode()){
			if ( ((adjSucc[cl])->twinNode() == cr)
						&& ( cto.isOnlyEdge(E.rightFace(adjSucc[cl])) ) ){
				cto.decSepf(cl);
				cto.decSepf(cr);
			}
		}

		// update cto
		cto.doUpdate();

		#ifdef OUTPUT_TSO
			cto.output();
		#endif
	}// while (cto.isPossible())

	// finally push the base (v1,v2) to the order
	V = ShellingOrderSet(2);
	V[1] = v1;
	V[2] = v2;
	partition.pushFront(V);

	#ifdef OUTPUT_TSO
		cout << "output of the computed partition:" << endl;
		int k = 1;
		for (const ShellingOrderSet &S : partition) {
			int size = S.len();
			cout << "nodeset with nr " << k << ":" << endl;
			for (int j=1; j<=size; j++)
				cout << " node " << S[j] <<", ";
			cout << "." << endl;
		}
	#endif
}// void TriconnectedShellingOrder::doCall
Пример #13
0
Module::ReturnType SubgraphPlanarizerUML::doCall(
	PlanRepUML           &pr,
	int                   cc,
	const EdgeArray<int> *pCostOrig,
	int                  &crossingNumber)
{
	OGDF_ASSERT(m_permutations >= 1);

	PlanarSubgraphModule   &subgraph = m_subgraph.get();
	UMLEdgeInsertionModule &inserter = m_inserter.get();

	unsigned int nThreads = min(m_maxThreads, (unsigned int)m_permutations);

	int64_t startTime;
	System::usedRealTime(startTime);
	int64_t stopTime = (m_timeLimit >= 0) ? (startTime + int64_t(1000.0*m_timeLimit)) : -1;

	//
	// Compute subgraph
	//
	if(m_setTimeout)
		subgraph.timeLimit(m_timeLimit);

	pr.initCC(cc);

	// gather generalization edges, which should all be in the planar subgraph
	List<edge> preferedEdges;
	for(edge e : pr.edges) {
		if (pr.typeOf(e) == Graph::generalization)
			preferedEdges.pushBack(e);
	}

	List<edge> delEdges;
	ReturnType retValue;

	if(pCostOrig) {
		EdgeArray<int> costPG(pr);
		for(edge e : pr.edges)
			costPG[e] = (*pCostOrig)[pr.original(e)];

		retValue = subgraph.call(pr, costPG, preferedEdges, delEdges);

	} else
		retValue = subgraph.call(pr, preferedEdges, delEdges);

	if(isSolution(retValue) == false)
		return retValue;

	const int m = delEdges.size();
	for(ListIterator<edge> it = delEdges.begin(); it.valid(); ++it)
		*it = pr.original(*it);

	//
	// Permutation phase
	//

	int seed = rand();
	minstd_rand rng(seed);

	if(nThreads > 1) {
		//
		// Parallel implementation
		//
		ThreadMaster master(
			pr, cc,
			pCostOrig,
			delEdges,
			seed,
			m_permutations - nThreads,
			stopTime);

		Array<Worker *> worker(nThreads-1);
		Array<Thread> thread(nThreads-1);
		for(unsigned int i = 0; i < nThreads-1; ++i) {
			worker[i] = new Worker(i, &master, inserter.clone());
			thread[i] = Thread(*worker[i]);
		}

		doWorkHelper(master, inserter, rng);

		for(unsigned int i = 0; i < nThreads-1; ++i) {
			thread[i].join();
			delete worker[i];
		}

		master.restore(pr, crossingNumber);

	} else {
		//
		// Sequential implementation
		//
		PlanRepLight prl(pr);

		Array<edge> deletedEdges(m);
		int j = 0;
		for(ListIterator<edge> it = delEdges.begin(); it.valid(); ++it)
			deletedEdges[j++] = *it;

		bool foundSolution = false;
		CrossingStructure cs;
		for(int i = 1; i <= m_permutations; ++i)
		{
			int cr;
			bool ok = doSinglePermutation(prl, cc, pCostOrig, deletedEdges, inserter, rng, cr);

			if(ok && (foundSolution == false || cr < cs.weightedCrossingNumber())) {
				foundSolution = true;
				cs.init(prl, cr);
			}

			if(stopTime >= 0 && System::realTime() >= stopTime) {
				if(foundSolution == false)
					return retTimeoutInfeasible; // not able to find a solution...
				break;
			}
		}

		cs.restore(pr,cc); // restore best solution in PG
		crossingNumber = cs.weightedCrossingNumber();

		OGDF_ASSERT(isPlanar(pr) == true);
	}

	return retFeasible;
}