1 /** \file
2  * \brief Implements class FaceSinkGraph
3  *
4  * \author Carsten Gutwenger
5  *
6  * \par License:
7  * This file is part of the Open Graph Drawing Framework (OGDF).
8  *
9  * \par
10  * Copyright (C)<br>
11  * See README.md in the OGDF root directory for details.
12  *
13  * \par
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * Version 2 or 3 as published by the Free Software Foundation;
17  * see the file LICENSE.txt included in the packaging of this file
18  * for details.
19  *
20  * \par
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * \par
27  * You should have received a copy of the GNU General Public
28  * License along with this program; if not, see
29  * http://www.gnu.org/copyleft/gpl.html
30  */
31 
32 
33 #include <ogdf/upward/FaceSinkGraph.h>
34 
35 
36 namespace ogdf {
37 
38 
39 // construction of face sink graph with cross references
FaceSinkGraph(const ConstCombinatorialEmbedding & E,node s)40 FaceSinkGraph::FaceSinkGraph(
41 	const ConstCombinatorialEmbedding &E, // given embedding
42 	node s) :                             // single source
43 	m_pE            (&E),
44 	m_source        (s),
45 	m_T             (nullptr)
46 {
47 	m_originalNode  .init(*this, nullptr);
48 	m_originalFace  .init(*this, nullptr);
49 	m_containsSource.init(*this, false);
50 	doInit();
51 }
52 
53 
init(const ConstCombinatorialEmbedding & E,node s)54 void FaceSinkGraph::init(
55 	const ConstCombinatorialEmbedding &E, // given embedding
56 	node s)                               // single source
57 {
58 	m_pE     = &E;
59 	m_source = s;
60 	m_T      = nullptr;
61 	m_originalNode  .init(*this,nullptr);
62 	m_originalFace  .init(*this,nullptr);
63 	m_containsSource.init(*this,false);
64 
65 	doInit();
66 }
67 
68 
doInit()69 void FaceSinkGraph::doInit()
70 {
71 	const ConstCombinatorialEmbedding &E = *m_pE;
72 
73 	NodeArray<node> sinkSwitch(E,nullptr); // corresponding node in F (if any)
74 	NodeArray<bool> isSinkSwitch(E,true);
75 
76 	NodeArray<int> visited(E,-1);
77 	int faceNo = -1;
78 	for(face f : E.faces)
79 	{
80 		faceNo++;
81 		node faceNode = newNode();
82 		m_originalFace[faceNode] = f;
83 
84 		SListPure<node> nodesInF;
85 
86 		adjEntry adj1 = f->firstAdj(), adj = adj1;
87 		do {
88 			node v = adj->theNode();
89 			// if the graph is not biconnected, then node v can visited more than once
90 			if (visited[v] != faceNo) {
91 				nodesInF.pushBack(v);
92 				visited[v] = faceNo;
93 			}
94 
95 			if (v == m_source)
96 				m_containsSource[faceNode] = true;
97 
98 			isSinkSwitch[adj->theEdge()->source()] = false;
99 
100 			adj = adj->twin()->cyclicPred();
101 		} while (adj != adj1);
102 
103 		SListConstIterator<node> it;
104 		for(it = nodesInF.begin(); it.valid(); ++it)
105 		{
106 			node v = *it;
107 			if(isSinkSwitch[v])	{
108 				if (sinkSwitch[v] == nullptr) {
109 					node vF = newNode();
110 					m_originalNode[vF] = v;
111 					sinkSwitch[v] = vF;
112 				}
113 
114 				newEdge(faceNode,sinkSwitch[v]);
115 			}
116 		}
117 
118 		for(it = nodesInF.begin(); it.valid(); ++it)
119 			isSinkSwitch[*it] = true;
120 	}
121 }
122 
123 
124 #if 0
125 void FaceSinkGraph::doInit()
126 {
127 	const ConstCombinatorialEmbedding &E = *m_pE;
128 
129 	NodeArray<node> sinkSwitch(E,0); // corresponding node in F (if any)
130 
131 	for(face f : E.faces)
132 	{
133 		node faceNode = newNode();
134 		m_originalFace[faceNode] = f;
135 
136 		adjEntry adj1 = f->firstAdj(), adj = adj1;
137 		adjEntry adjPred = adj->cyclicSucc();
138 		do {
139 			node v = adj->theNode();
140 
141 			if (v == m_source)
142 				m_containsSource[faceNode] = true;
143 
144 			// v is a sink-switch iff both adjacent edges (there are only two
145 			// in f since G is biconnected) are directed towards v
146 			if(adj->theEdge()->target() == v &&
147 				adjPred->theEdge()->target() == v)
148 			{
149 				if (sinkSwitch[v] == 0) {
150 					node vF = newNode();
151 					m_originalNode[vF] = v;
152 					sinkSwitch[v] = vF;
153 				}
154 
155 				newEdge(faceNode,sinkSwitch[v]);
156 			}
157 
158 			adjPred = adj->twin();
159 			adj = adjPred->cyclicPred();
160 		} while (adj != adj1);
161 	}
162 }
163 #endif
164 
165 
166 // checks if F is a forest with
167 //  1) there exactly one tree T containing no internal vertex of G
168 //  2) all other trees contain exactly one internal vertex of G
169 // a node in tree T is returned as representative
checkForest()170 node FaceSinkGraph::checkForest()
171 {
172 	// representative of tree T (0 indicates none found yet)
173 	m_T = nullptr;
174 
175 	// we perform a dfs traversal on F and check if there are backwards edges
176 	// (then F is not a forest)
177 	NodeArray<bool> visited(*this,false);
178 
179 	for(node v : nodes)
180 	{
181 		if (visited[v]) continue;
182 
183 		// number of internal vertices in current tree
184 		int nInternalVertices = 0;
185 		if (dfsCheckForest(v,nullptr,visited,nInternalVertices) == 0)
186 			return nullptr;
187 
188 		// either we have a unique tree with no internal vertices
189 		if(nInternalVertices == 0) {
190 			if(m_T)
191 				return nullptr;
192 			else
193 				m_T = v;
194 
195 		// or we have exactly one internal vertex
196 		} else if (nInternalVertices != 1)
197 			return nullptr;
198 	}
199 
200 	return m_T;
201 }
202 
203 
204 // performs dfs-traversal and checks for backwards edges
dfsCheckForest(node v,node parent,NodeArray<bool> & visited,int & nInternalVertices)205 bool FaceSinkGraph::dfsCheckForest(
206 	node v,                              // current node
207 	node parent,                         // its parent in tree
208 	NodeArray<bool> &visited,            // not already visited ?
209 	int &nInternalVertices) // number of internal vertices of G in current tree
210 {
211 	visited[v] = true;
212 
213 	// check if original node of v is an internal vertex in G
214 	node vOrig = m_originalNode[v];
215 	if(vOrig && vOrig->indeg() >= 1 && vOrig->outdeg() >= 1)
216 		++nInternalVertices;
217 
218 	// iterate over all adjacent nodes of v different from parent
219 	for(adjEntry adj : v->adjEntries)
220 	{
221 		node w = adj->twinNode();
222 
223 		if (w == parent) continue;
224 		if(visited[w]) return false;
225 
226 		if(!dfsCheckForest(w,v,visited,nInternalVertices))
227 			return false;
228 	}
229 
230 	return true;
231 }
232 
233 
234 
235 // builds list of possible external faces (all faces in tree T containing
236 // the single source s) by a dfs traversal of T
gatherExternalFaces(node v,node parent,SList<face> & externalFaces)237 void FaceSinkGraph::gatherExternalFaces(
238 	node v,                                // current node
239 	node parent,                           // its parent
240 	SList<face> &externalFaces)      // returns list of possible external faces
241 {
242 	if (m_containsSource[v])
243 		externalFaces.pushBack(m_originalFace[v]);
244 
245 	// since we already know that T is a tree we can omit the visited array
246 	for(adjEntry adj : v->adjEntries)
247 	{
248 		node w = adj->twinNode();
249 
250 		if (w != parent)
251 			gatherExternalFaces(w,v,externalFaces);
252 	}
253 }
254 
255 
dfsFaceNodeOf(node v,node parent,face f1,face f2)256 node FaceSinkGraph::dfsFaceNodeOf(node v, node parent, face f1, face f2)
257 {
258 	face f = m_originalFace[v];
259 	if (m_containsSource[v] && (f == f1 || f == f2))
260 		return v;
261 
262 	// since we already know that T is a tree we can omit the visited array
263 	for(adjEntry adj : v->adjEntries)
264 	{
265 		node w = adj->twinNode();
266 
267 		if (w != parent) {
268 			node found = dfsFaceNodeOf(w,v,f1,f2);
269 			if (found != nullptr)
270 				return found;
271 		}
272 	}
273 
274 	return nullptr;
275 }
276 
277 
278 // original variant of st-augmentation
279 // Inserts also new nodes representing faces into G.
stAugmentation(node h,Graph & G,SList<node> & augmentedNodes,SList<edge> & augmentedEdges)280 void FaceSinkGraph::stAugmentation(
281 	node h,                       // node corresponding to external face
282 	Graph &G,                     // original graph (not const)
283 	SList<node> &augmentedNodes,  // list of augmented nodes
284 	SList<edge> &augmentedEdges)  // list of augmented edges
285 {
286 	SListPure<node> roots;
287 	for(node v : nodes) {
288 		node vOrig = m_originalNode[v];
289 		if (vOrig != nullptr && vOrig->indeg() > 0 && vOrig->outdeg() > 0)
290 			roots.pushBack(v);
291 	}
292 
293 	node vh = dfsStAugmentation(h,nullptr,G,augmentedNodes,augmentedEdges);
294 
295 	SListConstIterator<node> it;
296 	for(it = roots.begin(); it.valid(); ++it)
297 		dfsStAugmentation(*it,nullptr,G,augmentedNodes,augmentedEdges);
298 
299 	augmentedEdges.pushBack(G.newEdge(m_source,vh));
300 
301 }
302 
303 
dfsStAugmentation(node v,node parent,Graph & G,SList<node> & augmentedNodes,SList<edge> & augmentedEdges)304 node FaceSinkGraph::dfsStAugmentation(
305 	node v,                      // current node
306 	node parent,                 // its parent
307 	Graph &G,                    // original graph (not const)
308 	SList<node> &augmentedNodes, // list of augmented nodes
309 	SList<edge> &augmentedEdges) // list of augmented edges
310 {
311 	bool isFace = (m_originalFace[v] != nullptr);
312 	node vf = nullptr;
313 
314 	// since we already know that T is a tree we can omit the visited array
315 	for(adjEntry adj : v->adjEntries)
316 	{
317 		node w = adj->twinNode();
318 
319 		if (w == parent) continue;
320 
321 		if (isFace) {
322 			if (vf == nullptr) {
323 				vf = G.newNode();
324 				augmentedNodes.pushBack(vf);
325 				if (parent) {
326 					edge eParent = G.newEdge(vf,m_originalNode[parent]);
327 					augmentedEdges.pushBack(eParent);
328 				}
329 			}
330 
331 			edge ew = G.newEdge(m_originalNode[w],vf);
332 			augmentedEdges.pushBack(ew);
333 		}
334 
335 		dfsStAugmentation(w,v,G,augmentedNodes,augmentedEdges);
336 	}
337 
338 	return vf;
339 }
340 
341 
342 // improved variant of st-augmentation
343 // Inserts also one new node representing the super sink into G.
stAugmentation(node h,Graph & G,node & superSink,SList<edge> & augmentedEdges)344 void FaceSinkGraph::stAugmentation(
345 	node  h,                      // node corresponding to external face
346 	Graph &G,                     // original graph (not const)
347 	node  &superSink,             // super sink
348 	SList<edge> &augmentedEdges)  // list of augmented edges
349 {
350 	SListPure<node> roots;
351 	for (node v : nodes) {
352 		node vOrig = m_originalNode[v];
353 		if (vOrig != nullptr && vOrig->indeg() > 0 && vOrig->outdeg() > 0)
354 			roots.pushBack(v);
355 	}
356 
357 
358 	superSink = dfsStAugmentation(h,nullptr,G,augmentedEdges);
359 
360 	SListConstIterator<node> it;
361 	for(it = roots.begin(); it.valid(); ++it)
362 		dfsStAugmentation(*it,nullptr,G,augmentedEdges);
363 
364 	augmentedEdges.pushBack(G.newEdge(m_source,superSink));
365 
366 }
367 
368 
dfsStAugmentation(node v,node parent,Graph & G,SList<edge> & augmentedEdges)369 node FaceSinkGraph::dfsStAugmentation(
370 	node v,                      // current node
371 	node parent,                 // its parent
372 	Graph &G,                    // original graph (not const)
373 	SList<edge> &augmentedEdges) // list of augmented edges
374 {
375 	bool isFace = (m_originalFace[v] != nullptr);
376 	node vf = (parent != nullptr) ? m_originalNode[parent] : nullptr;
377 
378 	// since we already know that T is a tree we can omit the visited array
379 	for(adjEntry adj : v->adjEntries)
380 	{
381 		node w = adj->twinNode();
382 
383 		if (w == parent) continue;
384 
385 		if (isFace) {
386 			if (vf == nullptr) {
387 				vf = G.newNode();
388 			}
389 
390 			edge ew = G.newEdge(m_originalNode[w],vf);
391 			augmentedEdges.pushBack(ew);
392 		}
393 
394 		dfsStAugmentation(w,v,G,augmentedEdges);
395 	}
396 
397 	return vf;
398 }
399 
400 
401 
sinkSwitches(FaceArray<List<adjEntry>> & faceSwitches)402 void FaceSinkGraph::sinkSwitches(FaceArray< List<adjEntry> > &faceSwitches) {
403 	OGDF_ASSERT(m_pE->externalFace() != nullptr);
404 
405 	List<adjEntry> dummyList;
406 	faceSwitches.init(*m_pE, dummyList);
407 
408 	NodeArray<bool> visited(m_pE->getGraph(), false);
409 	List<face> toDo;
410 	FaceArray<bool> faceDone(*m_pE, false);
411 
412 #if 0
413 	m_pE->getGraph().writeGML("c:/temp/debug.gml");
414 #endif
415 
416 	//compute sink-switches for the ext. face
417 	for(adjEntry adj : m_pE->externalFace()->entries)
418 	{
419 		node u = adj->theNode();
420 		if (u->outdeg() == 0 && !visited[u])
421 			faceSwitches[m_pE->externalFace()].pushBack(adj);
422 
423 		if (u->indeg() > 1 && !visited[u]) {
424 			List<edge> outEdges;
425 			u->outEdges(outEdges);
426 			if (outEdges.empty()) {
427 				for(adjEntry run : u->adjEntries) {
428 					if (m_pE->rightFace(run) != m_pE->externalFace())
429 						toDo.pushBack(m_pE->rightFace(run));
430 				}
431 
432 			}
433 			else {
434 				edge e = outEdges.front();
435 				adjEntry run = e->adjSource();
436 				run = run->cyclicSucc();
437 				while (run->theEdge() != e) {
438 					adjEntry next = run->cyclicSucc();
439 					if (next->theEdge()->target() == u && run->theEdge()->target() == u)
440 						toDo.pushBack(m_pE->rightFace(run));
441 					run = run->cyclicSucc();
442 				}
443 			}
444 		}
445 		visited[u] = true;
446 	}
447 
448 	faceDone[m_pE->externalFace()] = true;
449 
450 	while (!toDo.empty()) {
451 		face f = toDo.popFrontRet();
452 		if (faceDone[f])
453 			continue;
454 
455 		for(adjEntry adj : f->entries) {
456 			node u = adj->theNode();
457 			if (visited[u] && adj->theEdge()->target() == adj->faceCyclePred()->theEdge()->target()
458 					&& m_pE->rightFace(adj) != m_pE->leftFace(adj))
459 				faceSwitches[f].pushFront(adj); // the top sink switch of f
460 
461 			else {
462 				if (u->outdeg() == 0)
463 					faceSwitches[f].pushBack(adj); // the non top sink switch of f
464 			}
465 
466 
467 			if (u->indeg() > 1) {
468 				List<edge> outEdges;
469 				u->outEdges(outEdges);
470 				if (outEdges.empty()) {
471 					for(adjEntry run : u->adjEntries) {
472 						if (m_pE->rightFace(run) != f)
473 							toDo.pushBack(m_pE->rightFace(run));
474 					}
475 				}
476 				else {
477 					edge e = outEdges.front();
478 					adjEntry run = e->adjSource();
479 					run = run->cyclicSucc();
480 					while (run->theEdge() != e) {
481 						adjEntry next = run->cyclicSucc();
482 						if (next->theEdge()->target() == u && run->theEdge()->target() == u)
483 							toDo.pushBack(m_pE->rightFace(run));
484 						run = run->cyclicSucc();
485 					}
486 				}
487 			}
488 			visited[u] = true;
489 		}
490 		faceDone[f] = true;
491 
492 		OGDF_ASSERT(!faceSwitches[f].empty());
493 	}
494 
495 #if 0
496 	std::cout << std::endl;
497 	std::cout << "switche (FaceSinkGraph::sinkSwitches) : " << std::endl;
498 	for(face f : m_pE->faces) {
499 		std::cout << "face : " << f->index() << std::endl;
500 		const List<adjEntry> &adjList = faceSwitches[f];
501 		for(adjEntry adj : adjList) {
502 			std::cout << adj->theNode() << ";   ";
503 		}
504 		std::cout << std::endl;
505 	}
506 #endif
507 }
508 
509 }
510