Prefer to use VS2013 for compiling and testing on AppVeyor
[TortoiseGit.git] / ext / OGDF / src / planarity / BoyerMyrvoldPlanar.cpp
bloba56cdbf6921961e9fa6ffc1036e22fecb1be3992
1 /*
2 * $Revision: 2559 $
4 * last checkin:
5 * $Author: gutwenger $
6 * $Date: 2012-07-06 15:04:28 +0200 (Fr, 06. Jul 2012) $
7 ***************************************************************/
9 /** \file
10 * \brief implementation of the class BoyerMyrvoldPlanar
12 * \author Jens Schmidt
14 * \par License:
15 * This file is part of the Open Graph Drawing Framework (OGDF).
17 * \par
18 * Copyright (C)<br>
19 * See README.txt in the root directory of the OGDF installation for details.
21 * \par
22 * This program is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU General Public License
24 * Version 2 or 3 as published by the Free Software Foundation;
25 * see the file LICENSE.txt included in the packaging of this file
26 * for details.
28 * \par
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
34 * \par
35 * You should have received a copy of the GNU General Public
36 * License along with this program; if not, write to the Free
37 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
38 * Boston, MA 02110-1301, USA.
40 * \see http://www.gnu.org/copyleft/gpl.html
41 ***************************************************************/
44 #include <ogdf/internal/planarity/BoyerMyrvoldPlanar.h>
45 #include <ogdf/internal/planarity/BoyerMyrvoldInit.h>
46 #include <ogdf/internal/planarity/FindKuratowskis.h>
49 namespace ogdf {
52 // constructor
53 BoyerMyrvoldPlanar::BoyerMyrvoldPlanar(
54 Graph& g,
55 bool bundles,
56 int embeddingGrade, // see enumeration enumEmbeddingGrade for options
57 bool limitStructures, // limits number of structures to embeddingGrade
58 SListPure<KuratowskiStructure>& output,
59 bool randomDFSTree, // creates a random DFS-Tree, if true
60 bool avoidE2Minors) // avoids multiple identical minors (type AE2/E2)
62 m_g(g),
63 m_bundles(bundles),
64 m_embeddingGrade(embeddingGrade),
65 m_limitStructures(limitStructures),
66 m_randomDFSTree(randomDFSTree),
67 m_avoidE2Minors(avoidE2Minors),
69 // BoyerMyrvoldInit members
70 m_realVertex(g,0),
71 m_dfi(g,0),
72 m_nodeFromDFI(-g.numberOfNodes(),g.numberOfNodes(),0),
73 m_adjParent(g,0),
74 m_leastAncestor(g), // doesn't need initialization
75 m_edgeType(g,EDGE_UNDEFINED),
76 m_lowPoint(g), // doesn't need initialization
77 m_separatedDFSChildList(g),
78 m_pNodeInParent(g), // doesn't need initialization
80 // Walkup & Walkdown members
81 m_visited(g,0),
82 m_flipped(g,false),
83 m_backedgeFlags(g),
84 m_pertinentRoots(g),
85 m_output(output)
87 m_link[CCW].init(g,0);
88 m_link[CW].init(g,0);
89 m_beforeSCE[CCW].init(g,0);
90 m_beforeSCE[CW].init(g,0);
91 m_output.clear();
92 // apply this only, if FIND-procedure will be called
93 if (m_embeddingGrade > doNotFind) {
94 m_pointsToRoot.init(g,0);
95 m_visitedWithBackedge.init(g,0);
96 m_highestSubtreeDFI.init(g); // doesn't need initialization
98 m_flippedNodes = 0;
102 // walk upon external face in the given direction, see getSucessorOnExternalFace
103 // the difference is, that all inactive vertices are skipped, i.e. the returned node
104 // is active in relation to the node with dfi v
105 // in the special case of degree-one nodes the direction is not changed
106 // info returns the dynamic nodetype of the endnode
107 node BoyerMyrvoldPlanar::activeSuccessor(node w, int& direction, int v, int& info)
109 OGDF_ASSERT(w!=0);
110 OGDF_ASSERT(w->degree()>0);
111 OGDF_ASSERT(m_link[CW][w]!=0 && m_link[CCW][w]!=0);
112 node next;
113 adjEntry adj;
115 do {
116 adj = m_link[direction][w];
117 next = adj->theNode();
118 OGDF_ASSERT(next!=0);
119 OGDF_ASSERT(next->degree()>0);
120 OGDF_ASSERT(m_link[CW][next]!=0 && m_link[CCW][next]!=0);
122 if (w->degree() > 1)
123 direction = adj==beforeShortCircuitEdge(next,CCW)->twin();
124 w=next;
125 info = infoAboutNode(next,v);
127 } while (info==0); // until not inactive
128 return next;
132 // merges adjEntries of virtual node w and associated real vertex x according to
133 // given outgoing directions x_dir and w_dir.
134 // j is the outgoing traversal direction of the current embedded node.
135 void BoyerMyrvoldPlanar::mergeBiconnectedComponent(StackPure<int>& stack, const int /* j */)
137 const int w_dir = stack.pop(); // outgoing direction of w
138 const int x_dir = stack.pop(); // outgoing direction of x
139 int tmp = stack.pop();
140 const node w = m_nodeFromDFI[tmp]; // virtual DFS-Successor of x
141 const node w_child = m_nodeFromDFI[-tmp]; // real unique DFS-Child of bicomp with root w
142 const node x = m_realVertex[w];
144 // set new external face neighbors and save adjEntry, where edges will be merged
145 adjEntry mergeEntry;
146 Direction dir = (x_dir == CCW) ? before : after;
147 mergeEntry = beforeShortCircuitEdge(x,!x_dir)->twin();
148 m_link[!x_dir][x] = m_link[!w_dir][w];
149 m_beforeSCE[!x_dir][x] = m_beforeSCE[!w_dir][w];
151 // merge real and virtual nodes, flip biconnected component root if neccesary
152 OGDF_ASSERT(!m_flipped[w_child]);
153 adjEntry adj = w->firstAdj();
154 edge e;
155 if (x_dir==w_dir) {
156 // if not flipped
157 if (dir==after) {
158 mergeEntry=mergeEntry->cyclicSucc();
159 dir=before;
161 } else {
162 // if flipped:
163 // set unique DFS-child of associated bicomp root node to "flipped"
164 m_flipped[w_child] = true;
165 ++m_flippedNodes;
166 if (dir==before) {
167 mergeEntry = mergeEntry->cyclicPred();
168 dir = after;
172 // merge adjEntries
173 adjEntry temp;
174 while (adj != 0) {
175 temp = adj->succ();
176 e = adj->theEdge();
177 OGDF_ASSERT(e->source() != x && e->target() != x);
178 // this allows also self-loops when moving adjacency entries
179 if (e->source() == w) {
180 m_g.moveSource(e,mergeEntry,dir);
181 } else m_g.moveTarget(e,mergeEntry,dir);
182 adj = temp;
185 // remove w from pertinent roots of x
186 OGDF_ASSERT(!m_pertinentRoots[x].empty());
187 OGDF_ASSERT(m_pertinentRoots[x].front() == w);
188 m_pertinentRoots[x].popFront();
190 // consider x's unique dfs-successor in pertinent bicomp:
191 // remove this successor from separatedChildList of x using
192 // saved pointer pNodeInParent in constant time
193 OGDF_ASSERT(!m_separatedDFSChildList[x].empty());
194 OGDF_ASSERT(m_pNodeInParent[w_child].valid());
195 m_separatedDFSChildList[x].del(m_pNodeInParent[w_child]);
197 // delete virtual vertex, it must not contain any edges any more
198 OGDF_ASSERT(w->firstAdj()==0);
199 m_nodeFromDFI[m_dfi[w]]=0;
200 m_g.delNode(w);
204 // the same as mergeBiconnectedComponent, but without any embedding-related
205 // operations
206 void BoyerMyrvoldPlanar::mergeBiconnectedComponentOnlyPlanar(
207 StackPure<int>& stack,
208 const int /* j */)
210 const int w_dir = stack.pop(); // outgoing direction of w
211 const int x_dir = stack.pop(); // outgoing direction of x
212 int tmp = stack.pop();
213 const node w = m_nodeFromDFI[tmp]; // virtual DFS-Successor of x
214 const node w_child = m_nodeFromDFI[-tmp]; // real unique DFS-Child of bicomp with root w
215 const node x = m_realVertex[w];
217 // set new external face neighbors and save adjEntry, where edges will be merged
218 m_link[!x_dir][x] = m_link[!w_dir][w];
219 m_beforeSCE[!x_dir][x] = m_beforeSCE[!w_dir][w];
221 // merge real and virtual nodes, flipping is not necessary here
222 OGDF_ASSERT(!m_flipped[w_child]);
223 adjEntry adj = w->firstAdj();
224 edge e;
225 adjEntry temp;
226 while (adj != 0) {
227 temp = adj->succ();
228 e = adj->theEdge();
229 OGDF_ASSERT(e->source() != x && e->target() != x);
230 // this allows also self-loops when moving adjacency entries
231 if (e->source() == w) {
232 m_g.moveSource(e,x);
233 } else m_g.moveTarget(e,x);
234 adj = temp;
237 // remove w from pertinent roots of x
238 OGDF_ASSERT(m_pertinentRoots[x].front() == w);
239 m_pertinentRoots[x].popFront();
241 // consider x's unique dfs-successor in pertinent bicomp:
242 // remove this successor from separatedChildList of x using
243 // saved pointer pNodeInParent in constant time
244 OGDF_ASSERT(m_pNodeInParent[w_child].valid());
245 m_separatedDFSChildList[x].del(m_pNodeInParent[w_child]);
247 // delete virtual vertex, it must not contain any edges any more
248 OGDF_ASSERT(w->firstAdj()==0);
249 m_nodeFromDFI[m_dfi[w]]=0;
250 m_g.delNode(w);
254 // embeds backedges from node v with direction v_dir to node w
255 // with direction w_dir. i is the current embedded node.
256 void BoyerMyrvoldPlanar::embedBackedges(
257 const node v,
258 const int v_dir,
259 const node w,
260 const int w_dir,
261 const int /* i */)
263 OGDF_ASSERT(!m_backedgeFlags[w].empty());
264 OGDF_ASSERT(v!=0 && w!=0);
265 OGDF_ASSERT(m_link[CCW][v]!=0 && m_link[CW][v]!=0);
266 OGDF_ASSERT(m_link[CCW][w]!=0 && m_link[CW][w]!=0);
268 // if one edge is a short circuit edge, compute the former underlying adjEntry
269 // the adjEntry of v, used for inserting backedges
270 adjEntry mergeEntryV = beforeShortCircuitEdge(v,v_dir)->twin();
271 Direction insertv = (v_dir==CCW) ? after : before;
272 // the adjEntry of w, used for inserting backedges
273 adjEntry mergeEntryW = beforeShortCircuitEdge(w,!w_dir)->twin();
274 Direction insertw = (w_dir==CCW) ? before : after;
276 // the first backedge in the backedgeFlags-list will be
277 // the new external face adjEntry
278 edge e;
279 SListConstIterator<adjEntry> it;
280 // save first BackedgeEntry
281 adjEntry firstBack = m_backedgeFlags[w].front();
282 for (it = m_backedgeFlags[w].begin(); it.valid(); ++it) {
283 // embed this backedge
284 e = (*it)->theEdge();
286 OGDF_ASSERT(w==e->source() || w==e->target());
287 //OGDF_ASSERT((*it)->theNode()==m_nodeFromDFI[i]);
289 if (e->source() == w) {
290 // insert backedge to v
291 m_g.moveTarget(e,mergeEntryV,insertv);
292 // insert backedge to w
293 m_g.moveSource(e,mergeEntryW,insertw);
294 } else {
295 // insert backedge to v
296 m_g.moveSource(e,mergeEntryV,insertv);
297 // insert backedge to w
298 m_g.moveTarget(e,mergeEntryW,insertw);
302 // set external face link for this backedge and delete out-dated short
303 // circuit links
304 m_link[v_dir][v] = firstBack->twin();
305 m_beforeSCE[v_dir][v]=0;
306 m_link[!w_dir][w] = firstBack;
307 m_beforeSCE[!w_dir][w]=0;
309 // decrease counter of backedges per bicomp
310 if (m_embeddingGrade > doNotFind) {
311 node bicompRoot = m_pointsToRoot[m_backedgeFlags[w].front()->theEdge()];
312 m_visitedWithBackedge[bicompRoot] -= m_backedgeFlags[w].size();
313 OGDF_ASSERT(m_visitedWithBackedge[bicompRoot] >= 0);
316 // delete BackedgeFlags
317 m_backedgeFlags[w].clear();
321 // the same as embedBackedges, but for the planar check without returned embedding
322 void BoyerMyrvoldPlanar::embedBackedgesOnlyPlanar(
323 const node v,
324 const int v_dir,
325 const node w,
326 const int w_dir,
327 const int /* i */)
329 OGDF_ASSERT(!m_backedgeFlags[w].empty());
330 OGDF_ASSERT(m_link[CCW][v]!=0 && m_link[CW][v]!=0);
331 OGDF_ASSERT(m_link[CCW][w]!=0 && m_link[CW][w]!=0);
333 // the last backedge in the backedgeFlags-list will be
334 // the new external face adjEntry
335 edge e;
336 SListIterator<adjEntry> it;
337 // save last BackedgeEntry
338 adjEntry lastBack = m_backedgeFlags[w].back();
339 for(it=m_backedgeFlags[w].begin();it.valid();++it) {
340 // embed backedge
341 e = (*it)->theEdge();
343 //OGDF_ASSERT((*it)->theNode()==m_nodeFromDFI[i]);
344 OGDF_ASSERT(w==e->source() || w==e->target());
346 if (e->source() == w) {
347 // insert backedge to v
348 m_g.moveTarget(e,v);
349 } else {
350 // insert backedge to v
351 m_g.moveSource(e,v);
355 // set external face link for this backedge and delete out-dated short
356 // circuit links
357 m_link[v_dir][v] = lastBack->twin();
358 m_beforeSCE[v_dir][v]=0;
359 m_link[!w_dir][w] = lastBack;
360 m_beforeSCE[!w_dir][w]=0;
362 // delete BackedgeFlags
363 m_backedgeFlags[w].clear();
367 // create short circuit edge from node v with direction v_dir to node w with outgoing
368 // direction w_dir.
369 void BoyerMyrvoldPlanar::createShortCircuitEdge(
370 const node v,
371 const int v_dir,
372 const node w,
373 const int w_dir)
375 // save former neighbors
376 if (m_beforeSCE[v_dir][v]==0) m_beforeSCE[v_dir][v]=m_link[v_dir][v];
377 if (m_beforeSCE[!w_dir][w]==0) m_beforeSCE[!w_dir][w]=m_link[!w_dir][w];
378 // set new short circuit edge
379 adjEntry temp = m_beforeSCE[!w_dir][w]->twin();
380 m_link[!w_dir][w] = m_beforeSCE[v_dir][v]->twin();
381 m_link[v_dir][v] = temp;
385 // Walkup
386 // finds pertinent subgraph for descendant w of v.
387 // marks visited nodes with marker and returns the last traversed node.
388 node BoyerMyrvoldPlanar::walkup(
389 const node v,
390 const node w,
391 const int marker,
392 const edge back)
394 const int i = m_dfi[v];
395 node x = w;
396 node y = w;
397 node temp;
398 int x_dir = CW;
399 int y_dir = CCW;
401 while (m_visited[x]!=marker && m_visited[y]!=marker)
403 m_visited[x] = marker;
404 m_visited[y] = marker;
405 if (m_embeddingGrade > doNotFind) {
406 m_visitedWithBackedge[x] = back->index();
407 m_visitedWithBackedge[y] = back->index();
410 // is x or y root vertex?
411 if (m_realVertex[x] != 0) {
412 temp=x;
413 } else if (m_realVertex[y] != 0) {
414 temp=y;
415 } else temp=0;
417 if (temp != 0) {
418 // put pertinent root into the list of its non-virtual vertex.
419 // the insert-position is either front or back of the list, this
420 // depends on the external activity of the pertinent root's
421 // biconnected component.
423 x = m_realVertex[temp];
424 y = x;
426 OGDF_ASSERT(m_visited[x]==marker || m_pertinentRoots[x].empty());
427 // push pertinent root
428 if (m_lowPoint[m_nodeFromDFI[-m_dfi[temp]]] < i) {
429 m_pertinentRoots[x].pushBack(temp);
430 } else m_pertinentRoots[x].pushFront(temp);
431 // found v, finish walkup and return last traversed node
432 if (x==v) {
433 m_visited[x] = marker;
434 return temp;
436 } else {
437 // traverse to external face successors
438 x = successorOnExternalFace(x,x_dir);
439 y = successorOnExternalFace(y,y_dir);
443 // return last traversed node
444 return (m_visited[x] == marker) ? x : y;
448 // Walkdown
449 // for DFS-child w of the current processed vertex v': embed all backedges
450 // to the virtual node v of v'
451 // returns 1, iff the embedding process found a stopping configuration
452 int BoyerMyrvoldPlanar::walkdown(
453 const int i, // dfi of rootvertex v'
454 const node v, // v is virtual node of v'
455 FindKuratowskis *findKuratowskis)
457 StackPure<int> stack;
458 node stopX = 0;
460 bool stoppingNodesFound = 0; // 0=false,1=true,2=break
462 // in both directions
463 // j=current outgoing direction of current embedded node v
464 for (int j = CCW; j <= CW; ++j) {
465 int w_dir = j; // direction of traversal of node w
467 node w = successorOnExternalFace(v,w_dir); // current node
469 while (w != v) {
470 // assert, that CCW[] and CW[] return that adjEntry of the neighbor
471 OGDF_ASSERT(beforeShortCircuitEdge(w,w_dir)->twinNode()==w);
473 // if backedgeFlag is set
474 if (!m_backedgeFlags[w].empty()) {
475 if (m_embeddingGrade != doNotEmbed) {
476 // compute entire embedding
477 while (!stack.empty()) mergeBiconnectedComponent(stack,j);
478 // embed the backedge
479 embedBackedges(v,j,w,w_dir,i);
480 } else {
481 // compute only planarity, not the entire embedding
482 while (!stack.empty()) mergeBiconnectedComponentOnlyPlanar(stack,j);
483 // embed the backedge
484 embedBackedgesOnlyPlanar(v,j,w,w_dir,i);
488 // if pertinentRoots of w is not empty
489 if (!m_pertinentRoots[w].empty()) {
490 // append pertinent root of w and direction of entry in w to stack
491 // y is root of pertinent child bicomp
492 node root = m_pertinentRoots[w].front();
493 stack.push(m_dfi[root]);
495 // append outgoing direction of entry in w to stack
496 OGDF_ASSERT(w->degree() > 0);
497 stack.push(w_dir);
499 // get active successor in pertinent bicomp
500 // variables for recognizing the right direction after descending to a bicomp
501 int x_dir = CCW;
502 int y_dir = CW;
503 int infoX, infoY; // gives information about the type of endnode in that direction
504 node x = activeSuccessor(root,x_dir,i,infoX);
505 node y = activeSuccessor(root,y_dir,i,infoY);
507 OGDF_ASSERT(x != root && y != root);
508 createShortCircuitEdge(root,CCW,x,x_dir);
509 createShortCircuitEdge(root,CW,y,y_dir);
511 // push counterclockwise resp. clockwise active successor
512 // in pertinent bicomp
513 if (infoX == infoY) {
514 // if both attributes are externally active and non-pertinent,
515 // save stopping nodes
516 if (infoX==3) {
517 OGDF_ASSERT(x != y);
518 if (m_embeddingGrade <= doNotFind) return true;
520 // extract Kuratowskis
521 stoppingNodesFound = 1;
522 // check, if we have found enough kuratowski structures
523 if (m_embeddingGrade > 0 &&
524 findKuratowskis->getAllKuratowskis().size() >= m_embeddingGrade) {
525 return 2;
527 findKuratowskis->addKuratowskiStructure(m_nodeFromDFI[i],root,x,y);
529 // go to the pertinent starting node on father bicomp
530 stack.pop(); // delete new w_dir from stack
531 w = m_realVertex[m_nodeFromDFI[stack.pop()]]; // x itself
532 // refresh pertinentRoots information
533 m_pertinentRoots[w].popFront();
535 // if more pertinent child bicomps exist on the same root,
536 // let the walkdown either embed it or find a new kuratowski structure
537 while (!stack.empty() && !pertinent(w)) {
538 // last real root
539 node lastActiveNode = w;
541 // not in V-bicomp:
542 // go to the unvisited active node on father bicomp
543 w_dir = stack.pop(); // outgoing direction of w
544 x_dir = stack.pop(); // outgoing direction of x
545 w = m_nodeFromDFI[stack.top()]; // w, virtual node
547 node otherActiveNode = m_link[!w_dir][w]->theNode();
549 OGDF_ASSERT(otherActiveNode == constActiveSuccessor(w,!w_dir,i,infoX));
550 OGDF_ASSERT(externallyActive(otherActiveNode,i));
551 OGDF_ASSERT(lastActiveNode == m_link[w_dir][w]->theNode());
552 if (pertinent(otherActiveNode)) {
553 // push adapted information about actual bicomp in stack
554 stack.push(x_dir);
555 stack.push(!w_dir);
556 // go on with walkdown on unvisited active node
557 w_dir = !w_dir;
558 break;
559 } else {
560 // delete old root
561 stack.pop();
562 // if there are two stopping vertices, that are not pertinent
563 // there could be another kuratowski structure
564 if (lastActiveNode != otherActiveNode &&
565 wNodesExist(w,lastActiveNode,otherActiveNode)) {
566 // check, if we have found enough kuratowski structures
567 if (m_embeddingGrade > 0 &&
568 findKuratowskis->getAllKuratowskis().size() >= m_embeddingGrade) {
569 return 2;
571 // different stopping nodes:
572 // try to extract kuratowski structure and put the two
573 // stopping nodes in the right traversal order
574 if (w_dir==CCW) {
575 findKuratowskis->addKuratowskiStructure(m_nodeFromDFI[i],
576 w,lastActiveNode,otherActiveNode);
577 } else {
578 findKuratowskis->addKuratowskiStructure(m_nodeFromDFI[i],
579 w,otherActiveNode,lastActiveNode);
583 // refresh pertinentRoots information
584 w = m_realVertex[w]; // x
585 m_pertinentRoots[w].popFront();
586 w_dir = x_dir;
590 // if both attributes are the same: minimize flips
591 else if (w_dir==CCW) {
592 w = x;
593 w_dir = x_dir;
594 stack.push(CCW);
596 } else {
597 w = y;
598 w_dir = y_dir;
599 stack.push(CW);
601 } else if (infoX <= infoY) {
602 // push x
603 w=x; w_dir=x_dir;
604 stack.push(CCW);
606 } else {
607 // push y
608 w=y; w_dir=y_dir;
609 stack.push(CW);
612 } else if (inactive(w,i)) {
613 // w is an inactive vertex
614 w = successorOnExternalFace(w,w_dir);
616 } else {
617 // w must be a stopping vertex
618 OGDF_ASSERT(externallyActive(w,i));
619 OGDF_ASSERT(m_lowPoint[m_nodeFromDFI[-m_dfi[v]]] < i);
621 // embed shortCircuitEdge
622 /*if (stack.empty())*/ createShortCircuitEdge(v,j,w,w_dir);
624 // only save single stopping nodes, if we don't have already one
625 if (j==CCW) {
626 stopX = w;
627 } else if (w != stopX) {
628 OGDF_ASSERT(stopX!=0);
630 if (m_embeddingGrade <= doNotFind) return false;
631 // check, if some backedges were not embedded (=> nonplanar)
632 // note, that this is performed at most one time per virtual root
633 if (m_visitedWithBackedge[v] > 0) {
634 // some backedges are left on this bicomp
635 stoppingNodesFound = 1;
636 // check, if we have found enough kuratowski structures
637 if (m_embeddingGrade > 0 &&
638 findKuratowskis->getAllKuratowskis().size() >= m_embeddingGrade) {
639 return 2;
641 findKuratowskis->addKuratowskiStructure(m_nodeFromDFI[i],v,stopX,w);
645 break; // while
647 } // while
649 stack.clear();
650 } // for
652 return stoppingNodesFound;
655 // embed graph m_g node by node in descending DFI-order beginning with dfi i
656 bool BoyerMyrvoldPlanar::embed()
658 bool nonplanar=false; // true, if graph is not planar
660 //FindKuratowskis findKuratowskis(this);
661 FindKuratowskis* findKuratowskis =
662 (m_embeddingGrade <= doNotFind) ? 0 : new FindKuratowskis(this);
664 for (int i = m_nodeFromDFI.high(); i >= 1; --i)
666 const node v = m_nodeFromDFI[i];
668 // call Walkup
669 // for all sources of backedges of v: find pertinent subgraph
671 adjEntry adj;
672 forall_adj(adj,v) {
673 node w = adj->twinNode(); // dfs-descendant of v
674 edge e = adj->theEdge();
675 if (m_dfi[w] > i && m_edgeType[e] == EDGE_BACK) {
676 m_backedgeFlags[w].pushBack(adj);
678 node x = walkup(v,w,i,e);
679 if (m_embeddingGrade <= doNotFind) continue;
681 // divide children bicomps
682 if (m_realVertex[x] == v) {
683 m_pointsToRoot[e] = x;
684 // set backedgenumber to 1 on this root
685 m_visitedWithBackedge[x] = 1;
686 } else {
687 x = m_pointsToRoot[m_visitedWithBackedge[x]];
688 m_pointsToRoot[e] = x;
689 // increase backedgenumber on this root
690 OGDF_ASSERT(m_visitedWithBackedge[x]>=1);
691 ++m_visitedWithBackedge[x];
696 // call Walkdown
697 // for every pertinent subtrees with children w of v as roots
698 // embed all backedges to v
699 SListPure<node>& pert(m_pertinentRoots[v]);
700 while (!pert.empty()) {
701 OGDF_ASSERT(pert.front()->degree()==1);
702 int result = walkdown(i,pert.popFrontRet(),findKuratowskis);
703 if (result == 2) {
704 m_output = findKuratowskis->getAllKuratowskis();
705 delete findKuratowskis;
706 return false;
707 } else if (result == 1) {
708 // found stopping configuration
709 nonplanar = true;
710 if (m_embeddingGrade <= doNotFind) return false;
714 // if !embed, check, if there are any backedges left
715 if (m_embeddingGrade <= doNotFind) {
716 forall_adj(adj,v) {
717 if (m_edgeType[adj->theEdge()] == EDGE_BACK &&
718 m_dfi[adj->twinNode()] > m_dfi[v])
719 return false; // nonplanar
724 // embed and flip bicomps, if necessary
725 if (nonplanar) {
726 if(findKuratowskis)
727 m_output = findKuratowskis->getAllKuratowskis();
728 } else
729 postProcessEmbedding(); // flip graph and embed self-loops, etc.
731 delete findKuratowskis;
732 return !nonplanar;
736 // merge unprocessed virtual nodes such as the dfs-roots
737 void BoyerMyrvoldPlanar::mergeUnprocessedNodes()
739 node v = m_g.firstNode();
740 while (v) {
741 node next = v->succ();
742 if (m_dfi[v] < 0) {
743 node w = m_realVertex[v];
744 adjEntry adj = v->firstAdj();
745 // copy all adjEntries to non-virtual node
746 while (adj) {
747 edge e = adj->theEdge();
748 adj = adj->succ();
749 if (e->source()==v) {
750 m_g.moveSource(e,w);
751 } else m_g.moveTarget(e,w);
753 m_nodeFromDFI[m_dfi[v]]=0;
754 m_g.delNode(v);
756 v = next;
761 // flips all nodes of the bicomp with unique real root-child c as necessary.
762 // in addition all connected components with dfs-root c without virtual
763 // nodes are allowed. this function can be used to reverse the flip, too!
764 // marker has to be an non-existing int in array visited.
765 // if wholeGraph ist true, all bicomps of all connected components will be traversed.
766 // if deleteFlipFlags ist true, the flipping flags will be deleted after flipping
767 void BoyerMyrvoldPlanar::flipBicomp(
768 int c,
769 int marker,
770 NodeArray<int>& visited,
771 bool wholeGraph,
772 bool deleteFlipFlags)
774 if (m_flippedNodes == 0) {
775 if (wholeGraph) mergeUnprocessedNodes();
776 return;
779 StackPure<int> stack; // stack for dfs-traversal
780 node v;
781 int temp;
782 adjEntry adj;
784 if (wholeGraph) {
785 mergeUnprocessedNodes();
786 for (int i = 1; i <= m_g.numberOfNodes(); ++i)
787 stack.push(-i);
790 // flip bicomps, if the flipped-flag is set
791 bool flip;
792 stack.push(-c); // negative numbers: flip=false, otherwise flip=true
793 while (!stack.empty()) {
794 temp = stack.pop();
795 if (temp < 0) {
796 flip = false;
797 v = m_nodeFromDFI[-temp];
798 } else {
799 flip = true;
800 v = m_nodeFromDFI[temp];
802 if (wholeGraph) {
803 if (visited[v]==marker) continue;
804 // mark visited nodes
805 visited[v] = marker;
808 // flip adjEntries of node, if necessary
809 if (m_flipped[v]) {
810 flip = !flip;
812 // don't do this, if all flips on nodes of this bicomp will be reversed
813 if (deleteFlipFlags) {
814 m_flipped[v] = false;
815 --m_flippedNodes;
816 OGDF_ASSERT(m_flippedNodes >= 0);
819 if (flip) {
820 m_g.reverseAdjEdges(v);
821 if (deleteFlipFlags) {
822 adjEntry tmp = m_link[CCW][v];
823 m_link[CCW][v] = m_link[CW][v];
824 m_link[CW][v] = tmp;
826 tmp = m_beforeSCE[CCW][v];
827 m_beforeSCE[CCW][v] = m_beforeSCE[CW][v];
828 m_beforeSCE[CW][v] = tmp;
832 // go along the dfs-edges
833 forall_adj(adj,v) {
834 temp = m_dfi[adj->twinNode()];
835 OGDF_ASSERT(m_edgeType[adj->theEdge()] != EDGE_UNDEFINED);
836 if (temp > m_dfi[v] && m_edgeType[adj->theEdge()]==EDGE_DFS) {
837 stack.push(flip ? temp : -temp);
844 // postprocess the embedding, so that all unprocessed virtual vertices are
845 // merged with their non-virtual counterpart. Furthermore all bicomps
846 // are flipped, if necessary and parallel edges and self-loops are embedded.
847 void BoyerMyrvoldPlanar::postProcessEmbedding()
849 StackPure<int> stack; // stack for dfs-traversal
850 node v,w;
851 adjEntry adj;
852 int temp;
854 mergeUnprocessedNodes();
856 // flip bicomps, if the flipped-flag is set, i.e. postprocessing in
857 // reverse dfi-order
858 bool flip;
859 for(int i=1; i<=m_g.numberOfNodes(); ++i) {
860 if (m_visited[m_nodeFromDFI[i]] == -1) continue;
861 stack.push(-i); // negative numbers: flip=false, otherwise flip=true
863 while (!stack.empty()) {
864 temp = stack.pop();
865 if (temp < 0) {
866 flip=false;
867 v = m_nodeFromDFI[-temp];
868 } else {
869 flip=true;
870 v = m_nodeFromDFI[temp];
872 if (m_visited[v]==-1) continue;
873 // mark visited nodes with visited[v]==-1
874 m_visited[v] = -1;
876 // flip adjEntries of node, if necessary
877 if (m_flipped[v]) {
878 m_flipped[v] = false;
879 flip = !flip;
881 if (flip) m_g.reverseAdjEdges(v);
883 adj=v->firstAdj();
884 while (adj) {
885 w = adj->twinNode();
886 temp = m_edgeType[adj->theEdge()];
887 if (temp==EDGE_DFS) {
888 // go along the dfs-edges
889 stack.push(flip ? m_dfi[w] : -m_dfi[w]);
890 adj=adj->succ();
891 } else if (temp==EDGE_SELFLOOP) {
892 // embed self-loops
893 m_g.moveAdjBefore(adj->twin(),adj);
894 adj=adj->succ();
895 } else if (temp==EDGE_DFS_PARALLEL &&
896 m_adjParent[v]!=0 &&
897 w == m_adjParent[v]->theNode()) {
898 // embed edges that are parallel to dfs-edges
899 // it is only possible to deal with the parallel edges to the
900 // parent, since children nodes could be flipped later
901 adjEntry tmp = adj->succ();
902 m_g.moveAdjAfter(adj,m_adjParent[v]->twin());
903 m_g.moveAdjBefore(adj->twin(),m_adjParent[v]);
904 adj = tmp;
905 } else adj=adj->succ();
912 // tests Graph m_g for planarity
913 // if graph should be embedded, a planar embedding or a kuratowski subdivision
914 // of m_g is returned in addition, depending on whether m_g is planar
915 bool BoyerMyrvoldPlanar::start()
917 BoyerMyrvoldInit bmi(this);
918 bmi.computeDFS();
919 bmi.computeLowPoints();
920 bmi.computeDFSChildLists();
922 // call the embedding procedure
923 return embed();