6 * $Date: 2012-07-12 02:38:07 +0200 (Do, 12. Jul 2012) $
7 ***************************************************************/
10 * \brief Declaration of the class BoyerMyrvoldPlanar
12 * \author Jens Schmidt
16 * This file is part of the Open Graph Drawing Framework (OGDF).
20 * See README.txt in the root directory of the OGDF installation for details.
23 * This program is free software; you can redistribute it and/or
24 * modify it under the terms of the GNU General Public License
25 * Version 2 or 3 as published by the Free Software Foundation;
26 * see the file LICENSE.txt included in the packaging of this file
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
36 * You should have received a copy of the GNU General Public
37 * License along with this program; if not, write to the Free
38 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
39 * Boston, MA 02110-1301, USA.
41 * \see http://www.gnu.org/copyleft/gpl.html
42 ***************************************************************/
49 #ifndef OGDF_BOYER_MYRVOLD_PLANAR_H
50 #define OGDF_BOYER_MYRVOLD_PLANAR_H
52 #include <ogdf/basic/Graph_d.h>
53 #include <ogdf/basic/NodeArray.h>
54 #include <ogdf/basic/Stack.h>
55 #include <ogdf/basic/List.h>
56 #include <ogdf/basic/SList.h>
61 //! Directions for clockwise and counterclockwise traversal
68 /** @param 0 undefined
72 * @param 4 parallel DFS-edge
73 * @param 5 deleted backedge
84 class KuratowskiStructure
;
85 class FindKuratowskis
;
87 //! This class implements the extended BoyerMyrvold planarity embedding algorithm
88 class BoyerMyrvoldPlanar
90 friend class BoyerMyrvold
;
91 friend class BoyerMyrvoldInit
;
92 friend class FindKuratowskis
;
93 friend class ExtractKuratowskis
;
96 //! Constructor, for parameters see BoyerMyrvold
100 int m_embeddingGrade
,
101 bool limitStructures
,
102 SListPure
<KuratowskiStructure
>& output
,
107 ~BoyerMyrvoldPlanar() { }
109 //! Starts the embedding algorithm
112 //! Denotes the different embedding options
113 enum enumEmbeddingGrade
{
114 doNotEmbed
=-3, // and not find any kuratowski subdivisions
115 doNotFind
=-2, // but embed
116 doFindUnlimited
=-1, // and embed
117 doFindZero
=0 // and embed
120 //! Flips all nodes of the bicomp with unique, real, rootchild c as necessary
121 /** @param c is the unique rootchild of the bicomp
122 * @param marker is the value which marks nodes as visited
123 * @param visited is the array containing visiting information
124 * @param wholeGraph Iff true, all bicomps of all connected components will be traversed
125 * @param deleteFlipFlags Iff true, the flipping flags will be deleted after flipping
130 NodeArray
<int>& visited
,
132 bool deleteFlipFlags
);
134 // avoid automatic creation of assignment operator
135 //! Assignment operator is undefined!
136 BoyerMyrvoldPlanar
&operator=(const BoyerMyrvoldPlanar
&);
139 /***** Methods for Walkup and Walkdown ******/
141 //! Checks whether node \a w is pertinent. \a w has to be non-virtual.
142 inline bool pertinent(node w
) {
143 OGDF_ASSERT(w
!=NULL
);
144 if (m_dfi
[w
] <= 0) return false;
145 return (!m_backedgeFlags
[w
].empty() || !m_pertinentRoots
[w
].empty());
148 //! Checks whether real node \a w is internally active while embedding node with DFI \a v
149 inline bool internallyActive(node w
, int v
) {
150 OGDF_ASSERT(w
!=NULL
);
151 if (m_dfi
[w
] <= 0) return false;
152 return (pertinent(w
) && !externallyActive(w
,v
));
155 //! Checks whether real node \a w is externally active while embedding node with DFI \a v
156 inline bool externallyActive(node w
, int v
) {
157 OGDF_ASSERT(w
!=NULL
);
158 if (m_dfi
[w
] <= 0) return false;
159 if (m_leastAncestor
[w
] < v
) return true;
160 if (m_separatedDFSChildList
[w
].empty()) return false;
161 return (m_lowPoint
[m_separatedDFSChildList
[w
].front()] < v
);
164 //! Checks whether real node \a w is inactive while embedding node with DFI \a v
165 inline bool inactive(node w
, int v
) {
166 OGDF_ASSERT(w
!=NULL
);
167 if (m_dfi
[w
] <= 0) return true;
168 if (!m_backedgeFlags
[w
].empty() || !m_pertinentRoots
[w
].empty()
169 || m_leastAncestor
[w
] < v
) return false;
170 if (m_separatedDFSChildList
[w
].empty()) return true;
171 return (m_lowPoint
[m_separatedDFSChildList
[w
].front()] >= v
);
174 //! Checks all dynamic information about a node \a w while embedding node with DFI \a v
176 * @return This method returns the following values:
178 * - 1 = internallyActive
179 * - 2 = pertinent and externallyActive
180 * - 3 = externallyActive and not pertinent
182 inline int infoAboutNode(node w
, int v
) {
183 OGDF_ASSERT(w
!=NULL
);
184 if (m_dfi
[w
] <= 0) return 0;
185 if (!m_pertinentRoots
[w
].empty() || !m_backedgeFlags
[w
].empty()) {
187 if (m_leastAncestor
[w
] < v
) return 2;
188 if (m_separatedDFSChildList
[w
].empty()) return 1;
189 return (m_lowPoint
[m_separatedDFSChildList
[w
].front()] < v
193 if (m_leastAncestor
[w
] < v
) return 3;
194 if (m_separatedDFSChildList
[w
].empty()) return 0;
195 return (m_lowPoint
[m_separatedDFSChildList
[w
].front()] < v
200 //! Walks upon external face in the given \a direction starting at \a w
201 /** If none of the bicomps has been flipped then CW = clockwise and
202 * CCW = counterclockwise holds. In general, the traversaldirection could have
203 * been changed due to flipped components. If this occurs, the
204 * traversaldirection is flipped.
206 inline node
successorOnExternalFace(node w
, int& direction
) {
207 OGDF_ASSERT(w
!=NULL
);
208 OGDF_ASSERT(w
->degree()>0);
209 OGDF_ASSERT(m_link
[CW
][w
]!=NULL
&& m_link
[CCW
][w
]!=NULL
);
210 adjEntry adj
= m_link
[direction
][w
];
211 OGDF_ASSERT(adj
->theNode()!=NULL
);
213 if (w
->degree() > 1) direction
=
214 adj
==beforeShortCircuitEdge(adj
->theNode(),CCW
)->twin();
215 OGDF_ASSERT(direction
|| adj
==beforeShortCircuitEdge(adj
->theNode(),CW
)->twin());
216 return adj
->theNode();
219 //! Walks upon external face in given \a direction avoiding short circuit edges
220 inline node
successorWithoutShortCircuit(node w
, int& direction
) {
221 OGDF_ASSERT(w
!=NULL
);
222 OGDF_ASSERT(w
->degree()>0);
223 OGDF_ASSERT(m_link
[CW
][w
]!=NULL
&& m_link
[CCW
][w
]!=NULL
);
224 adjEntry adj
= beforeShortCircuitEdge(w
,direction
);
225 OGDF_ASSERT(adj
->theNode()!=NULL
);
227 if (w
->degree() > 1) direction
=
228 adj
==beforeShortCircuitEdge(adj
->theNode(),CCW
)->twin();
229 OGDF_ASSERT(direction
|| adj
==beforeShortCircuitEdge(adj
->theNode(),CW
)->twin());
230 return adj
->theNode();
233 //! Returns the successornode on the external face in given \a direction
234 /** \a direction is not changed.
236 inline node
constSuccessorOnExternalFace(node v
, int direction
) {
237 OGDF_ASSERT(v
!=NULL
);
238 OGDF_ASSERT(v
->degree()>0);
239 return m_link
[direction
][v
]->theNode();
242 //! Walks upon external face in \a direction avoiding short circuit edges
243 /** \a direction is not changed.
245 inline node
constSuccessorWithoutShortCircuit(node v
, int direction
) {
246 OGDF_ASSERT(v
!=NULL
);
247 OGDF_ASSERT(v
->degree()>0);
248 return beforeShortCircuitEdge(v
,direction
)->theNode();
251 //! Returns underlying former adjEntry, if a short circuit edge in \a direction of \a v exists
252 /** Otherwise the common edge is returned. In every case the returned adjEntry
253 * points to the targetnode.
255 inline adjEntry
beforeShortCircuitEdge(node v
, int direction
) {
256 OGDF_ASSERT(v
!=NULL
);
257 return (m_beforeSCE
[direction
][v
]==NULL
) ? m_link
[direction
][v
] : m_beforeSCE
[direction
][v
];
260 //! Walks upon external face in the given \a direction starting at \a w until an active vertex is reached
261 /** Returns dynamical typeinformation \a info of that endvertex.
263 node
activeSuccessor(node w
, int& direction
, int v
, int& info
);
265 //! Walks upon external face in the given \a direction (without changing it) until an active vertex is reached
266 /** Returns dynamical typeinformation \a info of that endvertex. But does not change the \a direction.
268 inline node
constActiveSuccessor(node w
, int direction
, int v
, int& info
) {
269 return activeSuccessor(w
,direction
,v
,info
);
272 //! Checks, if one ore more wNodes exist between the two stopping vertices \a stopx and \a stopy
273 /** The node \a root is root of the bicomp containing the stopping vertices
275 inline bool wNodesExist(node root
, node stopx
, node stopy
) {
276 OGDF_ASSERT(root
!= stopx
&& root
!= stopy
&& stopx
!= stopy
);
278 bool between
= false;
279 while (root
!= NULL
) {
280 root
= successorOnExternalFace(root
,dir
);
281 if (between
&& pertinent(root
)) {
284 if (root
== stopx
|| root
== stopy
) {
294 //! Prints informations about node \a v
295 inline void printNodeInfo(node v
) {
296 cout
<< "\nprintNodeInfo(" << m_dfi
[v
] << "): ";
297 cout
<< "CCW=" << m_dfi
[constSuccessorOnExternalFace(v
,CCW
)];
298 cout
<< ",CW=" << m_dfi
[constSuccessorOnExternalFace(v
,CW
)];
299 cout
<< "\tCCWBefore=" << m_dfi
[constSuccessorWithoutShortCircuit(v
,CCW
)];
300 cout
<< ",CWBefore=" << m_dfi
[constSuccessorWithoutShortCircuit(v
,CW
)];
301 cout
<< "\tadjList: ";
303 for (adj
= v
->firstAdj(); adj
; adj
= adj
->succ()) {
304 cout
<< adj
->twinNode() << " ";
308 //! Merges the last two biconnected components saved in \a stack while embedding them
309 /** \a j is the outgoing traversal direction of the current node to embed
311 void mergeBiconnectedComponent(StackPure
<int>& stack
, const int j
);
313 //! Merges the last two biconnected components saved in \a stack without embedding them
314 /** \a j is the outgoing traversal direction of the current node to embed
316 void mergeBiconnectedComponentOnlyPlanar(StackPure
<int>& stack
, const int j
);
318 //! Embeds backedges from node \a v with direction \a v_dir to node \a w with direction \a w_dir
319 /** \a i is the DFI of current embedded node.
321 void embedBackedges(const node v
, const int v_dir
,
322 const node w
, const int w_dir
, const int i
);
324 //! Links (not embed) backedges from node \a v with direction \a v_dir to node \a w with direction \a w_dir
325 /** \a i is the DFI of current embedded node.
327 void embedBackedgesOnlyPlanar(const node v
, const int v_dir
,
328 const node w
, const int w_dir
, const int i
);
330 //! Creates a short circuit edge from node \a v with direction \a v_dir to node \a w with direction \a w_dir
331 void createShortCircuitEdge(const node v
, const int v_dir
,
332 const node w
, const int w_dir
);
334 //! Walkup: Builds the pertinent subgraph for the backedge \a back.
335 /** \a back is the backedge between nodes \a v and \a w. \a v is the current node to embed.
336 * All visited nodes are marked with value \a marker. The Function returns the last traversed node.
338 node
walkup(const node v
, const node w
, const int marker
, const edge back
);
340 //! Walkdown: Embeds all backedges with DFI \a i as targetnode to node \a v
342 * @param i is the DFI of the current vertex to embed
343 * @param v is the virtual node being the root of the bicomp attached to \a i
344 * @param findKuratowskis collects information in order to extract Kuratowski Subdivisions later
345 * @return 1, iff the embedding process found a stopping configuration
347 int walkdown(const int i
, const node v
, FindKuratowskis
* findKuratowskis
);
349 //! Merges unprocessed virtual nodes such as the dfs-roots with their real counterpart
350 void mergeUnprocessedNodes();
352 //! Postprocessing of the graph, so that all virtual vertices are embedded and all bicomps are flipped
353 /** In addition, embedding steps for parallel edges and self-loops are implemented.
355 void postProcessEmbedding();
357 //! Starts the embedding phase, which embeds \a m_g node by node in descending DFI-order.
358 /** Returns true, if graph is planar, false otherwise.
363 /***** Members ******/
364 //! Input graph, which can be altered
367 //! Some parameters... see BoyerMyrvold for further options
368 const bool m_bundles
;
369 const int m_embeddingGrade
;
370 const bool m_limitStructures
;
371 const bool m_randomDFSTree
;
372 const bool m_avoidE2Minors
;
374 //! The whole number of bicomps, which have to be flipped
377 /***** Members from Boyer Myrvold-Init ******/
378 //! Link to non-virtual vertex of a virtual Vertex.
379 /** A virtual vertex has negative DFI of the DFS-Child of related non-virtual Vertex
381 NodeArray
<node
> m_realVertex
;
383 //! The one and only DFI-NodeArray
384 NodeArray
<int> m_dfi
;
386 //! Returns appropriate node from given DFI
387 Array
<node
> m_nodeFromDFI
;
389 //! Links to opposite adjacency entries on external face in clockwise resp. ccw order
390 /** m_link[0]=CCW, m_link[1]=CW
392 NodeArray
<adjEntry
> m_link
[2];
394 //! Links for short circuit edges.
395 /** If short circuit edges are introduced, the former adjEntries to the neighbors
396 * have to be saved here for embedding and merging purposes. If there is no
397 * short circuit edge, this adjEntry is NULL.
399 NodeArray
<adjEntry
> m_beforeSCE
[2];
401 //! The adjEntry which goes from DFS-parent to current vertex
402 NodeArray
<adjEntry
> m_adjParent
;
404 //! The DFI of the least ancestor node over all backedges
405 /** If no backedge exists, the least ancestor is the DFI of that node itself
407 NodeArray
<int> m_leastAncestor
;
409 //! Contains the type of each \a edge
410 /** @param 0 = EDGE_UNDEFINED
411 * @param 1 = EDGE_SELFLOOP
412 * @param 2 = EDGE_BACK
413 * @param 3 = EDGE_DFS
414 * @param 4 = EDGE_DFS_PARALLEL
415 * @param 5 = EDGE_BACK_DELETED
417 EdgeArray
<int> m_edgeType
;
419 //! The lowpoint of each \a node
420 NodeArray
<int> m_lowPoint
;
422 //! The highest DFI in a subtree with \a node as root
423 NodeArray
<int> m_highestSubtreeDFI
;
425 //! A list to all separated DFS-children of \a node
426 /** The list is sorted by lowpoint values (in linear time)
428 NodeArray
<ListPure
<node
> > m_separatedDFSChildList
;
430 //! Pointer to \a node contained in the DFSChildList of his parent, if exists.
431 /** If node isn't in list or list doesn't exist, the pointer is set to NULL.
433 NodeArray
<ListIterator
<node
> > m_pNodeInParent
;
435 /***** Members for Walkup and Walkdown ******/
436 //! This Array keeps track of all vertices that are visited by current walkup
437 NodeArray
<int> m_visited
;
439 //! Identifies the rootnode of the child bicomp the given backedge points to
440 EdgeArray
<node
> m_pointsToRoot
;
442 //! Keeps track of all vertices that are visited by the walkup through a specific backedge
443 /** This is done in order to refer to the unique child-bicomp of v.
445 NodeArray
<int> m_visitedWithBackedge
;
447 //! Iff true, the node is the root of a bicomp which has to be flipped.
448 /** The DFS-child of every bicomp root vertex is unique. if a bicomp
449 * is flipped, this DFS-child is marked to check whether the bicomp
450 * has to be flipped or not.
452 NodeArray
<bool> m_flipped
;
454 //! Holds information, if node is the source of a backedge.
455 /** This information refers to the adjEntries on the targetnode
456 * and is used during the walkdown
458 NodeArray
<SListPure
<adjEntry
> > m_backedgeFlags
;
460 //! List of virtual bicomp roots, that are pertinent to the current embedded node
461 NodeArray
<SListPure
<node
> > m_pertinentRoots
;
463 //! Data structure for the kuratowski subdivisions, which will be returned
464 SListPure
<KuratowskiStructure
>& m_output
;