Example #1
0
	void FixEdgeInserterCore::removeEdge(CombinatorialEmbedding &E, edge eOrig)
	{
		const List<edge> &path = m_pr.chain(eOrig);
		for(edge e : path)
		{
			adjEntry adj = e->adjSource();
			m_delFaces->insert(E.leftFace  (adj));
			m_delFaces->insert(E.rightFace (adj));
		}

		// delete all corresponding nodes in dual
		for(face f : m_delFaces->faces())
			m_dual.delNode(m_nodeOf[f]);

		m_delFaces->clear();

		// remove edge path from PG
		m_pr.removeEdgePathEmbedded(E,eOrig,*m_newFaces);

		// update dual
		// insert new nodes
		for(face f : m_newFaces->faces()) {
			m_nodeOf[f] = m_dual.newNode();
		}

		// insert new edges into dual
		for(face f : m_newFaces->faces())
			insertEdgesIntoDualAfterRemove(E, f);

		m_newFaces->clear();
	}
Example #2
0
	//---------------------------------------------------------
	// construct dual graph
	// assumes that m_pDual, m_primalAdj and m_nodeOf are already constructed
	//
	void FixEdgeInserterCore::constructDual(const CombinatorialEmbedding &E)
	{
		// insert a node in the dual graph for each face in E
		for(face f : E.faces)
			m_nodeOf[f] = m_dual.newNode();


		// Insert an edge into the dual graph for each adjacency entry in E.
		// The edges are directed from the left face to the right face.
		for(node v : m_pr.nodes)
		{
			for(adjEntry adj : v->adjEdges)
			{
				// Do not insert edges into dual if crossing the original edge
				// is forbidden
				if(m_pForbidden && (*m_pForbidden)[m_pr.original(adj->theEdge())] == true)
					continue;

				node vLeft  = m_nodeOf[E.leftFace (adj)];
				node vRight = m_nodeOf[E.rightFace(adj)];

				m_primalAdj[m_dual.newEdge(vLeft,vRight)] = adj;
			}
		}

		// Augment the dual graph by two new vertices. These are used temporarily
		// when searching for a shortest path in the dual graph.
		m_vS = m_dual.newNode();
		m_vT = m_dual.newNode();
	}
Example #3
0
	//---------------------------------------------------------
	// construct dual graph, marks dual edges corresponding to generalization
	// in m_primalIsGen
	// assumes that m_pDual, m_primalAdj and m_nodeOf are already constructed
	//
	void FixEdgeInserterUMLCore::constructDual(const CombinatorialEmbedding &E)
	{
		// insert a node in the dual graph for each face in E
		for(face f : E.faces)
			m_nodeOf[f] = m_dual.newNode();


		// Insert an edge into the dual graph for each adjacency entry in E.
		// The edges are directed from the left face to the right face.
		for(node v : m_pr.nodes)
		{
			for(adjEntry adj : v->adjEdges)
			{
				node vLeft  = m_nodeOf[E.leftFace (adj)];
				node vRight = m_nodeOf[E.rightFace(adj)];

				edge e = m_dual.newEdge(vLeft,vRight);
				m_primalAdj[e] = adj;

				// mark dual edges corresponding to generalizations
				if (m_pr.typeOf(adj->theEdge()) == Graph::generalization)
					m_primalIsGen[e] = true;
			}
		}

		// Augment the dual graph by two new vertices. These are used temporarily
		// when searching for a shortest path in the dual graph.
		m_vS = m_dual.newNode();
		m_vT = m_dual.newNode();
	}
