6 * $Date: 2012-07-15 22:39:24 +0200 (So, 15. Jul 2012) $
7 ***************************************************************/
10 * \brief implementation of ClusterPlanRep class
12 * \author Karsten Klein
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 ***************************************************************/
43 #include <ogdf/cluster/ClusterPlanRep.h>
44 #include <ogdf/orthogonal/OrthoRep.h>
45 #include <ogdf/basic/simple_graph_alg.h>
46 #include <ogdf/basic/extended_graph_alg.h>
47 #include <ogdf/basic/Layout.h>
48 #include <ogdf/basic/GridLayoutMapped.h>
49 #include <ogdf/basic/tuples.h>
54 enum edgeDir
{undef
, in
, out
};
58 ClusterPlanRep::ClusterPlanRep(
59 const ClusterGraphAttributes
&acGraph
,
60 const ClusterGraph
&clusterGraph
)
63 m_pClusterGraph(&clusterGraph
)
65 OGDF_ASSERT(&clusterGraph
.getGraph() == &acGraph
.constGraph())
67 m_edgeClusterID
.init(*this, -1);
68 m_nodeClusterID
.init(*this, -1);
70 //const Graph &CG = clusterGraph;
71 //const Graph &G = acGraph.constGraph();
75 // OGDF_ASSERT(&CG == &G);
80 //cluster numbers don't need to be consecutive
82 forall_clusters(ci
, clusterGraph
)
83 m_clusterOfIndex
[ci
->index()] = ci
; //numbers are unique
87 void ClusterPlanRep::initCC(int i
)
91 //this means that for every reinitialization IDs are set
92 //again, but this should not lead to problems
93 //it cant be done in the constructor because the copies
94 //in CCs are not yet initialized then
95 //they are maintained for original nodes and for crossings
96 //nodes on cluster boundaries
97 const Graph
&CG
= *m_pClusterGraph
;
101 m_nodeClusterID
[copy(v
)] = m_pClusterGraph
->clusterOf(v
)->index();
104 //todo: initialize dummy node ids for different CCs
106 //initialize all edges totally contained in a single cluster
108 forall_edges(e
, *this)
110 if (ClusterID(e
->source()) == ClusterID(e
->target()))
111 m_edgeClusterID
[e
] = ClusterID(e
->source());
118 * This is only an insertion for graphs with already modeled
119 * boundary edges, otherwise cluster recognition won't work
121 void ClusterPlanRep::insertEdgePathEmbedded(
123 CombinatorialEmbedding
&E
,
124 const SList
<adjEntry
> &crossedEdges
)
127 PlanRep::insertEdgePathEmbedded(eOrig
,E
,crossedEdges
);
129 //update node cluster ids for crossing dummies
130 ListConstIterator
<edge
> it
;
131 for(it
= chain(eOrig
).begin(); it
.valid(); ++it
)
133 node dummy
= (*it
)->target();
134 if (dummy
== copy(eOrig
->target())) continue;
136 OGDF_ASSERT(dummy
->degree() == 4)
138 //get the entries on the crossed edge
139 adjEntry adjIn
= (*it
)->adjTarget();
140 adjEntry adjC1
= adjIn
->cyclicPred();
141 adjEntry adjC2
= adjIn
->cyclicSucc();
143 //insert edge end points have the problem that the next one
144 //does not need to have a clusterid yet
145 //therefore we use the crossed edges endpoints
146 //the two endpoints of the splitted edge
147 node v1
= adjC1
->twinNode();
148 node v2
= adjC2
->twinNode();
149 OGDF_ASSERT(v1
!= dummy
)
150 OGDF_ASSERT(v2
!= dummy
)
151 node orV1
= original(v1
);
152 node orV2
= original(v2
);
153 //inserted edges are never boundaries
155 //cross boundary edge
156 //cross edge between different boundaries
157 //cross edge between boundary and crossing/end
158 //cross edge between crossings/ends
159 //there are three types of nodes: orig, boundary, crossing
160 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
161 //should only be used with modeled boundaries
165 OGDF_ASSERT(m_pClusterGraph
->clusterOf(orV1
) ==
166 m_pClusterGraph
->clusterOf(orV2
));
167 OGDF_ASSERT(m_nodeClusterID
[v1
] != -1)
168 m_nodeClusterID
[dummy
] = m_nodeClusterID
[v1
];
171 if (orV1
|| orV2
) //a dummy (crossing/boundary) and an original
173 node orV
= (orV1
? orV1
: orV2
);
174 //node vOr = (orV1 ? v1 : v2);
175 node vD
= (orV1
? v2
: v1
);
176 cluster orC
= m_pClusterGraph
->clusterOf(orV
);
177 cluster dC
= clusterOfDummy(vD
);
179 OGDF_ASSERT( (orC
== dC
) || //original and crossing
180 (orC
== dC
->parent()) || //original and boundary
181 (orC
->parent() == dC
) )
183 if (orC
== dC
) m_nodeClusterID
[dummy
] = orC
->index();
184 else if (orC
== dC
->parent()) m_nodeClusterID
[dummy
] = orC
->index();
185 else OGDF_THROW (AlgorithmFailureException
);
188 //no originals, only crossings/boundaries
189 c1
= clusterOfDummy(v1
);
190 c2
= clusterOfDummy(v2
);
191 OGDF_ASSERT( (c1
== c2
) || //min.one crossing
192 (c1
== c2
->parent()) || //min. one boundary
193 (c1
->parent() == c2
) ||
194 (c1
->parent() == c2
->parent())
198 m_nodeClusterID
[dummy
] = c1
->index();
199 else if (c1
== c2
->parent())
200 m_nodeClusterID
[dummy
] = c1
->index();
201 else if (c2
== c1
->parent())
202 m_nodeClusterID
[dummy
] = c2
->index();
204 m_nodeClusterID
[dummy
] = c1
->parent()->index();
210 }//insertEdgePathEmbedded
212 //use cluster structure to insert edges representing the cluster boundaries
213 void ClusterPlanRep::ModelBoundaries()
215 //clusters hold their adjacent adjEntries and edges in lists, but after
216 //insertion of boundaries these lists are outdated due to edge splittings
218 //is the clusteradjacent edge outgoing?
219 //2 means undef (only at inner leaf cluster)
220 AdjEntryArray
<int> outEdge(*m_pClusterGraph
, 2); //0 in 1 out
221 //what edge is currently adjacent to cluster (after possible split)
222 //with original adjEntry in clusteradjlist
223 AdjEntryArray
<edge
> currentEdge(*m_pClusterGraph
, 0);
225 List
<adjEntry
> rootEdges
; //edges that can be used to set the outer face
227 convertClusterGraph(m_pClusterGraph
->rootCluster(), currentEdge
, outEdge
);
230 //recursively insert cluster boundaries for all clusters in cluster tree
231 void ClusterPlanRep::convertClusterGraph(cluster act
,
232 AdjEntryArray
<edge
>& currentEdge
,
233 AdjEntryArray
<int>& outEdge
)
236 //are we at the first call (no real cluster)
237 bool isRoot
= (act
== m_pClusterGraph
->rootCluster());
239 //check if we have to set the external face adj by hand
240 if (isRoot
&& !act
->cBegin().valid())
241 m_rootAdj
= firstEdge()->adjSource(); //only root cluster present
242 //check if leaf cluster in cluster tree (most inner)
244 if ((!act
->cBegin().valid()) && (!isRoot
)) isLeaf
= true;
245 // Test children first
246 ListConstIterator
<cluster
> it
;
247 for (it
= act
->cBegin(); it
.valid();)
249 ListConstIterator
<cluster
> succ
= it
.succ();
250 convertClusterGraph((*it
), currentEdge
, outEdge
);
254 //do not convert root cluster
257 OGDF_ASSERT(this->representsCombEmbedding())
259 insertBoundary(act
, currentEdge
, outEdge
, isLeaf
);
261 OGDF_ASSERT(this->representsCombEmbedding())
264 }//convertclustergraph
266 //inserts Boundary for a single cluster, needs the cluster and updates a
267 //hashtable linking splitted original edges (used in clusteradjlist) to the
268 //current (new) edge, if cluster is leafcluster, we check and set the edge
269 //direction and the adjacent edge corresponding to the adjEntries in the clusters
271 void ClusterPlanRep::insertBoundary(cluster C
,
272 AdjEntryArray
<edge
>& currentEdge
,
273 AdjEntryArray
<int>& outEdge
,
276 //we insert edges to represent the cluster boundary
277 //by splitting the outgoing edges and connecting the
280 OGDF_ASSERT(this->representsCombEmbedding())
282 //retrieve the outgoing edges
284 //TODO: nichtverbundene Cluster abfangen
286 SList
<adjEntry
> outAdj
;
287 //outgoing adjEntries in clockwise order
288 m_pClusterGraph
->adjEntries(C
, outAdj
);
290 //now split the edges and save adjEntries
291 //we maintain two lists of adjentries
292 List
<adjEntry
> targetEntries
, sourceEntries
;
293 //we need to find out if edge is outgoing
295 SListIterator
<adjEntry
> it
= outAdj
.begin();
296 //if no outAdj exist, we have a connected component
297 //and dont need a boundary, change this when unconnected
299 if (!it
.valid()) return;
303 //if clusterIsLeaf, save the corresponding direction and edge
306 //save the current, unsplitted edge
307 //be careful with clusterleaf connecting, layered cl
308 if (currentEdge
[(*it
)] == 0)
309 currentEdge
[(*it
)] = copy((*it
)->theEdge());
312 //check direction, adjEntry is outgoing, compare with edge
313 outEdge
[(*it
)] = ( ((*it
) == (*it
)->theEdge()->adjSource()) ? 1 : 0);
316 //workaround for nonleaf edges
317 if (outEdge
[(*it
)] == 2)
318 outEdge
[(*it
)] = ( ((*it
) == (*it
)->theEdge()->adjSource()) ? 1 : 0);
320 if (currentEdge
[(*it
)] == 0)
322 //may already be splitted from head
323 currentEdge
[(*it
)] = copy((*it
)->theEdge());
327 //We need to find the real edge here
328 edge splitEdge
= currentEdge
[(*it
)];
331 OGDF_ASSERT(outEdge
[(*it
)] != 2);
332 isOut
= outEdge
[(*it
)] == 1;
334 edge newEdge
= split(splitEdge
);
336 //store the adjEntries depending on in/out direction
339 //splitresults "upper" edge to old target is newEdge!?
340 //only update for outgoing edges, ingoing stay actual
341 currentEdge
[(*it
)] = newEdge
;
342 currentEdge
[(*it
)->twin()] = newEdge
;
343 sourceEntries
.pushBack(newEdge
->adjSource());
344 targetEntries
.pushBack(splitEdge
->adjTarget());
346 m_nodeClusterID
[newEdge
->source()] = C
->index();
347 //m_nodeClusterID[splitEdge->source()];
351 sourceEntries
.pushBack(splitEdge
->adjTarget());
352 targetEntries
.pushBack(newEdge
->adjSource());
354 m_nodeClusterID
[newEdge
->source()] = C
->index();
357 //always set some rootAdj for external face
358 if ( (C
->parent() == m_pClusterGraph
->rootCluster()) && !(it
.succ().valid()))
360 //save the adjentry corresponding to new splitresult edge
361 m_rootAdj
= currentEdge
[(*it
)]->adjSource();
362 OGDF_ASSERT(m_rootAdj
!= 0);
365 //go on with next edge
371 //we need pairs of adjEntries
372 OGDF_ASSERT(targetEntries
.size() == sourceEntries
.size());
373 //now flip first target entry to front
375 adjEntry flipper
= targetEntries
.popFrontRet();
376 targetEntries
.pushBack(flipper
);
378 //connect the new nodes to form the boundary
379 while (!targetEntries
.empty())
382 edge e
= newEdge(sourceEntries
.popFrontRet(), targetEntries
.popFrontRet());
383 //set type of new edges
384 setClusterBoundary(e
);
385 m_edgeClusterID
[e
] = C
->index();
387 OGDF_ASSERT(this->representsCombEmbedding())
390 OGDF_ASSERT(this->representsCombEmbedding())
396 void ClusterPlanRep::expand(bool lowDegreeExpand
)
398 PlanRep::expand(lowDegreeExpand
);
399 //update cluster info
401 forall_nodes(v
, *this)
403 if (expandedNode(v
) != 0)
405 OGDF_ASSERT(m_nodeClusterID
[expandedNode(v
)] != -1)
406 m_nodeClusterID
[v
] = m_nodeClusterID
[expandedNode(v
)];
411 void ClusterPlanRep::expandLowDegreeVertices(OrthoRep
&OR
)
413 PlanRep::expandLowDegreeVertices(OR
);
414 //update cluster info
416 forall_nodes(v
, *this)
418 if (expandedNode(v
) != 0)
420 OGDF_ASSERT(m_nodeClusterID
[expandedNode(v
)] != -1)
421 m_nodeClusterID
[v
] = m_nodeClusterID
[expandedNode(v
)];
427 //*****************************************************************************
430 void ClusterPlanRep::writeGML(const char *fileName
, const Layout
&drawing
)
432 ofstream
os(fileName
);
433 writeGML(os
,drawing
);
437 void ClusterPlanRep::writeGML(const char *fileName
)
439 Layout
drawing(*this);
440 ofstream
os(fileName
);
441 writeGML(os
,drawing
);
445 void ClusterPlanRep::writeGML(ostream
&os
, const Layout
&drawing
)
447 const Graph
&G
= *this;
449 NodeArray
<int> id(*this);
452 os
.setf(ios::showpoint
);
455 os
<< "Creator \"ogdf::GraphAttributes::writeGML\"\n";
457 os
<< " directed 1\n";
462 node ori
= original(v
);
466 os
<< " id " << (id
[v
] = nextId
++) << "\n";
468 os
<< " graphics [\n";
469 os
<< " x " << drawing
.x(v
) << "\n";
470 os
<< " y " << drawing
.y(v
) << "\n";
471 os
<< " w " << 10.0 << "\n";
472 os
<< " h " << 10.0 << "\n";
473 os
<< " type \"rectangle\"\n";
474 os
<< " width 1.0\n";
475 if (typeOf(v
) == Graph::generalizationMerger
) {
476 os
<< " type \"oval\"\n";
477 os
<< " fill \"#0000A0\"\n";
479 else if (typeOf(v
) == Graph::generalizationExpander
) {
480 os
<< " type \"oval\"\n";
481 os
<< " fill \"#00FF00\"\n";
483 else if (typeOf(v
) == Graph::highDegreeExpander
||
484 typeOf(v
) == Graph::lowDegreeExpander
)
485 os
<< " fill \"#FFFF00\"\n";
486 else if (typeOf(v
) == Graph::dummy
)
487 os
<< " type \"oval\"\n";
490 if (m_pClusterGraph
->clusterOf(ori
)->index() != 0) //cluster
493 os
<< " fill \"#" << std::hex
<< std::setw(6) << std::setfill('0')
494 << m_pClusterGraph
->clusterOf(ori
)->index()*256*256+
495 m_pClusterGraph
->clusterOf(ori
)->index()*256+
496 m_pClusterGraph
->clusterOf(ori
)->index()*4 << std::dec
<< "\"\n";
501 os
<< " fill \"#FFFF00\"\n";
504 os
<< " fill \"#000000\"\n";
507 os
<< " ]\n"; // graphics
509 os
<< " ]\n"; // node
517 os
<< " source " << id
[e
->source()] << "\n";
518 os
<< " target " << id
[e
->target()] << "\n";
520 os
<< " generalization " << typeOf(e
) << "\n";
522 os
<< " graphics [\n";
524 os
<< " type \"line\"\n";
526 if (typeOf(e
) == Graph::generalization
)
528 os
<< " arrow \"last\"\n";
530 os
<< " fill \"#FF0000\"\n";
531 os
<< " width 3.0\n";
536 if (typeOf(e
->source()) == Graph::generalizationExpander
||
537 typeOf(e
->source()) == Graph::generalizationMerger
||
538 typeOf(e
->target()) == Graph::generalizationExpander
||
539 typeOf(e
->target()) == Graph::generalizationMerger
)
541 os
<< " arrow \"none\"\n";
543 os
<< " fill \"#F0F000\"\n"; //gelb
544 else if (isHalfBrother(e
))
545 os
<< " fill \"#FF00AF\"\n";
546 else if (isClusterBoundary(e
))
547 os
<< " fill \"#FF0000\"\n";
549 os
<< " fill \"#FF0000\"\n";
552 os
<< " arrow \"none\"\n";
554 os
<< " fill \"#F0F000\"\n"; //gelb
555 else if (isHalfBrother(e
))
556 os
<< " fill \"#FF00AF\"\n";
557 else if (isClusterBoundary(e
))
558 os
<< " fill \"#FF0000\"\n";
560 os
<< " fill \"#00000F\"\n";
561 os
<< " width 1.0\n";
562 }//else generalization
563 os
<< " ]\n"; // graphics
565 os
<< " ]\n"; // edge
568 os
<< "]\n"; // graph