6 * $Date: 2012-07-15 22:39:24 +0200 (So, 15. Jul 2012) $
7 ***************************************************************/
10 * \brief Implementation of class CompactionConstraintGraphBase.
12 * Represents base class for CompactionConstraintGraph<ATYPE>
14 * \author Carsten Gutwenger
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/orthogonal/CompactionConstraintGraph.h>
47 #include <ogdf/planarity/PlanRep.h>
48 #include <ogdf/basic/simple_graph_alg.h>
49 #include <ogdf/basic/extended_graph_alg.h>
56 // constructor for orthogonal representation
57 // builds constraint graph with basic arcs
58 CompactionConstraintGraphBase::CompactionConstraintGraphBase(
67 m_edgeToBasicArc(OR
,0)
69 OGDF_ASSERT(&PG
== &(const Graph
&)OR
);
72 m_cost
.init(*this,costAssoc
);
73 m_type
.init(*this,cetBasicArc
);
74 m_verticalGen
.init(PG
, false);
75 m_verticalArc
.init(*this, false);
76 m_border
.init(*this, false);
77 m_alignmentArc
.init(*this, false);
78 m_pathToEdge
.init(*this, 0);
79 m_originalEdge
.init(*this, 0);
81 m_pPR
= &PG
;//only used in detecting cage visibility arcs
85 m_oppArcDir
= OR
.oppDir(arcDir
);
86 m_edgeCost
[Graph::generalization
] = costGen
;
87 m_edgeCost
[Graph::association
] = costAssoc
;
92 if ((PG
.typeOf(e
) == Graph::generalization
) && (!PG
.isExpansionEdge(e
)))
93 m_verticalGen
[e
] = true;
96 insertPathVertices(PG
);
101 // insert vertex for each segment
102 void CompactionConstraintGraphBase::insertPathVertices(const PlanRep
&PG
)
104 NodeArray
<node
> genOpposite(PG
,0);
109 const OrthoRep::VertexInfoUML
*vi
= m_pOR
->cageInfo(v
);
110 if (vi
== 0 || PG
.typeOf(v
) == Graph::generalizationMerger
) continue;
112 adjEntry adjGen
= vi
->m_side
[m_arcDir
].m_adjGen
;
113 adjEntry adjOpp
= vi
->m_side
[m_oppArcDir
].m_adjGen
;
114 if (adjGen
!= 0 && adjOpp
!= 0)
116 node v1
= adjGen
->theNode();
117 node v2
= adjOpp
->theNode();
118 genOpposite
[genOpposite
[v1
] = v2
] = v1
;
122 //TODO: hier abfragen, ob Kantensegment einer Originalkante
123 //und diese hat Multikantenstatus => man muss sich die Abstaende am Rand merken
124 //und auf alle Segmente anwenden
126 NodeArray
<bool> visited(PG
,false);
131 node pathVertex
= newNode();
133 dfsInsertPathVertex(v
, pathVertex
, visited
, genOpposite
);
136 //if exact two nodes in path, edge between is original edge segment,
137 //save this to recall the minsep for all segments later over original
138 //muss man hier originaledge als rueckgabe reintun???
139 if ((m_path
[pathVertex
].size() == 2) && m_pathToEdge
[pathVertex
])
142 }//if original segment
143 else m_pathToEdge
[pathVertex
] = 0;
149 // insert all graph vertices into segment pathVertex
150 void CompactionConstraintGraphBase::dfsInsertPathVertex(
153 NodeArray
<bool> &visited
,
154 const NodeArray
<node
> &genOpposite
)
157 m_path
[pathVertex
].pushFront(v
);
158 m_pathNode
[v
] = pathVertex
;
163 OrthoDir dirAdj
= m_pOR
->direction(adj
);
164 OGDF_ASSERT(dirAdj
!= odUndefined
);
166 if (dirAdj
!= m_arcDir
&& dirAdj
!= m_oppArcDir
) {
167 //for multiedges, only useful if only one edge considered on path
168 //maybe zero if no original edge exists
169 if (!m_pathToEdge
[pathVertex
])
171 //only reset later for multi edge segments
172 m_pathToEdge
[pathVertex
] = m_pPR
->original(adj
->theEdge());
173 //used for all vertices
177 node w
= adj
->theEdge()->opposite(v
);
179 dfsInsertPathVertex(w
, pathVertex
, visited
, genOpposite
);
183 node w
= genOpposite
[v
];
184 if (w
!= 0 && !visited
[w
])
185 dfsInsertPathVertex(w
, pathVertex
, visited
, genOpposite
);
191 // insert an arc for each edge with direction m_arcDir
192 void CompactionConstraintGraphBase::insertBasicArcs(const PlanRep
&PG
)
194 const Graph
&G
= *m_pOR
;
199 node start
= m_pathNode
[v
];
203 if (m_pOR
->direction(adj
) == m_arcDir
) {
204 edge e
= newEdge(start
, m_pathNode
[adj
->theEdge()->opposite(v
)]);
205 m_edgeToBasicArc
[adj
] = e
;
207 m_cost
[e
] = m_edgeCost
[PG
.typeOf(adj
->theEdge())];
209 //try to pull nodes up in hierarchies
210 if ( (PG
.typeOf(adj
->theEdge()) == Graph::generalization
) &&
211 (PG
.typeOf(adj
->theEdge()->target()) == Graph::generalizationExpander
) &&
212 !(PG
.isExpansionEdge(adj
->theEdge()))
217 //got to be higher than vertexarccost*doublebendfactor
218 m_cost
[e
] = 4000*m_cost
[e
]; //use parameter later corresponding
219 m_alignmentArc
[e
] = true;
221 //to compconsgraph::doublebendfactor
222 else m_cost
[e
] = 2*m_cost
[e
];
225 //set generalization type
226 if (verticalGen(adj
->theEdge())) m_verticalArc
[e
] = true;
228 if (PG
.isDegreeExpansionEdge(adj
->theEdge()))
230 edge borderE
= adj
->theEdge();
231 node v1
= borderE
->source();
232 node v2
= borderE
->target();
233 m_border
[e
] = ((v1
->degree()>2) && (v2
->degree()>2) ? 2 : 1);
242 // embeds constraint graph such that all sources and sinks lie in a common
244 void CompactionConstraintGraphBase::embed()
246 NodeArray
<bool> onExternal(*this,false);
247 const CombinatorialEmbedding
&E
= *m_pOR
;
248 face fExternal
= E
.externalFace();
251 forall_face_adj(adj
,fExternal
)
252 onExternal
[m_pathNode
[adj
->theNode()]] = true;
254 // compute lists of sources and sinks
255 SList
<node
> sources
, sinks
;
258 forall_nodes(v
,*this) {
262 if (v
->outdeg() == 0)
268 writeGML("c:\\temp\\CCgraphbefore.gml", onExternal
);
271 // determine super source and super sink
273 if (sources
.size() > 1)
276 SListIterator
<node
> it
;
277 for (it
= sources
.begin(); it
.valid(); it
++)
283 if (sinks
.size() > 1)
286 SListIterator
<node
> it
;
287 for (it
= sinks
.begin(); it
.valid(); it
++)
293 edge st
= newEdge(s
,t
);
296 writeGML(String("c:\\temp\\CCgraph%d.gml"));//,randomNumber(0,20)));
297 writeGML("c:\\temp\\CClastgraph.gml");
300 bool isPlanar
= planarEmbed(*this);
301 if (!isPlanar
) OGDF_THROW(AlgorithmFailureException
);
305 if (sources
.size() > 1)
307 if (sinks
.size() > 1)
312 // computes topological numbering on the segments of the constraint graph.
313 // Usage: If used on the basic (and vertex size) arcs, the numbering can be
314 // used in order to serve as sorting criteria for respecting the given
315 // embedding, e.g., when computing visibility arcs and allowing edges
317 void CompactionConstraintGraphBase::computeTopologicalSegmentNum(
318 NodeArray
<int> &topNum
)
320 NodeArray
<int> indeg(*this);
321 StackPure
<node
> sources
;
324 forall_nodes(v
,*this) {
326 indeg
[v
] = v
->indeg();
331 while(!sources
.empty())
333 node v
= sources
.pop();
336 forall_adj_edges(e
,v
) {
337 if(e
->source() != v
) continue;
339 node w
= e
->target();
341 if (topNum
[w
] < topNum
[v
] + 1)
342 topNum
[w
] = topNum
[v
] + 1;
352 class BucketFirstIndex
: public BucketFunc
<Tuple2
<node
,node
> >
355 int getBucket(const Tuple2
<node
,node
> &t
) {
356 return t
.x1()->index();
360 class BucketSecondIndex
: public BucketFunc
<Tuple2
<node
,node
> >
363 int getBucket(const Tuple2
<node
,node
> &t
) {
364 return t
.x2()->index();
369 // remove "arcs" from visibArcs which we already have in the constraint graph
371 void CompactionConstraintGraphBase::removeRedundantVisibArcs(
372 SListPure
<Tuple2
<node
,node
> > &visibArcs
)
374 // bucket sort list of all edges
377 parallelFreeSort(*this,all
);
379 // bucket sort visibArcs
380 BucketFirstIndex bucketSrc
;
381 visibArcs
.bucketSort(0,maxNodeIndex(),bucketSrc
);
383 BucketSecondIndex bucketTgt
;
384 visibArcs
.bucketSort(0,maxNodeIndex(),bucketTgt
);
386 // now, in both lists, arcs are sorted by increasing target index,
387 // and arcs with the same target index by increasing source index.
388 SListConstIterator
<edge
> itAll
= all
.begin();
389 SListIterator
<Tuple2
<node
,node
> > it
, itNext
, itPrev
;
391 // for each arc in visibArcs, we check if it is also contained in list all
392 for(it
= visibArcs
.begin(); it
.valid(); it
= itNext
)
394 // required since we delete from the list we traverse
396 int i
= (*it
).x1()->index();
397 int j
= (*it
).x2()->index();
399 // skip all arcs with smaller target index
400 while(itAll
.valid() && (*itAll
)->target()->index() < j
)
403 // no more arcs => no more duplicates, so return
404 if (!itAll
.valid()) break;
406 // if target index is j, we also skip all arcs with target index i
407 // and source index smaller than i
408 while(itAll
.valid() && (*itAll
)->target()->index() == j
&& (*itAll
)->source()->index() < i
)
411 // no more arcs => no more duplicates, so return
412 if (!itAll
.valid()) break;
414 // if (i,j) is already present, we delete it from visibArcs
415 if ((*itAll
)->source()->index() == i
&&
416 (*itAll
)->target()->index() == j
)
420 visibArcs
.delSucc(itPrev
);
422 visibArcs
.popFront();
427 //****************************CHECK for
428 //special treatment for cage visibility
429 //two cases: input node cage: just compare arbitrary node
430 // merger cage: check first if there are mergers
432 for(it
= visibArcs
.begin(); it
.valid(); it
= itNext
)
437 OGDF_ASSERT(!(m_path
[(*it
).x1()].empty()) && !(m_path
[(*it
).x1()].empty()));
439 node boundRepresentant1
= m_path
[(*it
).x1()].front();
440 node boundRepresentant2
= m_path
[(*it
).x2()].front();
441 node en1
= m_pPR
->expandedNode(boundRepresentant1
);
442 node en2
= m_pPR
->expandedNode(boundRepresentant2
);
443 //do not allow visibility constraints in fixed cages
444 //due to non-planarity with middle position constraints
446 if ( ( en1
&& en2
) && ( en1
== en2
) )
448 if (itPrev
.valid()) visibArcs
.delSucc(itPrev
);
449 else visibArcs
.popFront();
453 //check if its a genmergerspanning vis arc, merge cases later
454 node firstn
= 0, secondn
= 0;
455 SListIterator
< node
> itn
;
456 for (itn
= m_path
[(*it
).x1()].begin(); itn
.valid(); itn
++)
458 node en
= m_pPR
->expandedNode(*itn
);
460 if (!(m_pPR
->typeOf(*itn
) == Graph::generalizationExpander
)) continue;
461 else {firstn
= en
; break;}
463 for (itn
= m_path
[(*it
).x2()].begin(); itn
.valid(); itn
++)
465 node en
= m_pPR
->expandedNode(*itn
);
467 if (!(m_pPR
->typeOf(*itn
) == Graph::generalizationExpander
)) continue;
468 else {secondn
= en
; break;}
470 if ((firstn
&& secondn
) && (firstn
== secondn
))
472 if (itPrev
.valid()) visibArcs
.delSucc(itPrev
);
473 else visibArcs
.popFront();
483 // output in gml-format with special edge colouring
484 // arcs with cost 0 are green, other arcs red
485 void CompactionConstraintGraphBase::writeGML(const char *filename
) const
487 ofstream
os(filename
);
490 void CompactionConstraintGraphBase::writeGML(const char *filename
, NodeArray
<bool> one
) const
492 ofstream
os(filename
);
496 void CompactionConstraintGraphBase::writeGML(ostream
&os
) const
498 const Graph
&G
= *this;
500 NodeArray
<int> id(*this);
503 os
.setf(ios::showpoint
);
506 os
<< "Creator \"ogdf::CompactionConstraintGraphBase::writeGML\"\n";
508 os
<< " directed 1\n";
514 os
<< " id " << (id
[v
] = nextId
++) << "\n";
516 os
<< " graphics [\n";
522 os
<< " fill \"#FFFF00\"\n";
523 os
<< " ]\n"; // graphics
525 os
<< " ]\n"; // node
533 os
<< " source " << id
[e
->source()] << "\n";
534 os
<< " target " << id
[e
->target()] << "\n";
536 // nice idea to use the edge length as edge labels, but Graphwin-
537 // garbage is not able to show the graph (thinks it has to set
538 // the y-coordinates of all nodes to NAN)
545 os
<< " graphics [\n";
547 os
<< " type \"line\"\n";
548 os
<< " arrow \"last\"\n";
551 case cetBasicArc
: // red
552 os
<< " fill \"#FF0000\"\n";
554 case cetVertexSizeArc
: // blue
555 os
<< " fill \"#0000FF\"\n";
557 case cetVisibilityArc
: // green
558 os
<< " fill \"#00FF00\"\n";
560 case cetReducibleArc
: // rose
561 os
<< " fill \"#FF00FF\"\n";
563 case cetFixToZeroArc
: //violett
564 os
<< " fill \"#3F00FF\"\n";
569 os
<< " ]\n"; // graphics
572 os
<< " LabelGraphics [\n";
573 os
<< " type \"text\"\n";
574 os
<< " fill \"#000000\"\n";
575 os
<< " anchor \"w\"\n";
579 os
<< " ]\n"; // edge
582 os
<< "]\n"; // graph
585 void CompactionConstraintGraphBase::writeGML(ostream
&os
, NodeArray
<bool> one
) const
587 const Graph
&G
= *this;
589 NodeArray
<int> id(*this);
592 os
.setf(ios::showpoint
);
595 os
<< "Creator \"ogdf::CompactionConstraintGraphBase::writeGML\"\n";
597 os
<< " directed 1\n";
603 os
<< " id " << (id
[v
] = nextId
++) << "\n";
605 os
<< " graphics [\n";
611 os
<< " fill \"#FF0F0F\"\n";
613 os
<< " fill \"#FFFF00\"\n";
615 os
<< " ]\n"; // graphics
617 os
<< " ]\n"; // node
625 os
<< " source " << id
[e
->source()] << "\n";
626 os
<< " target " << id
[e
->target()] << "\n";
628 // nice idea to use the edge length as edge labels, but Graphwin-
629 // garbage is not able to show the graph (thinks it has to set
630 // the y-coordinates of all nodes to NAN)
637 os
<< " graphics [\n";
639 os
<< " type \"line\"\n";
640 os
<< " arrow \"last\"\n";
643 case cetBasicArc
: // red
644 os
<< " fill \"#FF0000\"\n";
646 case cetVertexSizeArc
: // blue
647 os
<< " fill \"#0000FF\"\n";
649 case cetVisibilityArc
: // green
650 os
<< "fill \"#00FF00\"\n";
652 case cetReducibleArc
: // rose
653 os
<< " fill \"#FF00FF\"\n";
655 case cetFixToZeroArc
: //violett
656 os
<< " fill \"#3F00FF\"\n";
661 os
<< " ]\n"; // graphics
664 os
<< " LabelGraphics [\n";
665 os
<< " type \"text\"\n";
666 os
<< " fill \"#000000\"\n";
667 os
<< " anchor \"w\"\n";
671 os
<< " ]\n"; // edge
674 os
<< "]\n"; // graph
678 } // end namespace ogdf