Example #4
0
//is only called when CC not connected => m_eTreeArray is initialized
void PlanRepInc::deleteTreeConnection(int i, int j, CombinatorialEmbedding &E)
{

	edge e = m_eTreeArray(i, j);
	if (e == nullptr) return;
	edge nexte = nullptr;
	OGDF_ASSERT(e);
	OGDF_ASSERT(m_treeEdge[e]);
	//we have to take care of treeConnection edges that
	//are already crossed
	while ((e->target()->degree() == 4) &&
		m_treeEdge[e->adjTarget()->cyclicSucc()->cyclicSucc()->theEdge()])
	{
		nexte = e->adjTarget()->cyclicSucc()->cyclicSucc()->theEdge();
		OGDF_ASSERT(original(nexte) == 0)
		E.joinFaces(e);
		e = nexte;
	}
	E.joinFaces(e);
	m_eTreeArray(i, j) = nullptr;
	m_eTreeArray(j, i) = nullptr;

	OGDF_ASSERT(isConnected(*this));

}//deleteTreeConnection
Example #5
0
UpwardPlanRep::UpwardPlanRep(const CombinatorialEmbedding &Gamma) :
	GraphCopy(Gamma.getGraph()),
	isAugmented(false),
	t_hat(nullptr),
	extFaceHandle(nullptr),
	crossings(0)
{
	OGDF_ASSERT(Gamma.externalFace() != nullptr);
	OGDF_ASSERT(hasSingleSource(*this));
	OGDF_ASSERT(isSimple(*this));

	m_isSourceArc.init(*this, false);
	m_isSinkArc.init(*this, false);
	hasSingleSource(*this, s_hat);
	m_Gamma.init(*this);

	//compute the ext. face;
	adjEntry adj;
	node v = this->original(s_hat);
	adj = getAdjEntry(Gamma, v, Gamma.externalFace());
	adj = this->copy(adj->theEdge())->adjSource();
	m_Gamma.setExternalFace(m_Gamma.rightFace(adj));

	//outputFaces(Gamma);

	computeSinkSwitches();
}
Example #6
0
	face SimpleEmbedder::findBestExternalFace(
		const PlanRep& PG,
		const CombinatorialEmbedding& E)
	{
		FaceArray<int> weight(E);

		for(face f : E.faces)
			weight[f] = f->size();

		for(node v : PG.nodes)
		{
			if(PG.typeOf(v) != Graph::generalizationMerger)
				continue;

			adjEntry adjFound = nullptr;
			for(adjEntry adj : v->adjEdges) {
				if (adj->theEdge()->source() == v) {
					adjFound = adj;
					break;
				}
			}

			OGDF_ASSERT(adjFound->theEdge()->source() == v);

			node w = adjFound->theEdge()->target();
			bool isBase = true;

			for(adjEntry adj : w->adjEdges) {
				edge e = adj->theEdge();
				if(e->target() != w && PG.typeOf(e) == Graph::generalization) {
					isBase = false;
					break;
				}
			}

			if(isBase == false)
				continue;

			face f1 = E.leftFace(adjFound);
			face f2 = E.rightFace(adjFound);

			weight[f1] += v->indeg();
			if(f2 != f1)
				weight[f2] += v->indeg();
		}

		face fBest = E.firstFace();
		for(face f : E.faces)
			if(weight[f] > weight[fBest])
				fBest = f;

		return fBest;
	}
