6 * $Date: 2012-07-06 15:04:28 +0200 (Fr, 06. Jul 2012) $
7 ***************************************************************/
10 * \brief implementation of the class BoyerMyrvoldPlanar
12 * \author Jens Schmidt
15 * This file is part of the Open Graph Drawing Framework (OGDF).
19 * See README.txt in the root directory of the OGDF installation for details.
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
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.
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>
53 BoyerMyrvoldPlanar::BoyerMyrvoldPlanar(
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)
64 m_embeddingGrade(embeddingGrade
),
65 m_limitStructures(limitStructures
),
66 m_randomDFSTree(randomDFSTree
),
67 m_avoidE2Minors(avoidE2Minors
),
69 // BoyerMyrvoldInit members
72 m_nodeFromDFI(-g
.numberOfNodes(),g
.numberOfNodes(),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
87 m_link
[CCW
].init(g
,0);
89 m_beforeSCE
[CCW
].init(g
,0);
90 m_beforeSCE
[CW
].init(g
,0);
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
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
)
110 OGDF_ASSERT(w
->degree()>0);
111 OGDF_ASSERT(m_link
[CW
][w
]!=0 && m_link
[CCW
][w
]!=0);
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);
123 direction
= adj
==beforeShortCircuitEdge(next
,CCW
)->twin();
125 info
= infoAboutNode(next
,v
);
127 } while (info
==0); // until not inactive
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
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();
158 mergeEntry
=mergeEntry
->cyclicSucc();
163 // set unique DFS-child of associated bicomp root node to "flipped"
164 m_flipped
[w_child
] = true;
167 mergeEntry
= mergeEntry
->cyclicPred();
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
);
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;
204 // the same as mergeBiconnectedComponent, but without any embedding-related
206 void BoyerMyrvoldPlanar::mergeBiconnectedComponentOnlyPlanar(
207 StackPure
<int>& stack
,
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();
229 OGDF_ASSERT(e
->source() != x
&& e
->target() != x
);
230 // this allows also self-loops when moving adjacency entries
231 if (e
->source() == w
) {
233 } else m_g
.moveTarget(e
,x
);
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;
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(
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
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
);
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
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(
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
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
) {
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
350 // insert backedge to v
355 // set external face link for this backedge and delete out-dated short
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
369 void BoyerMyrvoldPlanar::createShortCircuitEdge(
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
;
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(
394 const int i
= m_dfi
[v
];
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) {
413 } else if (m_realVertex
[y
] != 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
];
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
433 m_visited
[x
] = marker
;
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
;
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
;
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
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
);
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);
499 // get active successor in pertinent bicomp
500 // variables for recognizing the right direction after descending to a bicomp
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
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
) {
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
)) {
539 node lastActiveNode
= w
;
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
556 // go on with walkdown on unvisited active node
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
) {
571 // different stopping nodes:
572 // try to extract kuratowski structure and put the two
573 // stopping nodes in the right traversal order
575 findKuratowskis
->addKuratowskiStructure(m_nodeFromDFI
[i
],
576 w
,lastActiveNode
,otherActiveNode
);
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();
590 // if both attributes are the same: minimize flips
591 else if (w_dir
==CCW
) {
601 } else if (infoX
<= infoY
) {
612 } else if (inactive(w
,i
)) {
613 // w is an inactive vertex
614 w
= successorOnExternalFace(w
,w_dir
);
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
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
) {
641 findKuratowskis
->addKuratowskiStructure(m_nodeFromDFI
[i
],v
,stopX
,w
);
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
];
669 // for all sources of backedges of v: find pertinent subgraph
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;
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
];
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
);
704 m_output
= findKuratowskis
->getAllKuratowskis();
705 delete findKuratowskis
;
707 } else if (result
== 1) {
708 // found stopping configuration
710 if (m_embeddingGrade
<= doNotFind
) return false;
714 // if !embed, check, if there are any backedges left
715 if (m_embeddingGrade
<= doNotFind
) {
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
727 m_output
= findKuratowskis
->getAllKuratowskis();
729 postProcessEmbedding(); // flip graph and embed self-loops, etc.
731 delete findKuratowskis
;
736 // merge unprocessed virtual nodes such as the dfs-roots
737 void BoyerMyrvoldPlanar::mergeUnprocessedNodes()
739 node v
= m_g
.firstNode();
741 node next
= v
->succ();
743 node w
= m_realVertex
[v
];
744 adjEntry adj
= v
->firstAdj();
745 // copy all adjEntries to non-virtual node
747 edge e
= adj
->theEdge();
749 if (e
->source()==v
) {
751 } else m_g
.moveTarget(e
,w
);
753 m_nodeFromDFI
[m_dfi
[v
]]=0;
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(
770 NodeArray
<int>& visited
,
772 bool deleteFlipFlags
)
774 if (m_flippedNodes
== 0) {
775 if (wholeGraph
) mergeUnprocessedNodes();
779 StackPure
<int> stack
; // stack for dfs-traversal
785 mergeUnprocessedNodes();
786 for (int i
= 1; i
<= m_g
.numberOfNodes(); ++i
)
790 // flip bicomps, if the flipped-flag is set
792 stack
.push(-c
); // negative numbers: flip=false, otherwise flip=true
793 while (!stack
.empty()) {
797 v
= m_nodeFromDFI
[-temp
];
800 v
= m_nodeFromDFI
[temp
];
803 if (visited
[v
]==marker
) continue;
804 // mark visited nodes
808 // flip adjEntries of node, if necessary
812 // don't do this, if all flips on nodes of this bicomp will be reversed
813 if (deleteFlipFlags
) {
814 m_flipped
[v
] = false;
816 OGDF_ASSERT(m_flippedNodes
>= 0);
820 m_g
.reverseAdjEdges(v
);
821 if (deleteFlipFlags
) {
822 adjEntry tmp
= m_link
[CCW
][v
];
823 m_link
[CCW
][v
] = m_link
[CW
][v
];
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
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
854 mergeUnprocessedNodes();
856 // flip bicomps, if the flipped-flag is set, i.e. postprocessing in
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()) {
867 v
= m_nodeFromDFI
[-temp
];
870 v
= m_nodeFromDFI
[temp
];
872 if (m_visited
[v
]==-1) continue;
873 // mark visited nodes with visited[v]==-1
876 // flip adjEntries of node, if necessary
878 m_flipped
[v
] = false;
881 if (flip
) m_g
.reverseAdjEdges(v
);
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
]);
891 } else if (temp
==EDGE_SELFLOOP
) {
893 m_g
.moveAdjBefore(adj
->twin(),adj
);
895 } else if (temp
==EDGE_DFS_PARALLEL
&&
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
]);
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);
919 bmi
.computeLowPoints();
920 bmi
.computeDFSChildLists();
922 // call the embedding procedure