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