Example #7
0
adjEntry UpwardPlanRep::getAdjEntry(const CombinatorialEmbedding &Gamma, node v, face f) const {
	adjEntry adjFound = nullptr;
	for(adjEntry adj : v->adjEntries) {
		if (Gamma.rightFace(adj) == f) {
			adjFound = adj;
			break;
		}
	}
	OGDF_ASSERT(adjFound != nullptr);
	OGDF_ASSERT(Gamma.rightFace(adjFound) == f);

	return adjFound;
}
Example #8
0
	face SimpleEmbedder::findBestExternalFace(const PlanRep& PG,
	                                          const CombinatorialEmbedding& E)
	{
		FaceArray<int> weight(E);

		face f;
		forall_faces(f,E)
			weight[f] = f->size();

		node v;
		forall_nodes(v,PG)
		{
			if(PG.typeOf(v) != Graph::generalizationMerger)
				continue;

			adjEntry adj;
			forall_adj(adj,v) {
				if(adj->theEdge()->source() == v)
					break;
			}

			OGDF_ASSERT(adj->theEdge()->source() == v);

			node w = adj->theEdge()->target();
			bool isBase = true;

			adjEntry adj2;
			forall_adj(adj2, w) {
				edge e = adj2->theEdge();
				if(e->target() != w && PG.typeOf(e) == Graph::generalization) {
					isBase = false;
					break;
				}
			}

			if(isBase == false)
				continue;

			face f1 = E.leftFace(adj);
			face f2 = E.rightFace(adj);

			weight[f1] += v->indeg();
			if(f2 != f1)
				weight[f2] += v->indeg();
		}
Example #9
0
void CPlanarEdgeInserter::constructDualGraph(ClusterPlanRep& CPR,
											 CombinatorialEmbedding& E,
											 EdgeArray<edge>& arcRightToLeft,
											 EdgeArray<edge>& arcLeftToRight,
		 									 FaceArray<node>& nodeOfFace,
											 //NodeArray<face>&, faceOfNode,
											 EdgeArray<edge>& arcTwin)
{
	//dual graph gets two arcs for each edge (in both directions)
	//these arcs get their status (usable for path) depending on
	//the edge to be reinserted

	m_dualGraph.clear();
	//faceOfNode.init(m_dualGraph, 0);

	//*********************************
	//construct nodes
	//corresponding to the graphs faces
	face f;
	for (f = E.firstFace(); f; f = f->succ())
	{
		node v = m_dualGraph.newNode();
		nodeOfFace[f] = v;
		//faceOfNode[v] = f;
	}

	//*********************************
	//
	edge e;
	forall_edges(e, CPR)
	{
		edge arc1 = m_dualGraph.newEdge( nodeOfFace[E.rightFace(e->adjTarget())],
			nodeOfFace[E.rightFace(e->adjSource())] );
		arcLeftToRight[e] = arc1;
		edge arc2 = m_dualGraph.newEdge( nodeOfFace[E.rightFace(e->adjSource())],
			nodeOfFace[E.rightFace(e->adjTarget())] );
		arcRightToLeft[e] = arc2;
		arcTwin[arc1] = arc2;
		arcTwin[arc2] = arc1;
		m_arcOrig[arc1] = e->adjSource();//e->adjTarget();
		m_arcOrig[arc2] = e->adjTarget();//e->adjSource();
	}
Example #10
0
	void FixEdgeInserterUMLCore::insertEdgesIntoDual(const CombinatorialEmbedding &E, adjEntry adjSrc)
	{
		face f = E.rightFace(adjSrc);
		node vRight = m_nodeOf[f];

		adjEntry adj1 = f->firstAdj(), adj = adj1;
		do {
			node vLeft = m_nodeOf[E.leftFace(adj)];

			edge eLR = m_dual.newEdge(vLeft,vRight);
			m_primalAdj[eLR] = adj;

			edge eRL = m_dual.newEdge(vRight,vLeft);
			m_primalAdj[eRL] = adj->twin();

			if(m_pr.typeOf(adj->theEdge()) == Graph::generalization)
				m_primalIsGen[eLR] = m_primalIsGen[eRL] = true;
		}
		while((adj = adj->faceCycleSucc()) != adj1);

		// the other face adjacent to *itEdge ...
		f = E.rightFace(adjSrc->twin());
		vRight = m_nodeOf[f];

		adj1 = f->firstAdj();
		adj = adj1;
		do {
			node vLeft = m_nodeOf[E.leftFace(adj)];

			edge eLR = m_dual.newEdge(vLeft,vRight);
			m_primalAdj[eLR] = adj;

			edge eRL = m_dual.newEdge(vRight,vLeft);
			m_primalAdj[eRL] = adj->twin();

			if(m_pr.typeOf(adj->theEdge()) == Graph::generalization)
				m_primalIsGen[eLR] = m_primalIsGen[eRL] = true;
		}
		while((adj = adj->faceCycleSucc()) != adj1);
	}
Example #11
0
	void FixEdgeInserterCore::insertEdgesIntoDual(const CombinatorialEmbedding &E, adjEntry adjSrc)
	{
		face f = E.rightFace(adjSrc);
		node vRight = m_nodeOf[f];

		adjEntry adj1 = f->firstAdj(), adj = adj1;
		do {
			if(m_pForbidden && (*m_pForbidden)[m_pr.original(adj->theEdge())] == true)
				continue;

			node vLeft = m_nodeOf[E.leftFace(adj)];

			edge eLR = m_dual.newEdge(vLeft,vRight);
			m_primalAdj[eLR] = adj;

			edge eRL = m_dual.newEdge(vRight,vLeft);
			m_primalAdj[eRL] = adj->twin();
		}
		while((adj = adj->faceCycleSucc()) != adj1);

		// the other face adjacent to *itEdge ...
		f = E.rightFace(adjSrc->twin());
		vRight = m_nodeOf[f];

		adj1 = f->firstAdj();
		adj = adj1;
		do {
			if(m_pForbidden && (*m_pForbidden)[m_pr.original(adj->theEdge())] == true)
				continue;

			node vLeft = m_nodeOf[E.leftFace(adj)];

			edge eLR = m_dual.newEdge(vLeft,vRight);
			m_primalAdj[eLR] = adj;

			edge eRL = m_dual.newEdge(vRight,vLeft);
			m_primalAdj[eRL] = adj->twin();
		}
		while((adj = adj->faceCycleSucc()) != adj1);
	}
Example #12
0
	void FixEdgeInserterCore::insertEdge(CombinatorialEmbedding &E, edge eOrig, const SList<adjEntry> &crossed)
	{
		// remove dual nodes on insertion path
		SListConstIterator<adjEntry> it;
		for(it = crossed.begin(); it != crossed.rbegin(); ++it) {
			m_dual.delNode(m_nodeOf[E.rightFace(*it)]);
		}

		// update primal
		m_pr.insertEdgePathEmbedded(eOrig,E,crossed);

		// insert new face nodes into dual
		const List<edge> &path = m_pr.chain(eOrig);
		for(edge e : path)
		{
			adjEntry adj = e->adjSource();
			m_nodeOf[E.leftFace (adj)] = m_dual.newNode();
			m_nodeOf[E.rightFace(adj)] = m_dual.newNode();
		}

		// insert new edges into dual
		for(edge e : path)
			insertEdgesIntoDual(E, e->adjSource());
	}
Example #13
0
	void FixEdgeInserterUMLCore::insertEdgesIntoDualAfterRemove(const CombinatorialEmbedding &E, face f)
	{
		node vRight = m_nodeOf[f];

		adjEntry adj1 = f->firstAdj(), adj = adj1;
		do {
			node vLeft = m_nodeOf[E.leftFace(adj)];

			edge eLR = m_dual.newEdge(vLeft,vRight);
			m_primalAdj[eLR] = adj;

			edge eRL = m_dual.newEdge(vRight,vLeft);
			m_primalAdj[eRL] = adj->twin();

			if(m_pr.typeOf(adj->theEdge()) == Graph::generalization)
			{
				m_primalIsGen[eLR] = m_primalIsGen[eRL] = true;
			}
		}
		while((adj = adj->faceCycleSucc()) != adj1);
	}
Example #14
0
// Computes combinatorial embedding of dual graph
// Precondition: CE must be combinatorial embedding of connected planar graph
DualGraph::DualGraph(CombinatorialEmbedding &CE)
{
    m_primalEmbedding = &CE;
    Graph &primalGraph = CE.getGraph();
    init(*(new Graph));
    Graph &dualGraph = getGraph();
    
    m_dualNode.init(CE);
    m_dualEdge.init(primalGraph);
    m_dualFace.init(primalGraph);
    m_primalNode.init(*this);
    m_primalFace.init(dualGraph);
    m_primalEdge.init(dualGraph);
    
    // create dual nodes
    face f;
    forall_faces(f, CE)
    {
		node vDual = dualGraph.newNode();
		m_dualNode[f] = vDual;
		m_primalFace[vDual] = f;
    }
/**---------------------------------------------------
calling function taking the planar representation, the
external face (adjentry), the layout to be filled,
a list of non-planar edges, a list of inserted edges
and the original graph as input
*/
void ClusterOrthoLayout::call(ClusterPlanRep &PG,
	adjEntry adjExternal,
	Layout &drawing,
	List<NodePair>& npEdges,
	List<edge>& newEdges,
	Graph& originalGraph)
{
	// We don't care about UML hierarchies and therefore do not allow alignment
	OGDF_ASSERT(!m_align);

	// if we have only one vertex in PG ...
	if(PG.numberOfNodes() == 1) {
		node v1 = PG.firstNode();
		node vOrig = PG.original(v1);
		double w = PG.widthOrig(vOrig);
		double h = PG.heightOrig(vOrig);

		drawing.x(v1) = m_margin + w/2;
		drawing.y(v1) = m_margin + h/2;
		m_boundingBox = DPoint(w + 2*m_margin, h + 2*m_margin);
		return;
	}

	OGDF_ASSERT(PG.representsCombEmbedding())
	//-------------------------
	// insert cluster boundaries
	PG.ModelBoundaries();
	OGDF_ASSERT(PG.representsCombEmbedding())


	//--------------------------
	// insert non-planar edges
	CombinatorialEmbedding* CE = new CombinatorialEmbedding(PG);
	if (!npEdges.empty())
	{
		CPlanarEdgeInserter CEI;
		CEI.call(PG, *CE, originalGraph, npEdges, newEdges);
	}//if

	//------------------------------------------------------------
	// now we set the external face, currently to the largest face
	adjEntry extAdj = 0;
	int maximum = 0;
	edge e, eSucc;

	for(e = PG.firstEdge(); e; e = eSucc)
	{
		eSucc = e->succ();
		if ( PG.clusterOfEdge(e) == PG.getClusterGraph().rootCluster() )
		{
			int asSize = CE->rightFace(e->adjSource())->size();
			if ( asSize > maximum)
			{
				maximum = asSize;
				extAdj = e->adjSource();
			}
			int atSize = CE->rightFace(e->adjTarget())->size();
			if ( atSize > maximum)
			{
				maximum = atSize;
				extAdj = e->adjTarget();
			}

		}//if root edge

	}//for

	delete CE;

	//returns adjEntry in rootcluster
	adjExternal = extAdj;
	OGDF_ASSERT(adjExternal != 0);


	//----------------------------------------------------------
	//Compaction scaling: help node cages to pass by each other:
	//First, the layout is blown up and then shrunk again in several steps
	//We change the separation value and save the original value.
	double l_orsep = m_separation;
	if (m_useScalingCompaction)
	{
		double scaleFactor = double(int(1 << m_scalingSteps));
		m_separation = scaleFactor*m_separation; //reduce this step by step in compaction
	}//if scaling

	//***********************************
	// PHASE 1: determine orthogonal shape

	//-------------------------------------------------------
	// expand high-degree vertices and generalization mergers
	PG.expand();

	// get combinatorial embedding
	CombinatorialEmbedding E(PG);
	E.setExternalFace(E.rightFace(adjExternal));

	// orthogonal shape representation
	OrthoRep OR;

	ClusterOrthoShaper COF;

	//set some options
	COF.align(false); //cannot be used yet with clusters
	COF.traditional(m_orthoStyle > 0 ? false : true); //prefer 90/270 degree angles over 180/180
	//bend cost depends on cluster depths avoiding unnecessary "inner" bends
	COF.bendCostTopDown(ClusterOrthoShaper::topDownCost);

	// New Call
	//COF.call(PG,E,OR,2);
	// Original call without bend bounds(still valid)
	COF.call(PG, E, OR);

	String msg;
	OGDF_ASSERT(OR.check(msg))

	//******************************************************************
	// PHASE 2: construction of a feasible drawing of the expanded graph

	//---------------------------
	// expand low degree vertices
	PG.expandLowDegreeVertices(OR);

	OGDF_ASSERT(PG.representsCombEmbedding());

	//------------------
	// restore embedding
	E.computeFaces();
	E.setExternalFace(E.rightFace(adjExternal));

	OGDF_ASSERT(OR.check(msg))

	//----------
	//COMPACTION

	//--------------------------
	// apply constructive compaction heuristics
	OR.normalize();
	OR.dissect();

	OR.orientate(PG,m_preferedDir);

	OGDF_ASSERT(OR.check(msg))

	// compute cage information and routing channels
	OR.computeCageInfoUML(PG);
	//temporary grid layout
	GridLayoutMapped gridDrawing(PG,OR,m_separation,m_cOverhang,4);

	RoutingChannel<int> rcGrid(PG,gridDrawing.toGrid(m_separation),m_cOverhang);
	rcGrid.computeRoutingChannels(OR, m_align);

	node v;
	const OrthoRep::VertexInfoUML *pInfoExp;
	forall_nodes(v,PG) {
		pInfoExp = OR.cageInfo(v);

		if (pInfoExp) break;
	}
Example #16
0
	void FixEdgeInserterCore::findWeightedShortestPath(const CombinatorialEmbedding &E, edge eOrig, SList<adjEntry> &crossed)
	{
		node s = m_pr.copy(eOrig->source());
		node t = m_pr.copy(eOrig->target());
		OGDF_ASSERT(s != t);

		int eSubgraph = (m_pSubgraph != nullptr) ? (*m_pSubgraph)[eOrig] : 0;

		EdgeArray<int> costDual(m_dual, 0);
		int maxCost = 0;
		for(edge eDual : m_dual.edges) {
			int c = getCost(m_primalAdj[eDual]->theEdge(), eSubgraph);
			costDual[eDual] = c;
			if (c > maxCost)
				maxCost = c;
		}

		++maxCost;
		Array<SListPure<edge> > nodesAtDist(maxCost);

		NodeArray<edge> spPred(m_dual,nullptr);

		int oldIdCount = m_dual.maxEdgeIndex();

		// augment dual by edges from s to all adjacent faces of s ...
		for(adjEntry adj : s->adjEdges) {
			// starting edges of bfs-search are all edges leaving s
			edge eDual = m_dual.newEdge(m_vS, m_nodeOf[E.rightFace(adj)]);
			m_primalAdj[eDual] = adj;
			nodesAtDist[0].pushBack(eDual);
		}

		// ... and from all adjacent faces of t to t
		for(adjEntry adj : t->adjEdges) {
			edge eDual = m_dual.newEdge(m_nodeOf[E.rightFace(adj)], m_vT);
			m_primalAdj[eDual] = adj;
		}

		// actual search (using extended bfs on directed dual)
		int currentDist = 0;

		for( ; ; )
		{
			// next candidate edge
			while(nodesAtDist[currentDist % maxCost].empty())
				++currentDist;

			edge eCand = nodesAtDist[currentDist % maxCost].popFrontRet();
			node v = eCand->target();

			// leads to an unvisited node?
			if (spPred[v] == nullptr)
			{
				// yes, then we set v's predecessor in search tree
				spPred[v] = eCand;

				// have we reached t ...
				if (v == m_vT)
				{
					// ... then search is done.
					// constructed list of used edges (translated to crossed
					// adjacency entries in PG) from t back to s (including first
					// and last!)

					do {
						edge eDual = spPred[v];
						crossed.pushFront(m_primalAdj[eDual]);
						v = eDual->source();
					} while(v != m_vS);

					break;
				}

				// append next candidate edges to queue (all edges leaving v)
				appendCandidates(nodesAtDist, costDual, maxCost, v, currentDist);
			}
		}

		// remove augmented edges again
		adjEntry adj;
		while ((adj = m_vS->firstAdj()) != nullptr)
			m_dual.delEdge(adj->theEdge());

		while ((adj = m_vT->firstAdj()) != nullptr)
			m_dual.delEdge(adj->theEdge());

		m_dual.resetEdgeIdCount(oldIdCount);
	}
Example #17
0
	void FixEdgeInserterCore::findShortestPath(const CombinatorialEmbedding &E, edge eOrig, SList<adjEntry> &crossed)
	{
		node s = m_pr.copy(eOrig->source());
		node t = m_pr.copy(eOrig->target());
		OGDF_ASSERT(s != t);

		NodeArray<edge> spPred(m_dual,nullptr);
		QueuePure<edge> queue;
		int oldIdCount = m_dual.maxEdgeIndex();

		// augment dual by edges from s to all adjacent faces of s ...
		for(adjEntry adj : s->adjEdges) {
			// starting edges of bfs-search are all edges leaving s
			edge eDual = m_dual.newEdge(m_vS, m_nodeOf[E.rightFace(adj)]);
			m_primalAdj[eDual] = adj;
			queue.append(eDual);
		}

		// ... and from all adjacent faces of t to t
		for(adjEntry adj : t->adjEdges) {
			edge eDual = m_dual.newEdge(m_nodeOf[E.rightFace(adj)], m_vT);
			m_primalAdj[eDual] = adj;
		}

		// actual search (using bfs on directed dual)
		for( ; ;)
		{
			// next candidate edge
			edge eCand = queue.pop();
			node v = eCand->target();

			// leads to an unvisited node?
			if (spPred[v] == nullptr)
			{
				// yes, then we set v's predecessor in search tree
				spPred[v] = eCand;

				// have we reached t ...
				if (v == m_vT)
				{
					// ... then search is done.
					// constructed list of used edges (translated to crossed
					// adjacency entries in PG) from t back to s (including first
					// and last!)

					do {
						edge eDual = spPred[v];
						crossed.pushFront(m_primalAdj[eDual]);
						v = eDual->source();
					} while(v != m_vS);

					break;
				}

				// append next candidate edges to queue (all edges leaving v)
				appendCandidates(queue, v);
			}
		}


		// remove augmented edges again
		adjEntry adj;
		while ((adj = m_vS->firstAdj()) != nullptr)
			m_dual.delEdge(adj->theEdge());

		while ((adj = m_vT->firstAdj()) != nullptr)
			m_dual.delEdge(adj->theEdge());

		m_dual.resetEdgeIdCount(oldIdCount);
	}