1 /** \file
2 * \brief implementation of the class BoyerMyrvoldPlanar
3 *
4 * \author Jens Schmidt
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/planarity/boyer_myrvold/BoyerMyrvoldPlanar.h>
34 #include <ogdf/planarity/boyer_myrvold/BoyerMyrvoldInit.h>
35 #include <ogdf/planarity/boyer_myrvold/FindKuratowskis.h>
36
37
38 namespace ogdf {
39
40 const int BoyerMyrvoldPlanar::DirectionCCW = 0;
41 const int BoyerMyrvoldPlanar::DirectionCW = 1;
42
43 // constructor
BoyerMyrvoldPlanar(Graph & g,bool bundles,int embeddingGrade,bool limitStructures,SListPure<KuratowskiStructure> & output,double randomness,bool avoidE2Minors,bool extractSubgraph,const EdgeArray<int> * edgeCosts)44 BoyerMyrvoldPlanar::BoyerMyrvoldPlanar(
45 Graph& g,
46 bool bundles,
47 int embeddingGrade, // see enumeration EmbeddingGrade for options
48 bool limitStructures, // limits number of structures to embeddingGrade
49 SListPure<KuratowskiStructure>& output,
50 double randomness, // creates a random DFS-Tree, if in [0,1[
51 // a value of 1 will always chose the edges with highest cost not be deleted
52 bool avoidE2Minors, // avoids multiple identical minors (type AE2/E2)
53 bool extractSubgraph, // will extract planar subgraph
54 const EdgeArray<int> *edgeCosts) // costs for removing each edge
55 :
56 m_g(g),
57 m_bundles(bundles),
58 m_embeddingGrade(embeddingGrade),
59 m_limitStructures(limitStructures),
60 m_randomness(randomness),
61 m_avoidE2Minors(avoidE2Minors),
62 m_edgeCosts(edgeCosts),
63 m_extractSubgraph(extractSubgraph),
64
65 // BoyerMyrvoldInit members
66 m_realVertex(g,nullptr),
67 m_dfi(g,0),
68 m_nodeFromDFI(-g.numberOfNodes(),g.numberOfNodes(),nullptr),
69 m_adjParent(g,nullptr),
70 m_leastAncestor(g), // doesn't need initialization
71 m_edgeType(g,BoyerMyrvoldEdgeType::Undefined),
72 m_lowPoint(g), // doesn't need initialization
73 m_separatedDFSChildList(g),
74 m_pNodeInParent(g), // doesn't need initialization
75
76 // Walkup & Walkdown members
77 m_visited(g,0),
78 m_flipped(g,false),
79 m_backedgeFlags(g),
80 m_pertinentRoots(g),
81 m_output(output)
82 {
83 m_rand.seed(rand());
84 m_link[BoyerMyrvoldPlanar::DirectionCCW].init(g,nullptr);
85 m_link[BoyerMyrvoldPlanar::DirectionCW].init(g,nullptr);
86 m_beforeSCE[BoyerMyrvoldPlanar::DirectionCCW].init(g,nullptr);
87 m_beforeSCE[BoyerMyrvoldPlanar::DirectionCW].init(g,nullptr);
88 m_output.clear();
89 // apply this only, if FIND-procedure will be called
90 if (m_embeddingGrade > EmbeddingGrade::doNotFind) {
91 m_pointsToRoot.init(g,nullptr);
92 m_visitedWithBackedge.init(g,nullptr);
93 m_numUnembeddedBackedgesInBicomp.init(g,0);
94 m_highestSubtreeDFI.init(g); // doesn't need initialization
95 }
96 m_flippedNodes = 0;
97 }
98
99
100 // walk upon external face in the given direction, see getSucessorOnExternalFace
101 // the difference is, that all inactive vertices are skipped, i.e. the returned node
102 // is active in relation to the node with dfi v
103 // in the special case of degree-one nodes the direction is not changed
104 // info returns the dynamic nodetype of the endnode
activeSuccessor(node w,int & direction,int v,int & info) const105 node BoyerMyrvoldPlanar::activeSuccessor(node w, int& direction, int v, int& info) const
106 {
107 OGDF_ASSERT(w!=nullptr);
108 OGDF_ASSERT(w->degree()>0);
109 OGDF_ASSERT(m_link[BoyerMyrvoldPlanar::DirectionCW][w]!=nullptr);
110 OGDF_ASSERT(m_link[BoyerMyrvoldPlanar::DirectionCCW][w]!=nullptr);
111 node next;
112 adjEntry adj;
113
114 do {
115 adj = m_link[direction][w];
116 next = adj->theNode();
117 OGDF_ASSERT(next!=nullptr);
118 OGDF_ASSERT(next->degree()>0);
119 OGDF_ASSERT(m_link[BoyerMyrvoldPlanar::DirectionCW][next]!=nullptr);
120 OGDF_ASSERT(m_link[BoyerMyrvoldPlanar::DirectionCCW][next]!=nullptr);
121
122 if (w->degree() > 1)
123 direction = adj==beforeShortCircuitEdge(next,BoyerMyrvoldPlanar::DirectionCCW)->twin();
124 w=next;
125 info = infoAboutNode(next,v);
126
127 } while (info==0); // until not inactive
128 return next;
129 }
130
131 // merges adjEntries of virtual node w and associated real vertex x according to
132 // given outgoing directions x_dir and w_dir.
mergeBiconnectedComponent(ArrayBuffer<int> & stack)133 void BoyerMyrvoldPlanar::mergeBiconnectedComponent(ArrayBuffer<int>& stack) {
134 bool doEmbed = m_embeddingGrade != EmbeddingGrade::doNotEmbed;
135
136 const int w_dir = stack.popRet(); // outgoing direction of w
137 const int x_dir = stack.popRet(); // outgoing direction of x
138 int tmp = stack.popRet();
139 const node w = m_nodeFromDFI[tmp]; // virtual DFS-Successor of x
140 const node w_child = m_nodeFromDFI[-tmp]; // real unique DFS-Child of bicomp with root w
141 const node x = m_realVertex[w];
142
143 // set new external face neighbors and save adjEntry, where edges will be merged
144 adjEntry mergeEntry = nullptr;
145 Direction dir;
146 if (doEmbed) {
147 dir = (x_dir == BoyerMyrvoldPlanar::DirectionCCW) ? Direction::before : Direction::after;
148 mergeEntry = beforeShortCircuitEdge(x, !x_dir)->twin();
149 }
150 m_link[!x_dir][x] = m_link[!w_dir][w];
151 m_beforeSCE[!x_dir][x] = m_beforeSCE[!w_dir][w];
152
153 // merge real and virtual nodes, flip biconnected component root if neccesary
154 // not neccesary if onlyPlanar
155 OGDF_ASSERT(!m_flipped[w_child]);
156 adjEntry adj = w->firstAdj();
157 if (doEmbed) {
158 if (x_dir == w_dir) {
159 // if not flipped
160 if (dir == Direction::after) {
161 mergeEntry = mergeEntry->cyclicSucc();
162 dir = Direction::before;
163 }
164 } else {
165 // if flipped:
166 // set unique DFS-child of associated bicomp root node to "flipped"
167 m_flipped[w_child] = true;
168 ++m_flippedNodes;
169 if (dir == Direction::before) {
170 mergeEntry = mergeEntry->cyclicPred();
171 dir = Direction::after;
172 }
173 }
174 }
175
176 // merge adjEntries
177 adjEntry temp;
178 while (adj != nullptr) {
179 temp = adj->succ();
180 edge e = adj->theEdge();
181 OGDF_ASSERT(e->source() != x);
182 OGDF_ASSERT(e->target() != x);
183 // this allows also self-loops when moving adjacency entries
184 if (e->source() == w) {
185 if (!doEmbed) { m_g.moveSource(e, x); }
186 else { m_g.moveSource(e, mergeEntry, dir); }
187 } else {
188 if (!doEmbed) { m_g.moveTarget(e, x); }
189 else { m_g.moveTarget(e, mergeEntry, dir); }
190 }
191 adj = temp;
192 }
193
194 // remove w from pertinent roots of x
195 OGDF_ASSERT( !doEmbed || !m_pertinentRoots[x].empty() );
196 OGDF_ASSERT( m_pertinentRoots[x].front() == w );
197 m_pertinentRoots[x].popFront();
198
199 // consider x's unique dfs-successor in pertinent bicomp:
200 // remove this successor from separatedChildList of x using
201 // saved pointer pNodeInParent in constant time
202 OGDF_ASSERT( !doEmbed || !m_separatedDFSChildList[x].empty() );
203 OGDF_ASSERT( m_pNodeInParent[w_child].valid() );
204 m_separatedDFSChildList[x].del(m_pNodeInParent[w_child]);
205
206 // delete virtual vertex, it must not contain any edges any more
207 OGDF_ASSERT( w->firstAdj() == nullptr );
208 m_nodeFromDFI[m_dfi[w]] = nullptr;
209 m_g.delNode(w);
210 }
211
embedBackedges(const node v,const int v_dir,const node w,const int w_dir)212 void BoyerMyrvoldPlanar::embedBackedges(
213 const node v,
214 const int v_dir,
215 const node w,
216 const int w_dir)
217 {
218 OGDF_ASSERT( v != nullptr );
219 OGDF_ASSERT( w != nullptr );
220 OGDF_ASSERT( !m_backedgeFlags[w].empty() );
221 OGDF_ASSERT( m_link[BoyerMyrvoldPlanar::DirectionCCW][v] != nullptr );
222 OGDF_ASSERT( m_link[BoyerMyrvoldPlanar::DirectionCW][v] != nullptr );
223 OGDF_ASSERT( m_link[BoyerMyrvoldPlanar::DirectionCCW][w] != nullptr );
224 OGDF_ASSERT( m_link[BoyerMyrvoldPlanar::DirectionCW][w] != nullptr );
225
226 bool doEmbed = m_embeddingGrade != EmbeddingGrade::doNotEmbed;
227
228 adjEntry mergeEntryV, mergeEntryW;
229 Direction insertv, insertw;
230 if (doEmbed) {
231 // if one edge is a short circuit edge, compute the former underlying adjEntry
232 // the adjEntry of v, used for inserting backedges
233 mergeEntryV = beforeShortCircuitEdge(v, v_dir)->twin();
234 insertv = (v_dir == BoyerMyrvoldPlanar::DirectionCCW) ? Direction::after : Direction::before;
235 // the adjEntry of w, used for inserting backedges
236 mergeEntryW = beforeShortCircuitEdge(w, !w_dir)->twin();
237 insertw = (w_dir == BoyerMyrvoldPlanar::DirectionCCW) ? Direction::before : Direction::after;
238
239 }
240
241 // the first/last (last iff !doEmbed) backedge in the backedgeFlags-list will be the new external face adjEntry
242 adjEntry saveBack = doEmbed ? m_backedgeFlags[w].front() : m_backedgeFlags[w].back();
243 for (adjEntry adj: m_backedgeFlags[w]) {
244 // embed backedge
245 edge e = adj->theEdge();
246 OGDF_ASSERT( e->isIncident(w) );
247
248 // insert backedge to v (and backedge to w iff doEmbed)
249 if (e->source() == w) {
250 if (doEmbed) {
251 m_g.moveTarget(e, mergeEntryV, insertv);
252 m_g.moveSource(e, mergeEntryW, insertw);
253 } else {
254 m_g.moveTarget(e, v);
255 }
256 } else {
257 if (doEmbed) {
258 m_g.moveSource(e, mergeEntryV, insertv);
259 m_g.moveTarget(e, mergeEntryW, insertw);
260 } else {
261 m_g.moveSource(e, v);
262 }
263 }
264 }
265
266 // set external face link for this backedge and delete out-dated short
267 // circuit links
268 m_link[v_dir][v] = saveBack->twin();
269 m_beforeSCE[v_dir][v] = nullptr;
270 m_link[!w_dir][w] = saveBack;
271 m_beforeSCE[!w_dir][w] = nullptr;
272
273 // decrease counter of backedges per bicomp
274 if (m_embeddingGrade > EmbeddingGrade::doNotFind) {
275 node bicompRoot = m_pointsToRoot[m_backedgeFlags[w].front()->theEdge()];
276 m_numUnembeddedBackedgesInBicomp[bicompRoot] -= m_backedgeFlags[w].size();
277 OGDF_ASSERT( m_extractSubgraph || m_numUnembeddedBackedgesInBicomp[bicompRoot] >= 0 );
278 }
279
280 // delete BackedgeFlags
281 m_backedgeFlags[w].clear();
282 }
283
284
285 // create short circuit edge from node v with direction v_dir to node w with outgoing
286 // direction w_dir.
createShortCircuitEdge(const node v,const int v_dir,const node w,const int w_dir)287 void BoyerMyrvoldPlanar::createShortCircuitEdge(
288 const node v,
289 const int v_dir,
290 const node w,
291 const int w_dir)
292 {
293 // save former neighbors
294 if (m_beforeSCE[v_dir][v]==nullptr) m_beforeSCE[v_dir][v]=m_link[v_dir][v];
295 if (m_beforeSCE[!w_dir][w]==nullptr) m_beforeSCE[!w_dir][w]=m_link[!w_dir][w];
296 // set new short circuit edge
297 adjEntry temp = m_beforeSCE[!w_dir][w]->twin();
298 m_link[!w_dir][w] = m_beforeSCE[v_dir][v]->twin();
299 m_link[v_dir][v] = temp;
300 }
301
302
303 // Walkup
304 // finds pertinent subgraph for descendant w of v.
305 // marks visited nodes with marker and returns the last traversed node.
walkup(const node v,const node w,const int marker,const edge back)306 node BoyerMyrvoldPlanar::walkup(
307 const node v,
308 const node w,
309 const int marker,
310 const edge back)
311 {
312 const int i = m_dfi[v];
313 node x = w;
314 node y = w;
315 node temp;
316 int x_dir = BoyerMyrvoldPlanar::DirectionCW;
317 int y_dir = BoyerMyrvoldPlanar::DirectionCCW;
318
319 while (m_visited[x]!=marker && m_visited[y]!=marker)
320 {
321 m_visited[x] = marker;
322 m_visited[y] = marker;
323 if (m_embeddingGrade > EmbeddingGrade::doNotFind) {
324 m_visitedWithBackedge[x] = back;
325 m_visitedWithBackedge[y] = back;
326 }
327
328 // is x or y root vertex?
329 if (m_realVertex[x] != nullptr) {
330 temp=x;
331 } else if (m_realVertex[y] != nullptr) {
332 temp=y;
333 } else temp=nullptr;
334
335 if (temp != nullptr) {
336 // put pertinent root into the list of its non-virtual vertex.
337 // the insert-position is either front or back of the list, this
338 // depends on the external activity of the pertinent root's
339 // biconnected component.
340
341 x = m_realVertex[temp];
342 y = x;
343
344 OGDF_ASSERT(m_extractSubgraph || m_visited[x]==marker || m_pertinentRoots[x].empty());
345 // push pertinent root
346 if (m_lowPoint[m_nodeFromDFI[-m_dfi[temp]]] < i) {
347 m_pertinentRoots[x].pushBack(temp);
348 } else m_pertinentRoots[x].pushFront(temp);
349 // found v, finish walkup and return last traversed node
350 if (x==v) {
351 m_visited[x] = marker;
352 return temp;
353 }
354 } else {
355 // traverse to external face successors
356 x = successorOnExternalFace(x,x_dir);
357 y = successorOnExternalFace(y,y_dir);
358 }
359 }
360
361 // return last traversed node
362 return (m_visited[x] == marker) ? x : y;
363 }
364
365
366 // Walkdown
367 // for DFS-child w of the current processed vertex v': embed all backedges
368 // to the virtual node v of v'
369 // returns 1, iff the embedding process found a stopping configuration
walkdown(const int i,const node v,FindKuratowskis * findKuratowskis)370 int BoyerMyrvoldPlanar::walkdown(
371 const int i, // dfi of rootvertex v'
372 const node v, // v is virtual node of v'
373 FindKuratowskis *findKuratowskis)
374 {
375 ArrayBuffer<int> stack;
376 node stopX = nullptr;
377
378 bool stoppingNodesFound = 0; // 0=false,1=true,2=break
379
380 // in both directions
381 // j=current outgoing direction of current embedded node v
382 for (int j: {BoyerMyrvoldPlanar::DirectionCCW, BoyerMyrvoldPlanar::DirectionCW}) {
383 int w_dir = j; // direction of traversal of node w
384
385 node w = successorOnExternalFace(v,w_dir); // current node
386
387 while (w != v) {
388 // assert, that CCW[] and CW[] return that adjEntry of the neighbor
389 OGDF_ASSERT(beforeShortCircuitEdge(w,w_dir)->twinNode()==w);
390
391 // if backedgeFlag is set
392 if (!m_backedgeFlags[w].empty()) {
393 while (!stack.empty()) {
394 mergeBiconnectedComponent(stack);
395 }
396 embedBackedges(v, j, w, w_dir);
397 }
398
399 // if pertinentRoots of w is not empty
400 if (!m_pertinentRoots[w].empty()) {
401 // append pertinent root of w and direction of entry in w to stack
402 // y is root of pertinent child bicomp
403 node root = m_pertinentRoots[w].front();
404
405 if(m_extractSubgraph && root->degree() == 0) {
406 m_pertinentRoots[w].popFront();
407 continue;
408 }
409
410 stack.push(m_dfi[root]);
411
412 // append outgoing direction of entry in w to stack
413 OGDF_ASSERT(w->degree() > 0);
414 stack.push(w_dir);
415
416 // get active successor in pertinent bicomp
417 // variables for recognizing the right direction after descending to a bicomp
418 int x_dir = BoyerMyrvoldPlanar::DirectionCCW;
419 int y_dir = BoyerMyrvoldPlanar::DirectionCW;
420 int infoX, infoY; // gives information about the type of endnode in that direction
421 node x = activeSuccessor(root,x_dir,i,infoX);
422 node y = activeSuccessor(root,y_dir,i,infoY);
423
424 OGDF_ASSERT(x != root);
425 OGDF_ASSERT(y != root);
426 createShortCircuitEdge(root,BoyerMyrvoldPlanar::DirectionCCW,x,x_dir);
427 createShortCircuitEdge(root,BoyerMyrvoldPlanar::DirectionCW,y,y_dir);
428
429 // push counterclockwise resp. clockwise active successor
430 // in pertinent bicomp
431 if (infoX == infoY) {
432 // if both attributes are externally active and non-pertinent,
433 // save stopping nodes
434 if (infoX==3) {
435 if(!m_extractSubgraph) {
436 OGDF_ASSERT(x != y);
437 if (m_embeddingGrade <= EmbeddingGrade::doNotFind) return true;
438 }
439
440 // extract Kuratowskis
441 stoppingNodesFound = 1;
442 if(!m_extractSubgraph) {
443 // check, if we have found enough kuratowski structures
444 if (m_embeddingGrade > 0 &&
445 findKuratowskis->getAllKuratowskis().size() >= m_embeddingGrade) {
446 return 2;
447 }
448 findKuratowskis->addKuratowskiStructure(m_nodeFromDFI[i],root,x,y);
449 }
450
451 // go to the pertinent starting node on father bicomp
452 stack.pop(); // delete new w_dir from stack
453 w = m_realVertex[m_nodeFromDFI[stack.popRet()]]; // x itself
454 // refresh pertinentRoots information
455 m_pertinentRoots[w].popFront();
456
457 // if more pertinent child bicomps exist on the same root,
458 // let the walkdown either embed it or find a new kuratowski structure
459 while (!stack.empty() && !pertinent(w)) {
460 // last real root
461 node lastActiveNode = w;
462
463 // not in V-bicomp:
464 // go to the unvisited active node on father bicomp
465 w_dir = stack.popRet(); // outgoing direction of w
466 x_dir = stack.popRet(); // outgoing direction of x
467 w = m_nodeFromDFI[stack.top()]; // w, virtual node
468
469 node otherActiveNode = m_link[!w_dir][w]->theNode();
470
471 OGDF_ASSERT(otherActiveNode == constActiveSuccessor(w,!w_dir,i,infoX));
472 OGDF_ASSERT(externallyActive(otherActiveNode,i));
473 OGDF_ASSERT(lastActiveNode == m_link[w_dir][w]->theNode());
474 if (pertinent(otherActiveNode)) {
475 // push adapted information about actual bicomp in stack
476 stack.push(x_dir);
477 stack.push(!w_dir);
478 // go on with walkdown on unvisited active node
479 w_dir = !w_dir;
480 break;
481 } else {
482 // delete old root
483 stack.pop();
484 // if there are two stopping vertices, that are not pertinent
485 // there could be another kuratowski structure
486 if (!m_extractSubgraph && lastActiveNode != otherActiveNode &&
487 wNodesExist(w,lastActiveNode,otherActiveNode)) {
488 // check, if we have found enough kuratowski structures
489 if (m_embeddingGrade > 0 &&
490 findKuratowskis->getAllKuratowskis().size() >= m_embeddingGrade) {
491 return 2;
492 }
493 // different stopping nodes:
494 // try to extract kuratowski structure and put the two
495 // stopping nodes in the right traversal order
496 if (w_dir==BoyerMyrvoldPlanar::DirectionCCW) {
497 findKuratowskis->addKuratowskiStructure(m_nodeFromDFI[i],
498 w,lastActiveNode,otherActiveNode);
499 } else {
500 findKuratowskis->addKuratowskiStructure(m_nodeFromDFI[i],
501 w,otherActiveNode,lastActiveNode);
502 }
503 }
504
505 // refresh pertinentRoots information
506 w = m_realVertex[w]; // x
507 m_pertinentRoots[w].popFront();
508 w_dir = x_dir;
509 }
510 }
511 }
512 // if both attributes are the same: minimize flips
513 else if (w_dir==BoyerMyrvoldPlanar::DirectionCCW) {
514 w = x;
515 w_dir = x_dir;
516 stack.push(BoyerMyrvoldPlanar::DirectionCCW);
517
518 } else {
519 w = y;
520 w_dir = y_dir;
521 stack.push(BoyerMyrvoldPlanar::DirectionCW);
522 }
523 } else if (infoX <= infoY) {
524 // push x
525 w=x; w_dir=x_dir;
526 stack.push(BoyerMyrvoldPlanar::DirectionCCW);
527
528 } else {
529 // push y
530 w=y; w_dir=y_dir;
531 stack.push(BoyerMyrvoldPlanar::DirectionCW);
532 }
533
534 } else if (inactive(w,i)) {
535 // w is an inactive vertex
536 w = successorOnExternalFace(w,w_dir);
537
538 } else {
539 // w must be a stopping vertex
540 OGDF_ASSERT(externallyActive(w,i));
541 OGDF_ASSERT(m_lowPoint[m_nodeFromDFI[-m_dfi[v]]] < i);
542
543 // embed shortCircuitEdge
544 /*if (stack.empty())*/ createShortCircuitEdge(v,j,w,w_dir);
545
546 // only save single stopping nodes, if we don't have already one
547 if (j==BoyerMyrvoldPlanar::DirectionCCW) {
548 stopX = w;
549 } else if (w != stopX) {
550 OGDF_ASSERT(stopX!=nullptr);
551
552 if (m_embeddingGrade <= EmbeddingGrade::doNotFind) return false;
553 // check, if some backedges were not embedded (=> nonplanar)
554 // note, that this is performed at most one time per virtual root
555 if (m_numUnembeddedBackedgesInBicomp[v] > 0) {
556 // some backedges are left on this bicomp
557 stoppingNodesFound = 1;
558 // check, if we have found enough kuratowski structures
559 if(!m_extractSubgraph) {
560 if (m_embeddingGrade > 0 &&
561 findKuratowskis->getAllKuratowskis().size() >= m_embeddingGrade) {
562 return 2;
563 }
564 findKuratowskis->addKuratowskiStructure(m_nodeFromDFI[i], v, stopX, w);
565 }
566 }
567 }
568 break;
569 }
570 }
571
572 stack.clear();
573 }
574
575 return stoppingNodesFound;
576 }
577
578 // embed graph m_g node by node in descending DFI-order beginning with dfi i
embed()579 bool BoyerMyrvoldPlanar::embed()
580 {
581 bool nonplanar=false; // true, if graph is not planar
582
583 #if 0
584 FindKuratowskis findKuratowskis(this);
585 #else
586 FindKuratowskis* findKuratowskis =
587 (m_embeddingGrade <= EmbeddingGrade::doNotFind) ? nullptr : new FindKuratowskis(this);
588 #endif
589
590 for (int i = m_nodeFromDFI.high(); i >= 1; --i)
591 {
592 const node v = m_nodeFromDFI[i];
593
594 // call Walkup
595 // for all sources of backedges of v: find pertinent subgraph
596
597 for(adjEntry adj : v->adjEntries) {
598 node w = adj->twinNode(); // dfs-descendant of v
599 edge e = adj->theEdge();
600 if (m_dfi[w] > i && m_edgeType[e] == BoyerMyrvoldEdgeType::Back) {
601 m_backedgeFlags[w].pushBack(adj);
602
603 node x = walkup(v,w,i,e);
604 if (m_embeddingGrade <= EmbeddingGrade::doNotFind) continue;
605
606 // divide children bicomps
607 if (m_realVertex[x] == v) {
608 // x is a (virtual) root vertex.
609 m_pointsToRoot[e] = x;
610 OGDF_ASSERT(m_numUnembeddedBackedgesInBicomp[x] == 0);
611 } else {
612 // Set x to the (virtual) root of its bicomp.
613 x = m_pointsToRoot[m_visitedWithBackedge[x]];
614 m_pointsToRoot[e] = x;
615 OGDF_ASSERT(m_numUnembeddedBackedgesInBicomp[x] >= 1);
616 }
617 // Increase the number of backedges leading to x's child bicomp.
618 ++m_numUnembeddedBackedgesInBicomp[x];
619 }
620 }
621
622 // call Walkdown
623 // for every pertinent subtrees with children w of v as roots
624 // embed all backedges to v
625 SListPure<node>& pert(m_pertinentRoots[v]);
626 while (!pert.empty()) {
627 OGDF_ASSERT(pert.front()->degree()==1);
628 int result = walkdown(i,pert.popFrontRet(),findKuratowskis);
629 if (!m_extractSubgraph) {
630 if(result == 2) {
631 m_output = findKuratowskis->getAllKuratowskis();
632 delete findKuratowskis;
633 return false;
634 } else if (result == 1) {
635 // found stopping configuration
636 nonplanar = true;
637 if (m_embeddingGrade <= EmbeddingGrade::doNotFind) return false;
638 }
639 }
640 }
641
642 // if !embed, check, if there are any backedges left
643 if (!m_extractSubgraph && m_embeddingGrade <= EmbeddingGrade::doNotFind) {
644 for(adjEntry adj : v->adjEntries) {
645 if (m_edgeType[adj->theEdge()] == BoyerMyrvoldEdgeType::Back &&
646 m_dfi[adj->twinNode()] > m_dfi[v])
647 {
648 delete findKuratowskis;
649 return false; // nonplanar
650 }
651 }
652 }
653 }
654
655 // embed and flip bicomps, if necessary
656 if (nonplanar) {
657 if(findKuratowskis)
658 m_output = findKuratowskis->getAllKuratowskis();
659 } else
660 postProcessEmbedding(); // flip graph and embed self-loops, etc.
661
662 delete findKuratowskis;
663 return !nonplanar;
664 }
665
666
667 // merge unprocessed virtual nodes such as the dfs-roots
mergeUnprocessedNodes()668 void BoyerMyrvoldPlanar::mergeUnprocessedNodes()
669 {
670 node v = m_g.firstNode();
671 while (v) {
672 node next = v->succ();
673 if (m_dfi[v] < 0) {
674 node w = m_realVertex[v];
675 adjEntry adj = v->firstAdj();
676 // copy all adjEntries to non-virtual node
677 while (adj) {
678 edge e = adj->theEdge();
679 adj = adj->succ();
680 if (e->source()==v) {
681 m_g.moveSource(e,w);
682 } else m_g.moveTarget(e,w);
683 }
684 m_nodeFromDFI[m_dfi[v]]=nullptr;
685 m_g.delNode(v);
686 }
687 v = next;
688 }
689 }
690
691
692 // flips all nodes of the bicomp with unique real root-child c as necessary.
693 // in addition all connected components with dfs-root c without virtual
694 // nodes are allowed. this function can be used to reverse the flip, too!
695 // marker has to be an non-existing int in array visited.
696 // if wholeGraph ist true, all bicomps of all connected components will be traversed.
697 // if deleteFlipFlags ist true, the flipping flags will be deleted after flipping
flipBicomp(int c,int marker,NodeArray<int> & visited,bool wholeGraph,bool deleteFlipFlags)698 void BoyerMyrvoldPlanar::flipBicomp(
699 int c,
700 int marker,
701 NodeArray<int>& visited,
702 bool wholeGraph,
703 bool deleteFlipFlags)
704 {
705 if (m_flippedNodes == 0) {
706 if (wholeGraph) mergeUnprocessedNodes();
707 return;
708 }
709
710 ArrayBuffer<int> stack; // stack for dfs-traversal
711
712 if (wholeGraph) {
713 mergeUnprocessedNodes();
714 for (int i = 1; i <= m_g.numberOfNodes(); ++i)
715 stack.push(-i);
716 }
717
718 // flip bicomps, if the flipped-flag is set
719 bool flip;
720 stack.push(-c); // negative numbers: flip=false, otherwise flip=true
721 while (!stack.empty()) {
722 int stackTop = stack.popRet();
723 node v;
724 if (stackTop < 0) {
725 flip = false;
726 v = m_nodeFromDFI[-stackTop];
727 } else {
728 flip = true;
729 v = m_nodeFromDFI[stackTop];
730 }
731 if (wholeGraph) {
732 if (visited[v]==marker) continue;
733 // mark visited nodes
734 visited[v] = marker;
735 }
736
737 // flip adjEntries of node, if necessary
738 if (m_flipped[v]) {
739 flip = !flip;
740
741 // don't do this, if all flips on nodes of this bicomp will be reversed
742 if (deleteFlipFlags) {
743 m_flipped[v] = false;
744 --m_flippedNodes;
745 OGDF_ASSERT(m_flippedNodes >= 0);
746 }
747 }
748 if (flip) {
749 m_g.reverseAdjEdges(v);
750 if (deleteFlipFlags) {
751 adjEntry tmp = m_link[BoyerMyrvoldPlanar::DirectionCCW][v];
752 m_link[BoyerMyrvoldPlanar::DirectionCCW][v] = m_link[BoyerMyrvoldPlanar::DirectionCW][v];
753 m_link[BoyerMyrvoldPlanar::DirectionCW][v] = tmp;
754
755 tmp = m_beforeSCE[BoyerMyrvoldPlanar::DirectionCCW][v];
756 m_beforeSCE[BoyerMyrvoldPlanar::DirectionCCW][v] = m_beforeSCE[BoyerMyrvoldPlanar::DirectionCW][v];
757 m_beforeSCE[BoyerMyrvoldPlanar::DirectionCW][v] = tmp;
758 }
759 }
760
761 // go along the dfs-edges
762 for(adjEntry adj : v->adjEntries) {
763 int temp = m_dfi[adj->twinNode()];
764 OGDF_ASSERT(m_edgeType[adj->theEdge()] != BoyerMyrvoldEdgeType::Undefined);
765 if (temp > m_dfi[v] && m_edgeType[adj->theEdge()]==BoyerMyrvoldEdgeType::Dfs) {
766 stack.push(flip ? temp : -temp);
767 }
768 }
769 }
770 }
771
772
773 // postprocess the embedding, so that all unprocessed virtual vertices are
774 // merged with their non-virtual counterpart. Furthermore all bicomps
775 // are flipped, if necessary and parallel edges and self-loops are embedded.
postProcessEmbedding()776 void BoyerMyrvoldPlanar::postProcessEmbedding()
777 {
778 ArrayBuffer<int> stack; // stack for dfs-traversal
779 node v,w;
780 adjEntry adj;
781 int temp;
782
783 mergeUnprocessedNodes();
784
785 // flip bicomps, if the flipped-flag is set, i.e. postprocessing in
786 // reverse dfi-order
787 bool flip;
788 for(int i=1; i<=m_g.numberOfNodes(); ++i) {
789 if (m_visited[m_nodeFromDFI[i]] == -1) continue;
790 stack.push(-i); // negative numbers: flip=false, otherwise flip=true
791
792 while (!stack.empty()) {
793 temp = stack.popRet();
794 if (temp < 0) {
795 flip=false;
796 v = m_nodeFromDFI[-temp];
797 } else {
798 flip=true;
799 v = m_nodeFromDFI[temp];
800 }
801 if (m_visited[v]==-1) continue;
802 // mark visited nodes with visited[v]==-1
803 m_visited[v] = -1;
804
805 // flip adjEntries of node, if necessary
806 if (m_flipped[v]) {
807 m_flipped[v] = false;
808 flip = !flip;
809 }
810 if (flip) m_g.reverseAdjEdges(v);
811
812 adj=v->firstAdj();
813 while (adj) {
814 w = adj->twinNode();
815 BoyerMyrvoldEdgeType tempType = m_edgeType[adj->theEdge()];
816 if (tempType==BoyerMyrvoldEdgeType::Dfs) {
817 // go along the dfs-edges
818 stack.push(flip ? m_dfi[w] : -m_dfi[w]);
819 adj=adj->succ();
820 } else if (tempType==BoyerMyrvoldEdgeType::Selfloop) {
821 // embed self-loops
822 m_g.moveAdjBefore(adj->twin(),adj);
823 adj=adj->succ();
824 } else if (tempType==BoyerMyrvoldEdgeType::DfsParallel &&
825 m_adjParent[v]!=nullptr &&
826 w == m_adjParent[v]->theNode()) {
827 // embed edges that are parallel to dfs-edges
828 // it is only possible to deal with the parallel edges to the
829 // parent, since children nodes could be flipped later
830 adjEntry tmp = adj->succ();
831 m_g.moveAdjAfter(adj,m_adjParent[v]->twin());
832 m_g.moveAdjBefore(adj->twin(),m_adjParent[v]);
833 adj = tmp;
834 } else adj=adj->succ();
835 }
836 }
837 }
838 }
839
840
841 // tests Graph m_g for planarity
842 // if graph should be embedded, a planar embedding or a kuratowski subdivision
843 // of m_g is returned in addition, depending on whether m_g is planar
start()844 bool BoyerMyrvoldPlanar::start()
845 {
846 boyer_myrvold::BoyerMyrvoldInit bmi(this);
847 bmi.computeDFS();
848 bmi.computeLowPoints();
849 bmi.computeDFSChildLists();
850
851 // call the embedding procedure
852 return embed();
853 }
854
855
856 }
857