6 * $Date: 2012-07-15 22:39:24 +0200 (So, 15. Jul 2012) $
7 ***************************************************************/
10 * \brief Implementation of cluster planarity tests and cluster
11 * planar embedding for c-connected clustered graphs. Based on
12 * the algorithm by Cohen, Feng and Eades which uses PQ-trees.
14 * \author Sebastian Leipert
17 * This file is part of the Open Graph Drawing Framework (OGDF).
21 * See README.txt in the root directory of the OGDF installation for details.
24 * This program is free software; you can redistribute it and/or
25 * modify it under the terms of the GNU General Public License
26 * Version 2 or 3 as published by the Free Software Foundation;
27 * see the file LICENSE.txt included in the packaging of this file
31 * This program is distributed in the hope that it will be useful,
32 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 * GNU General Public License for more details.
37 * You should have received a copy of the GNU General Public
38 * License along with this program; if not, write to the Free
39 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
40 * Boston, MA 02110-1301, USA.
42 * \see http://www.gnu.org/copyleft/gpl.html
43 ***************************************************************/
46 #include <ogdf/cluster/CconnectClusterPlanar.h>
47 #include <ogdf/basic/simple_graph_alg.h>
48 #include <ogdf/basic/extended_graph_alg.h>
53 CconnectClusterPlanar::CconnectClusterPlanar()
55 ogdf::strcpy(errorCode
,124,"\0");
60 CconnectClusterPlanar::~CconnectClusterPlanar()
64 // Tests if a ClusterGraph is C-planar
65 // Specifies reason for non planarity
66 bool CconnectClusterPlanar::call(ClusterGraph
&C
, char (&code
)[124])
68 bool cPlanar
= call(C
);
69 strcpy(code
,124,errorCode
);
73 // Tests if a ClusterGraph is C-planar
74 bool CconnectClusterPlanar::call(ClusterGraph
&C
)
78 OGDF_ASSERT(Cp
.consistencyCheck());
81 m_clusterPQTree
.init(Cp
,0);
83 bool cPlanar
= preProcess(Cp
,G
);
85 m_parallelEdges
.init();
87 m_clusterPQTree
.init();
94 // Tests if a ClusterGraph is C-planar
95 bool CconnectClusterPlanar::call(const ClusterGraph
&C
)
99 OGDF_ASSERT(Cp
.consistencyCheck());
100 OGDF_ASSERT(&G
== &(Graph
&) Cp
);
102 m_clusterPQTree
.init(Cp
,0);
105 bool cPlanar
= preProcess(Cp
,G
);
107 m_parallelEdges
.init();
109 m_clusterPQTree
.init();
119 // call(ClusterGraph &C)
121 // preProcess(ClusterGraph &C,Graph &G)
123 // planarityTest(ClusterGraph &C,cluster &act,Graph &G)
125 // foreach ChildCluster
126 // planarityTest(ClusterGraph &C,cluster &act,Graph &G)
128 // preparation(Graph &G,cluster &cl)
130 // foreach biconnected Component
131 // doTest(Graph &G,NodeArray<int> &numbering,cluster &cl)
139 bool CconnectClusterPlanar::preProcess(ClusterGraph
&C
,Graph
&G
)
141 if (!isCConnected(C
))
143 ogdf::sprintf(errorCode
,124,"Graph is not C-connected \n");
144 m_errorCode
= nonCConnected
;
150 ogdf::sprintf(errorCode
,124,"Graph is not planar\n");
151 m_errorCode
= nonPlanar
;
157 SListPure
<node
> selfLoops
;
158 makeLoopFree(G
,selfLoops
);
162 bool cPlanar
= planarityTest(C
,c
,G
);
169 // Recursive call for testing c-planarity of the clustered graph
170 // that is induced by cluster act
171 bool CconnectClusterPlanar::planarityTest(
176 // Test children first
177 ListConstIterator
<cluster
> it
;
178 for (it
= act
->cBegin(); it
.valid();)
180 ListConstIterator
<cluster
> succ
= it
.succ();
181 cluster next
= (*it
);
182 if (!planarityTest(C
,next
,G
))
187 // Get induced subgraph of cluster act and test it for planarity
189 List
<node
> subGraphNodes
;
190 ListIterator
<node
> its
;
191 for (its
= act
->nBegin(); its
.valid(); its
++)
192 subGraphNodes
.pushBack(*its
);
195 NodeArray
<node
> table
;
196 inducedSubGraph(G
,subGraphNodes
.begin(),subGraph
,table
);
199 // Introduce super sink and add edges corresponding
200 // to outgoing edges of the cluster
202 node superSink
= subGraph
.newNode();
203 EdgeArray
<node
> outgoingTable(subGraph
,0);
207 for (its
= act
->nBegin(); its
.valid(); its
++)
210 adjEntry adj
= w
->firstAdj();
213 edge e
= adj
->theEdge();
215 if (table
[e
->source()] == 0) // edge is connected to a node outside the cluster
217 cor
= subGraph
.newEdge(table
[e
->target()],superSink
);
218 outgoingTable
[cor
] = e
->source();
220 else if (table
[e
->target()] == 0) // dito
222 cor
= subGraph
.newEdge(table
[e
->source()],superSink
);
223 outgoingTable
[cor
] = e
->target();
226 // else edge connects two nodes of the cluster
229 if (superSink
->degree() == 0) // root cluster is not connected to outside clusters
231 subGraph
.delNode(superSink
);
236 bool cPlanar
= preparation(subGraph
,act
,superSink
);
239 if (cPlanar
&& act
!= C
.rootCluster())
241 // Remove induced subgraph and the cluster act.
242 // Replace it by a wheel graph
243 while (!subGraphNodes
.empty())
245 node w
= subGraphNodes
.popFrontRet();
246 // C.unassignNode(w);
250 cluster parent
= act
->parent();
252 if (superSink
&& m_clusterPQTree
[act
])
253 constructWheelGraph(C
,G
,parent
,m_clusterPQTree
[act
],outgoingTable
);
256 if (m_clusterPQTree
[act
] != 0) // if query necessary for clusters with just one child
258 m_clusterPQTree
[act
]->emptyAllPertinentNodes();
259 delete m_clusterPQTree
[act
];
265 ogdf::sprintf(errorCode
,124,"Graph is not planar at cluster %d.\n",act
->index());
266 m_errorCode
= nonCPlanar
;
276 void CconnectClusterPlanar::constructWheelGraph(ClusterGraph
&C
,
280 EdgeArray
<node
> &outgoingTable
)
283 const PQNode
<edge
,IndInfo
*,bool>* root
= T
->root();
284 const PQNode
<edge
,IndInfo
*,bool>* checkNode
= 0;
286 Queue
<const PQNode
<edge
,IndInfo
*,bool>*> treeNodes
;
287 treeNodes
.append(root
);
289 node correspond
= G
.newNode(); // Corresponds to the root node.
290 // root node is either a leaf or a P-node
291 C
.reassignNode(correspond
,parent
);
293 Queue
<node
> graphNodes
;
294 graphNodes
.append(correspond
);
299 node newNode
; // corresponds to anchor of a hub or a cut node
303 while (!treeNodes
.empty())
305 checkNode
= treeNodes
.pop();
306 correspond
= graphNodes
.pop();
308 PQNode
<edge
,IndInfo
*,bool>* firstSon
= 0;
309 PQNode
<edge
,IndInfo
*,bool>* nextSon
= 0;
310 PQNode
<edge
,IndInfo
*,bool>* oldSib
= 0;
311 PQNode
<edge
,IndInfo
*,bool>* holdSib
= 0;
314 if (checkNode
->type() == PQNodeRoot::PNode
)
316 // correspond is a cut node
318 OGDF_ASSERT(checkNode
->referenceChild())
319 firstSon
= checkNode
->referenceChild();
321 if (firstSon
->type() != PQNodeRoot::leaf
)
323 treeNodes
.append(firstSon
);
324 newNode
= G
.newNode();
325 C
.reassignNode(newNode
,parent
);
326 graphNodes
.append(newNode
);
327 G
.newEdge(correspond
,newNode
);
331 // insert Edge to the outside
332 PQLeaf
<edge
,IndInfo
*,bool>* leaf
=
333 (PQLeaf
<edge
,IndInfo
*,bool>*) firstSon
;
334 edge f
= leaf
->getKey()->m_userStructKey
;
335 //node x = outgoingTable[f];
336 G
.newEdge(correspond
,outgoingTable
[f
]);
337 delete leaf
->getKey();
340 nextSon
= firstSon
->getNextSib(oldSib
);
343 while (nextSon
&& nextSon
!= firstSon
)
345 if (nextSon
->type() != PQNodeRoot::leaf
)
347 treeNodes
.append(nextSon
);
348 newNode
= G
.newNode(); // new node corresponding to anchor
350 C
.reassignNode(newNode
,parent
);
351 graphNodes
.append(newNode
);
352 G
.newEdge(correspond
,newNode
);
356 // insert Edge to the outside
357 PQLeaf
<edge
,IndInfo
*,bool>* leaf
=
358 (PQLeaf
<edge
,IndInfo
*,bool>*) nextSon
;
359 edge f
= leaf
->getKey()->m_userStructKey
;
360 //node x = outgoingTable[f];
361 G
.newEdge(correspond
,outgoingTable
[f
]);
362 delete leaf
->getKey();
364 holdSib
= nextSon
->getNextSib(oldSib
);
370 else if (checkNode
->type() == PQNodeRoot::QNode
)
372 // correspond is the anchor of a hub
373 OGDF_ASSERT(checkNode
->getEndmost(PQNodeRoot::LEFT
))
374 firstSon
= checkNode
->getEndmost(PQNodeRoot::LEFT
);
377 C
.reassignNode(hub
,parent
);
378 G
.newEdge(hub
,correspond
); // link anchor and hub
379 next
= G
.newNode(); // for first son
380 C
.reassignNode(next
,parent
);
382 G
.newEdge(correspond
,next
);
384 if (firstSon
->type() != PQNodeRoot::leaf
)
386 treeNodes
.append(firstSon
);
387 newNode
= G
.newNode();
388 C
.reassignNode(newNode
,parent
);
389 graphNodes
.append(newNode
);
390 G
.newEdge(next
,newNode
);
394 // insert Edge to the outside
395 PQLeaf
<edge
,IndInfo
*,bool>* leaf
=
396 (PQLeaf
<edge
,IndInfo
*,bool>*) firstSon
;
397 edge f
= leaf
->getKey()->m_userStructKey
;
398 //node x = outgoingTable[f];
399 G
.newEdge(next
,outgoingTable
[f
]);
400 delete leaf
->getKey();
403 nextSon
= firstSon
->getNextSib(oldSib
);
409 C
.reassignNode(next
,parent
);
412 if (nextSon
->type() != PQNodeRoot::leaf
)
414 treeNodes
.append(nextSon
);
415 newNode
= G
.newNode(); // new node corresponding to anchor
417 C
.reassignNode(newNode
,parent
);
418 graphNodes
.append(newNode
);
420 G
.newEdge(next
,newNode
);
424 // insert Edge to the outside
425 PQLeaf
<edge
,IndInfo
*,bool>* leaf
=
426 (PQLeaf
<edge
,IndInfo
*,bool>*) nextSon
;
427 edge f
= leaf
->getKey()->m_userStructKey
;
428 G
.newEdge(next
,outgoingTable
[f
]);
429 delete leaf
->getKey();
431 holdSib
= nextSon
->getNextSib(oldSib
);
437 G
.newEdge(next
,correspond
);
441 OGDF_ASSERT(C
.consistencyCheck());
449 // Prepare planarity test for one cluster
451 bool CconnectClusterPlanar::preparation(Graph
&G
,
458 int bcIdSuperSink
= -1; // ID of biconnected component that contains superSink
459 // Initialization with -1 necessary for assertion
463 NodeArray
<node
> tableNodes(G
,0);
464 EdgeArray
<edge
> tableEdges(G
,0);
465 NodeArray
<bool> mark(G
,0);
467 EdgeArray
<int> componentID(G
);
470 // Determine Biconnected Components
471 int bcCount
= biconnectedComponents(G
,componentID
);
473 // Determine edges per biconnected component
474 Array
<SList
<edge
> > blockEdges(0,bcCount
-1);
477 blockEdges
[componentID
[e
]].pushFront(e
);
480 // Determine nodes per biconnected component.
481 Array
<SList
<node
> > blockNodes(0,bcCount
-1);
482 for (int i
= 0; i
< bcCount
; i
++)
484 SListIterator
<edge
> it
;
485 for (it
= blockEdges
[i
].begin(); it
.valid(); ++it
)
488 if (!mark
[e
->source()])
490 blockNodes
[i
].pushBack(e
->source());
491 mark
[e
->source()] = true;
493 if (!mark
[e
->target()])
495 blockNodes
[i
].pushBack(e
->target());
496 mark
[e
->target()] = true;
499 if (superSink
&& mark
[superSink
])
501 OGDF_ASSERT(bcIdSuperSink
== -1);
504 SListIterator
<node
> itn
;
505 for (itn
= blockNodes
[i
].begin(); itn
.valid(); ++itn
)
512 OGDF_ASSERT(mark
[v
]); // v has been placed two times on the list.
519 // Perform planarity test for every biconnected component
523 // Compute st-numbering
524 NodeArray
<int> numbering(G
,0);
527 n
= stNumber(G
,numbering
,0,superSink
);
529 n
= stNumber(G
,numbering
);
530 OGDF_ASSERT_IF(dlConsistencyChecks
,testSTnumber(G
,numbering
,n
))
533 EdgeArray
<edge
> backTableEdges(G
,0);
535 backTableEdges
[e
] = e
;
537 cPlanar
= doTest(G
,numbering
,cl
,superSink
,backTableEdges
);
541 for (int i
= 0; i
< bcCount
; i
++)
544 if(int(ogdf::debugLevel
)>=int(dlHeavyChecks
)){
545 cout
<<endl
<<endl
<<"-----------------------------------";
546 cout
<<endl
<<endl
<<"Component "<<i
<<endl
;}
551 SListIterator
<node
> itn
;
552 for (itn
= blockNodes
[i
].begin(); itn
.valid(); ++ itn
)
555 node w
= C
.newNode();
559 if(int(ogdf::debugLevel
)>=int(dlHeavyChecks
)){
560 cout
<<"Original: " << v
<< " New: " << w
<< endl
;}
565 NodeArray
<node
> backTableNodes(C
,0);
567 SListIterator
<edge
> it
;
568 for (it
= blockEdges
[i
].begin(); it
.valid(); ++it
)
571 edge f
= C
.newEdge(tableNodes
[e
->source()],tableNodes
[e
->target()]);
575 EdgeArray
<edge
> backTableEdges(C
,0);
576 for (it
= blockEdges
[i
].begin(); it
.valid(); ++it
)
577 backTableEdges
[tableEdges
[*it
]] = *it
;
579 // Compute st-numbering
580 NodeArray
<int> numbering(C
,0);
582 if (bcIdSuperSink
== i
)
584 n
= stNumber(C
,numbering
,0,tableNodes
[superSink
]);
585 OGDF_ASSERT_IF(dlConsistencyChecks
,testSTnumber(C
,numbering
,n
))
586 cPlanar
= doTest(C
,numbering
,cl
,tableNodes
[superSink
],backTableEdges
);
590 n
= stNumber(C
,numbering
);
591 OGDF_ASSERT_IF(dlConsistencyChecks
,testSTnumber(C
,numbering
,n
))
592 cPlanar
= doTest(C
,numbering
,cl
,0,backTableEdges
);
608 // Performs a planarity test on a biconnected component
609 // of G. numbering contains an st-numbering of the component.
610 bool CconnectClusterPlanar::doTest(
612 NodeArray
<int> &numbering
,
615 EdgeArray
<edge
> &edgeTable
)
620 NodeArray
<SListPure
<PlanarLeafKey
<IndInfo
*>* > > inLeaves(G
);
621 NodeArray
<SListPure
<PlanarLeafKey
<IndInfo
*>* > > outLeaves(G
);
622 Array
<node
> table(G
.numberOfNodes()+1);
627 forall_adj_edges(e
,v
)
629 if (numbering
[e
->opposite(v
)] > numbering
[v
])
630 //sideeffect: loops are ignored
632 PlanarLeafKey
<IndInfo
*>* L
= OGDF_NEW PlanarLeafKey
<IndInfo
*>(e
);
633 inLeaves
[v
].pushFront(L
);
636 table
[numbering
[v
]] = v
;
641 SListIterator
<PlanarLeafKey
<IndInfo
*>* > it
;
642 for (it
= inLeaves
[v
].begin(); it
.valid(); ++it
)
644 PlanarLeafKey
<IndInfo
*>* L
= *it
;
645 outLeaves
[L
->userStructKey()->opposite(v
)].pushFront(L
);
649 PlanarPQTree
* T
= new PlanarPQTree();
651 T
->Initialize(inLeaves
[table
[1]]);
652 for (int i
= 2; i
< G
.numberOfNodes(); i
++)
654 if (T
->Reduction(outLeaves
[table
[i
]]))
656 T
->ReplaceRoot(inLeaves
[table
[i
]]);
657 T
->emptyAllPertinentNodes();
666 if (cPlanar
&& cl
&& superSink
)
668 // Keep the PQTree to construct a wheelgraph
669 // Replace the edge stored in the keys of T
670 // by the original edges.
671 // Necessary, since the edges currently in T
672 // correspond to a graph that mirrors a biconnected
673 // component and thus is deallocated
675 SListIterator
<PlanarLeafKey
<IndInfo
*>* > it
;
676 int n
= G
.numberOfNodes();
678 for (it
= outLeaves
[table
[n
]].begin(); it
.valid(); ++it
)
680 PQLeafKey
<edge
,IndInfo
*,bool>* key
= (PQLeafKey
<edge
,IndInfo
*,bool>*) *it
;
681 key
->m_userStructKey
= edgeTable
[key
->m_userStructKey
];
684 m_clusterPQTree
[cl
] = T
;
693 if (v
!= superSink
|| !cPlanar
)
695 while (!outLeaves
[v
].empty())
697 PlanarLeafKey
<IndInfo
*>* L
= outLeaves
[v
].popFrontRet();
708 void CconnectClusterPlanar::prepareParallelEdges(Graph
&G
)
713 // Stores for one reference edge all parallel edges.
714 m_parallelEdges
.init(G
);
715 // Is true for any multiedge, except for the reference edge.
716 m_isParallel
.init(G
,false);
717 getParallelFreeUndirected(G
,m_parallelEdges
);
721 if (!m_parallelEdges
[e
].empty())
723 ListIterator
<edge
> it
;
724 for (it
= m_parallelEdges
[e
].begin(); it
.valid(); it
++)
726 m_isParallel
[*it
] = true;
736 } // end namespace ogdf