1 /** \file
2 * \brief implementation of PlanRepUML class
3 *
4 * \author Carsten Gutwenger
5 *
6 * \par License:
7 * This file is part of the Open Graph Drawing Framework (OGDF).
8 *
9 * \par
10 * Copyright (C)<br>
11 * See README.md in the OGDF root directory for details.
12 *
13 * \par
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * Version 2 or 3 as published by the Free Software Foundation;
17 * see the file LICENSE.txt included in the packaging of this file
18 * for details.
19 *
20 * \par
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * \par
27 * You should have received a copy of the GNU General Public
28 * License along with this program; if not, see
29 * http://www.gnu.org/copyleft/gpl.html
30 */
31
32
33 #include <ogdf/uml/PlanRepUML.h>
34 #include <ogdf/basic/GridLayoutMapped.h>
35
36
37 namespace ogdf {
38
PlanRepUML(const UMLGraph & umlGraph)39 PlanRepUML::PlanRepUML(const UMLGraph ¨Graph) :
40 PlanRep(umlGraph),
41 m_pUmlGraph(¨Graph)
42 {
43 m_alignUpward .init(*this, false);
44 m_faceSplitter.init(*this,false);
45 m_incMergers .init(m_ccInfo.numberOfCCs());
46 }
47
48
PlanRepUML(const GraphAttributes & GA)49 PlanRepUML::PlanRepUML(const GraphAttributes &GA) :
50 PlanRep(GA),
51 m_pUmlGraph(nullptr)
52 {
53 m_alignUpward .init(*this, false);
54 m_faceSplitter.init(*this,false);
55 m_incMergers .init(m_ccInfo.numberOfCCs());
56 }
57
58
initCC(int i)59 void PlanRepUML::initCC(int i)
60 {
61 PlanRep::initCC(i);
62
63 if(m_pUmlGraph != nullptr) {
64 //the new types that will replace the types just set
65 //maybe this should not be executed in initcc, only in constr.
66 //check this for crossings
67 for(edge e : edges)
68 {
69 if (original(e))
70 {
71 //edges should be embedded at outgoing generalization to allow alignment
72 if (m_pUmlGraph->upwards(original(e)->adjSource()))
73 {
74 m_alignUpward[e->adjSource()] = true;
75 } else {
76 m_alignUpward[e->adjSource()] = false;
77 }
78
79 //maybe it's enough to set gen/ass without extra array
80 //due to planarization we have to assure that no types are lost
81 oriEdgeTypes(original(e)) = edgeTypes(e);
82 }
83 }
84 }
85 }
86
expand(bool lowDegreeExpand)87 void PlanRepUML::expand(bool lowDegreeExpand)
88 {
89 OGDF_ASSERT(representsCombEmbedding());
90
91 for(node v : nodes)
92 {
93 // Replace merge vertices by cages.
94 if (typeOf(v) == Graph::NodeType::generalizationMerger)
95 {
96 // Scan the list of edges of v to find the outgoing edge of v
97 // Get then the cirular list of ingoing edges corresponding to
98 // the planar embedding.
99 SList<edge> inGens;
100 bool detect = false;
101 for(adjEntry adj : v->adjEntries) {
102 edge e = adj->theEdge();
103 OGDF_ASSERT(typeOf(e) == Graph::EdgeType::generalization);
104 if (e->target() != v)
105 {
106 detect = true;
107 continue;
108 }
109 if (detect)
110 inGens.pushBack(e);
111 }
112 for(adjEntry adj : v->adjEntries) {
113 edge e = adj->theEdge();
114 if (e->target() != v)
115 break;
116 inGens.pushBack(e);
117 }
118
119 setExpandedNode(v, v);
120 // Create the list of generalization expanders
121 // We need degree(v)-1 of them to construct a face.
122 SListPure<node> expander;
123 for (int i = 0; i < v->degree()-1; i++)
124 {
125 node u = newNode();
126 typeOf(u) = Graph::NodeType::generalizationExpander;
127 setExpandedNode(u, v);
128 expander.pushBack(u);
129 }
130
131 // We move the target node of each ingoing generalization of v to a new
132 // node stored in expander.
133 // Note that, for each such edge e, the target node of the original
134 // edge is then different from the original of the target node of e
135 // (the latter is 0 because u is a new (dummy) node)
136 SListConstIterator<edge> it;
137 SListConstIterator<node> itn;
138 NodeArray<adjEntry> ar(*this);
139
140 itn = expander.begin();
141
142 for (it = inGens.begin(); it.valid(); ++it)
143 {
144 // all edges in the list inGens must be ingoing generalizations of v
145 OGDF_ASSERT((*it)->target() == v);
146 OGDF_ASSERT(typeOf(*it) == Graph::EdgeType::generalization);
147 OGDF_ASSERT(itn.valid());
148
149 moveTarget(*it,*itn);
150 ar[*itn] = (*itn)->firstAdj();
151 ++itn;
152 }
153 ar[v] = v->firstAdj();
154
155 // Now introduce the circular list of new edges
156 // forming the border of the merge face. Keep the embedding.
157 adjEntry adjPrev = v->firstAdj();
158
159 for (itn = expander.begin(); itn.valid(); ++itn)
160 {
161 edge e = newEdge(adjPrev,(*itn)->firstAdj());
162
163 setExpansion(e);
164 setGeneralization(e);
165
166 if (!expandAdj(v))
167 expandAdj(v) = e->adjSource();
168
169 adjPrev = (*itn)->firstAdj();
170 }
171
172 edge e = newEdge(adjPrev,v->lastAdj());
173
174 setExpansion(e);
175 setGeneralization(e);
176
177 OGDF_ASSERT(representsCombEmbedding());
178 }
179
180 // Replace vertices with high degree by cages and
181 // replace degree 4 vertices with two generalizations
182 // adjacent in the embedding list by a cage.
183 else if (v->degree() >= 4 &&
184 typeOf(v) != Graph::NodeType::dummy &&
185 !lowDegreeExpand)
186 {
187 // Check first how many generalizations there are.
188 // If the node has degree 4 and at most one generalization
189 // nothing is to do.
190 int detect = 0;
191 for(adjEntry adj : v->adjEntries) {
192 edge e = adj->theEdge();
193 if (!detect && typeOf(e) == Graph::EdgeType::generalization)
194 detect = 1;
195 else if (typeOf(e) == Graph::EdgeType::generalization)
196 detect = 2;
197 }
198 if (v->degree() == 4 && detect < 2)
199 continue; // Nothing to do.
200
201 // Collects the nodes in the expanded face that
202 // are adjacent to a generalization.
203 // There are at most two of them.
204 SList<node> genNodes;
205
206 //Set the type of the node v. It remains in the graph
207 // as one of the nodes of the expanded face.
208 typeOf(v) = Graph::NodeType::highDegreeExpander;
209
210 // Scann the list of edges of v to find the adjacent edges of v
211 // according to the planar embedding. All except one edge
212 // will get a new adjacent node
213 // the planar embedding.
214 SList<edge> adjEdges;
215 for(adjEntry adj : v->adjEntries) {
216 edge e = adj->theEdge();
217 adjEdges.pushBack(e);
218 }
219
220 //The first edge remains at v. remove it from the list.
221 // Check if it is a generalization.
222 edge e = adjEdges.popFrontRet();
223
224 //super sink check: don't use sink generalization, as the node may be deleted
225 while (typeOf(e) == Graph::EdgeType::generalization)
226 {
227 adjEdges.pushBack(e);
228 e = adjEdges.popFrontRet();
229 }
230 //check end
231
232 if (typeOf(e) == Graph::EdgeType::generalization)
233 genNodes.pushBack(v);
234
235 // The node has maximum two generalization edges.
236 OGDF_ASSERT(genNodes.size() <= 2);
237
238 // Create the list of high degree expanders
239 // We need degree(v)-1 of them to construct a face.
240 // and set expanded Node to v
241 setExpandedNode(v, v);
242
243 SListPure<node> expander;
244 for (int i = 0; i < v->degree()-1; i++)
245 {
246 node u = newNode();
247 typeOf(u) = Graph::NodeType::highDegreeExpander;
248 setExpandedNode(u, v);
249 expander.pushBack(u);
250 }
251
252 // We move the target node of each ingoing generalization of v to a new
253 // node stored in expander.
254 // Note that, for each such edge e, the target node of the original
255 // edge is then different from the original of the target node of e
256 // (the latter is 0 because u is a new (dummy) node)
257 SListConstIterator<edge> it;
258 SListConstIterator<node> itn;
259
260 NodeArray<adjEntry> ar(*this);
261
262 itn = expander.begin();
263
264 for (it = adjEdges.begin(); it.valid(); ++it)
265 {
266 // Did we allocate enough dummy nodes?
267 OGDF_ASSERT(itn.valid());
268
269 // Check if edge is a generalization
270 if (typeOf((*it)) == Graph::EdgeType::generalization)
271 genNodes.pushBack(*itn);
272
273 if ((*it)->source() == v)
274 moveSource(*it,*itn);
275 else
276 moveTarget(*it,*itn);
277 ar[*itn] = (*itn)->firstAdj();
278 ++itn;
279 }
280 ar[v] = v->firstAdj();
281
282
283 // There may be at most two generalizations adjacent to v.
284 OGDF_ASSERT(genNodes.size() <= 2);
285
286 // Now introduce the circular list of new edges
287 // forming the border of the merge face. Keep the embedding.
288 adjEntry adjPrev = v->firstAdj();
289
290 for (itn = expander.begin(); itn.valid(); ++itn)
291 {
292 e = newEdge(adjPrev,(*itn)->firstAdj());
293 setExpansionEdge(e, 2);//can be removed if edgetypes work properly
294
295 setExpansion(e);
296 setAssociation(e);
297
298 typeOf(e) = EdgeType::association; //???
299
300 if (!expandAdj(v))
301 expandAdj(v) = e->adjSource();
302 adjPrev = (*itn)->firstAdj();
303 }
304
305 e = newEdge(adjPrev,v->lastAdj());
306
307 typeOf(e) = EdgeType::association; //???
308 setExpansionEdge(e, 2);//can be removed if edgetypes work properly
309 setAssociation(e);
310
311
312 if (genNodes.size() == 2)
313 {
314 node u = genNodes.popFrontRet();
315 node w = genNodes.popFrontRet();
316 e = newEdge(u->firstAdj()->succ(),w->firstAdj()->succ());
317 m_faceSplitter[e] = true;
318 }
319
320 OGDF_ASSERT(representsCombEmbedding());
321 }
322
323 // Replace all vertices with degree > 2 by cages.
324 else if (v->degree() >= 2 &&
325 typeOf(v) != Graph::NodeType::dummy &&
326 lowDegreeExpand)
327 {
328 // Check first how many generalizations there are.
329 // If the node has degree 4 and at most one generalization
330 // nothing is to do.
331 int detect = 0;
332 for(adjEntry adj : v->adjEntries) {
333 edge e = adj->theEdge();
334 if (!detect && typeOf(e) == Graph::EdgeType::generalization)
335 detect = 1;
336 else if (typeOf(e) == Graph::EdgeType::generalization)
337 detect = 2;
338 }
339 #if 0
340 if (v->degree() == 4 && detect < 2)
341 continue; // Nothing to do.
342 #endif
343
344 // Collects the nodes in the expanded face that
345 // are adjacent to a generalization.
346 // There are at most two of them.
347 SList<node> genNodes;
348
349 //Set the type of the node v. It remains in the graph
350 // as one of the nodes of the expanded face.
351 typeOf(v) = Graph::NodeType::highDegreeExpander;
352
353 // Scan the list of edges of v to find the adjacent edges of v
354 // according to the planar embedding. All except one edge
355 // will get a new adjacent node
356 // the planar embedding.
357 SList<edge> adjEdges;
358 for(adjEntry adj : v->adjEntries) {
359 edge e = adj->theEdge();
360 adjEdges.pushBack(e);
361 }
362
363 //The first edge remains at v. remove it from the list.
364 // Check if it is a generalization.
365 edge e = adjEdges.popFrontRet();
366 if (typeOf(e) == Graph::EdgeType::generalization)
367 genNodes.pushBack(v);
368
369 // The node has maximum two generalization edges.
370 OGDF_ASSERT(genNodes.size() <= 2);
371
372 // Create the list of high degree expanders
373 // We need degree(v)-1 of them to construct a face.
374 // and set expanded Node to v
375 setExpandedNode(v, v);
376 SListPure<node> expander;
377 for (int i = 0; i < v->degree()-1; i++)
378 {
379 node u = newNode();
380 typeOf(u) = Graph::NodeType::highDegreeExpander;
381 setExpandedNode(u, v);
382 expander.pushBack(u);
383 }
384
385
386 // We move the target node of each ingoing generalization of v to a new
387 // node stored in expander.
388 // Note that, for each such edge e, the target node of the original
389 // edge is then different from the original of the target node of e
390 // (the latter is 0 because u is a new (dummy) node)
391 SListConstIterator<edge> it;
392 SListConstIterator<node> itn;
393
394 NodeArray<adjEntry> ar(*this);
395
396 itn = expander.begin();
397
398 for (it = adjEdges.begin(); it.valid(); ++it)
399 {
400 // Did we allocate enough dummy nodes?
401 OGDF_ASSERT(itn.valid());
402
403 // Check if edge is a generalization
404 if (typeOf((*it)) == Graph::EdgeType::generalization)
405 genNodes.pushBack(*itn);
406
407 if ((*it)->source() == v)
408 moveSource(*it,*itn);
409 else
410 moveTarget(*it,*itn);
411 ar[*itn] = (*itn)->firstAdj();
412 ++itn;
413 }
414 ar[v] = v->firstAdj();
415
416
417 // There may be at most two generalizations adjacent to v.
418 OGDF_ASSERT(genNodes.size() <= 2);
419
420 // Now introduce the circular list of new edges
421 // forming the border of the merge face. Keep the embedding.
422 adjEntry adjPrev = v->firstAdj();
423
424 for (itn = expander.begin(); itn.valid(); ++itn)
425 {
426 e = newEdge(adjPrev,(*itn)->firstAdj());
427 if (!expandAdj(v)) expandAdj(v) = e->adjSource();
428 typeOf(e) = EdgeType::association; //???
429 setExpansionEdge(e, 2);
430
431 //new types
432 setAssociation(e); //should be dummy type?
433 setExpansion(e);
434
435 adjPrev = (*itn)->firstAdj();
436 }
437 e = newEdge(adjPrev,v->lastAdj());
438 typeOf(e) = EdgeType::association; //???
439 setExpansionEdge(e, 2);
440
441
442 if (genNodes.size() == 2)
443 {
444 node u = genNodes.popFrontRet();
445 node w = genNodes.popFrontRet();
446 e = newEdge(u->firstAdj()->succ(),w->firstAdj()->succ());
447 m_faceSplitter[e] = true;
448 }
449
450 OGDF_ASSERT(representsCombEmbedding());
451 }
452 }
453 }
454
455
expandLowDegreeVertices(OrthoRep & OR,bool alignSmallDegree)456 void PlanRepUML::expandLowDegreeVertices(OrthoRep &OR, bool alignSmallDegree)
457 {
458 for(node v : nodes)
459 {
460 if (!(isVertex(v)) || expandAdj(v) != nullptr)
461 continue;
462
463 int startDegree = v->degree();
464
465 SList<edge> adjEdges;
466 SListPure<Tuple2<node,int> > expander;
467
468 node u = v;
469 bool firstTime = true;
470
471 setExpandedNode(v, v);//obsolete?! u=v
472
473 for(adjEntry adj : v->adjEntries) {
474 adjEdges.pushBack(adj->theEdge());
475
476 if(!firstTime)
477 u = newNode();
478
479 setExpandedNode(u, v);
480 typeOf(u) = Graph::NodeType::lowDegreeExpander;
481 expander.pushBack(Tuple2<node,int>(u,OR.angle(adj)));
482 firstTime = false;
483 }
484
485
486 SListConstIterator<edge> it;
487 SListConstIterator<Tuple2<node,int> > itn;
488
489 itn = expander.begin().succ();
490
491 for (it = adjEdges.begin().succ(); it.valid(); ++it)
492 {
493 // Did we allocate enough dummy nodes?
494 OGDF_ASSERT(itn.valid());
495
496 if ((*it)->source() == v)
497 moveSource(*it,(*itn).x1());
498 else
499 moveTarget(*it,(*itn).x1());
500 ++itn;
501 }
502
503 adjEntry adjPrev = v->firstAdj();
504 itn = expander.begin();
505 int nBends = (*itn).x2();
506
507 edge e;
508 for (++itn; itn.valid(); ++itn)
509 {
510 e = newEdge(adjPrev,(*itn).x1()->firstAdj());
511
512 OR.bend(e->adjSource()).set(OrthoBendType::convexBend,nBends);
513 OR.bend(e->adjTarget()).set(OrthoBendType::reflexBend,nBends);
514 OR.angle(adjPrev) = 1;
515 OR.angle(e->adjSource()) = 2;
516 OR.angle(e->adjTarget()) = 1;
517
518 nBends = (*itn).x2();
519
520 typeOf(e) = EdgeType::association; //???
521 setExpansionEdge(e, 2);
522
523 adjPrev = (*itn).x1()->firstAdj();
524 }
525
526 e = newEdge(adjPrev,v->lastAdj());
527 typeOf(e) = EdgeType::association; //???
528 setExpansionEdge(e, 2);
529
530 expandAdj(v) = e->adjSource();
531
532 OR.bend(e->adjSource()).set(OrthoBendType::convexBend,nBends);
533 OR.bend(e->adjTarget()).set(OrthoBendType::reflexBend,nBends);
534 OR.angle(adjPrev) = 1;
535 OR.angle(e->adjSource()) = 2;
536 OR.angle(e->adjTarget()) = 1;
537
538 if (alignSmallDegree && (startDegree == 2))
539 {
540 node vOpp = e->source();
541 if (vOpp == v) vOpp = e->target(); //only one case necessary
542 edge eAlign = newEdge(vOpp->lastAdj(), vOpp->lastAdj()->faceCycleSucc());
543 typeOf(eAlign) = EdgeType::association;
544 OR.angle(eAlign->adjSource()) = 1;
545 OR.angle(eAlign->adjTarget()) = 1;
546 OR.angle(eAlign->adjSource()->faceCycleSucc()) = 1;
547 OR.angle(eAlign->adjTarget()->faceCycleSucc()) = 1;
548 }
549
550 }
551 }
552
553
collapseVertices(const OrthoRep & OR,Layout & drawing)554 void PlanRepUML::collapseVertices(const OrthoRep &OR, Layout &drawing)
555 {
556 for(node v : nodes) {
557 const OrthoRep::VertexInfoUML *vi = OR.cageInfo(v);
558
559 if(vi == nullptr ||
560 (typeOf(v) != Graph::NodeType::highDegreeExpander &&
561 typeOf(v) != Graph::NodeType::lowDegreeExpander))
562 continue;
563
564 node vOrig = original(v);
565 OGDF_ASSERT(vOrig != nullptr);
566
567 node vCenter = newNode();
568 m_vOrig[vCenter] = vOrig;
569 m_vCopy[vOrig] = vCenter;
570 m_vOrig[v] = nullptr;
571
572 node lowerLeft = vi->m_corner[static_cast<int>(OrthoDir::North)]->theNode();
573 node lowerRight = vi->m_corner[static_cast<int>(OrthoDir::West) ]->theNode();
574 node upperLeft = vi->m_corner[static_cast<int>(OrthoDir::East) ]->theNode();
575 drawing.x(vCenter) = 0.5*(drawing.x(lowerLeft)+drawing.x(lowerRight));
576 drawing.y(vCenter) = 0.5*(drawing.y(lowerLeft)+drawing.y(upperLeft ));
577
578 //Attention: The order in which the connection nodes are inserted
579 //ist not in the order of the copy embedding, but in the order
580 //of the adjacency lists of the original graph. Therefore, the edges
581 //may not be used to derive the correct copy order of outgoing edges.
582 //We compute a list of edges corresponding to the embedding and use
583 //this list to insert the edges. The order is e.g. used for clique positioning
584 List<edge> adjEdges;
585 //we start at an arbitrary corner
586 adjEntry adjCorner = vi->m_corner[static_cast<int>(OrthoDir::North)];
587 do {
588 adjEntry runAdj = adjCorner->twin();
589 edge eOrig = nullptr;
590 int count = 0; //should be max. 4 (3) edges at boundary node
591 //the order of the edges in the copy may be incorrect, we search for the
592 //edge with an original
593 #if 0
594 do {
595 #endif
596 runAdj = runAdj->cyclicSucc();
597 eOrig = original(runAdj->theEdge());
598 count++;
599 #if 0
600 } while ((count < 4) && !eOrig);
601 #endif
602 //edge found or corner reached
603 OGDF_ASSERT((count == 1) || (runAdj->theNode()->degree() == 2));
604 if (eOrig)
605 {
606 adjEdges.pushBack(eOrig);
607 }
608 adjCorner = adjCorner->faceCycleSucc(); //TODO: pred?
609 } while (adjCorner != vi->m_corner[static_cast<int>(OrthoDir::North)]);
610
611 OGDF_ASSERT(adjEdges.size() == vOrig->degree());
612 ListIterator<edge> itEdge = adjEdges.begin();
613
614 #if 0
615 edge eOrig;
616 forall_adj_edges(eOrig,vOrig) {
617 #else
618 while (itEdge.valid()) {
619 edge eOrig = *itEdge;
620 #endif
621 if(eOrig->target() == vOrig) {
622 node connect = m_eCopy[eOrig].back()->target();
623 edge eNew = newEdge(connect,vCenter);
624 m_eOrig[eNew] = eOrig;
625 m_eIterator[eNew] = m_eCopy[eOrig].pushBack(eNew);
626 } else {
627 node connect = m_eCopy[eOrig].front()->source();
628 edge eNew = newEdge(vCenter,connect);
629 m_eOrig[eNew] = eOrig;
630 m_eIterator[eNew] = m_eCopy[eOrig].pushFront(eNew);
631 }
632 ++itEdge;
633 }
634 }
635 }
636
637 void PlanRepUML::setupIncremental(int indexCC, CombinatorialEmbedding &E)
638 {
639 prepareIncrementalMergers(indexCC, E);
640 }
641
642 void PlanRepUML::prepareIncrementalMergers(int indexCC, CombinatorialEmbedding &E)
643 {
644 //we can't draw multiple hierarchies hanging at one class
645 //object, therefore we reduce the number in the given layout
646 //to 1 by interpreting only the edges in the largest sequence
647 //of generalizations as gens, all other edges are associations
648 //const Graph& G = uml;
649
650 for(node v : nodes)
651 {
652 if (v->degree() < 2) continue;
653 if (typeOf(v) == Graph::NodeType::generalizationMerger) continue;
654
655 int maxSeq = 0; //stores current best sequence size
656 int maxSeqRun = 0; //stores current sequence size
657
658 adjEntry maxSeqAdj = nullptr;
659 adjEntry runSeqAdj = nullptr;
660 adjEntry ad1 = v->firstAdj();
661 //We have to avoid the case where we start within a sequence
662 //we run back til the first non-input generalization is detected
663 //if there is none, the following is also correct
664 adjEntry stopAdj = ad1;
665 while ( ad1->cyclicPred() != stopAdj && ad1->theEdge()->target() == v && isGeneralization(ad1->theEdge()) )
666 {
667 ad1 = ad1->cyclicPred();
668 }
669 adjEntry ad = ad1->cyclicSucc();
670 while (ad != ad1)
671 {
672 edge e = ad->theEdge();
673 if (e->target() == v && isGeneralization(e))
674 {
675 if (maxSeqRun == 0)
676 {
677 //initialize once
678 runSeqAdj = ad;
679 maxSeqAdj = ad;
680 }
681 maxSeqRun++;
682
683 }
684 else
685 {
686 //we never stop here if there is only one
687 //gen sequence, but then we don't need to
688 //(we don't use maxSeq elsewhere)
689 //we change edge in both cases here to avoid
690 //running over all edges again (#gens may be
691 //significantly lower then #ass)
692 adjEntry changeAdj = nullptr;
693 if (maxSeqRun > maxSeq)
694 {
695 //change edge type for old favorites
696 if (maxSeqAdj != runSeqAdj)
697 changeAdj = maxSeqAdj;
698
699 maxSeq = maxSeqRun;
700 maxSeqAdj = runSeqAdj;
701 } else {
702 //change edge types for weaker sequence
703 if (maxSeqRun != 0)
704 changeAdj = runSeqAdj;
705 }
706
707 //Change types for a sequence
708 //invariant: on every pass of the loop, if a sequence
709 //end is detected, one of the two sequences is deleted
710 //(types changed) => only one sequence when we stop
711 if (changeAdj != nullptr)
712 {
713 adjEntry runGenAdj = changeAdj;
714 //no infinite loop because new sequence found
715 edge eRGA = runGenAdj->theEdge();
716 while ((eRGA->target() == v) && isGeneralization(eRGA))
717 {
718 setAssociation(eRGA);
719 runGenAdj = runGenAdj->cyclicSucc();
720 eRGA = runGenAdj->theEdge();
721 }
722 #if 0
723 changeAdj = 0;
724 #endif
725 }
726 maxSeqRun = 0;
727 }
728
729 ad = ad->cyclicSucc();
730 }
731
732 //now we insert mergers for all edges in best sequence
733 //do not use maxSeq to count, may be 0 if only incoming gens
734
735 if (maxSeqAdj != nullptr)
736 {
737 SList<edge> inGens;
738
739 edge eMSA = maxSeqAdj->theEdge();
740 adjEntry runAdj = maxSeqAdj;
741 while ((eMSA->target() == v) && isGeneralization(eMSA))
742 {
743 inGens.pushBack(eMSA);
744 runAdj = runAdj->cyclicSucc();
745 eMSA = runAdj->theEdge();
746 //maybe only one sequence around v
747 if (runAdj == maxSeqAdj)
748 break;
749 }
750
751 //insert the merger for v
752 OGDF_ASSERT(representsCombEmbedding());
753 node newMerger = insertGenMerger(v, inGens, E);
754 OGDF_ASSERT(representsCombEmbedding());
755 if (newMerger)
756 m_incMergers[indexCC].pushBack(newMerger);
757 }
758 }
759
760 #if 0
761 uml.adjustHierarchyParents();
762 #endif
763 }
764
765 //inserts a merger node for generalizations hanging at v, respecting
766 //embedding E
767 node PlanRepUML::insertGenMerger(node /* v */, const SList<edge> &inGens, CombinatorialEmbedding &E)
768 {
769 node u = nullptr;
770 if (empty()) return u;
771 if(inGens.size() >= 2)
772 {
773 // create a new node representing the merge point for the generalizations
774 u = newNode();
775 typeOf(u) = Graph::NodeType::generalizationMerger;
776
777 //store the embedding information before inserting objects
778 //TODO: Front or back
779 face fRight = E.rightFace(inGens.front()->adjSource());
780 face fLeft = E.rightFace(inGens.back()->adjTarget());
781 // add the edge from v to the merge point
782 // this edge is a generalization, but has no original edge
783 //edge eMerge = insertEdge(u, (*(inGens.rbegin()))->adjTarget(), E);
784 edge eMerge = newEdge(u,inGens.back()->adjTarget());
785 //newEdge(u, (*(inGens.rbegin()))->adjTarget()); //incoming generalization
786 typeOf(eMerge) = Graph::EdgeType::generalization;
787 m_mergeEdges.pushBack(eMerge);
788
789 // We move the target node of each ingoing generalization of v to u.
790 // Note that, for each such edge e, the target node of the original
791 // edge is then different from the original of the target node of e
792 // (the latter is 0 because u is a new (dummy) node)
793 SListConstIterator<edge> it;
794 for(it = inGens.begin(); it.valid(); ++it)
795 {
796 // all edges in the list inGens must be ingoing generalizations of v
797 #if 0
798 OGDF_ASSERT((*it)->target() == v);
799 OGDF_ASSERT(typeOf(*it) == Graph::generalization);
800 #endif
801
802 moveTarget(*it,u);
803 }
804 //now we update the combinatorial embedding to represent the new situation
805 //first, update the face information at the inserted edge
806 E.updateMerger(eMerge, fRight, fLeft);
807 }
808
809 return u;
810 }
811
812 // Same as in GraphAttributes. Except: Writes colors to new nodes and
813 // to generalizations. For debugging only
814 void PlanRepUML::writeGML(const char *fileName, const Layout &drawing)
815 {
816 std::ofstream os(fileName);
817 writeGML(os,drawing);
818 }
819
820
821 void PlanRepUML::writeGML(const char *fileName)
822 {
823 Layout drawing(*this);
824 std::ofstream os(fileName);
825 writeGML(os,drawing);
826 }
827
828
829 //zu debugzwecken
830 void PlanRepUML::writeGML(const char *fileName, GraphAttributes &AG)
831 {
832 OGDF_ASSERT(m_pGraphAttributes == &(AG));
833 Layout drawing(*this);
834 for(node v : nodes)
835 {
836 if (original(v))
837 {
838 drawing.x(v) = AG.x(original(v));
839 drawing.y(v) = AG.y(original(v));
840 }
841 }
842
843 std::ofstream os(fileName);
844 writeGML(os, drawing);
845 }
846
847 void PlanRepUML::writeGML(std::ostream &os, const Layout &drawing)
848 {
849 const Graph &G = *this;
850
851 NodeArray<int> id(*this);
852 int nextId = 0;
853
854 os.setf(std::ios::showpoint);
855 os.precision(10);
856
857 os << "Creator \"ogdf::GraphAttributes::writeGML\"\n";
858 os << "graph [\n";
859 os << " directed 1\n";
860
861 for(node v : G.nodes) {
862 os << " node [\n";
863
864 os << " id " << (id[v] = nextId++) << "\n";
865 #ifdef OGDF_DEBUG
866 os << " label \"" << v->index() << "\"\n";
867 #endif
868
869 os << " graphics [\n";
870 os << " x " << drawing.x(v) << "\n";
871 os << " y " << drawing.y(v) << "\n";
872 os << " w " << 10.0 << "\n";
873 os << " h " << 10.0 << "\n";
874 os << " type \"rectangle\"\n";
875 os << " width 1.0\n";
876 if (typeOf(v) == Graph::NodeType::generalizationMerger) {
877 os << " type \"oval\"\n";
878 os << " fill \"#0000A0\"\n";
879 }
880 else if (typeOf(v) == Graph::NodeType::generalizationExpander) {
881 os << " type \"oval\"\n";
882 os << " fill \"#00FF00\"\n";
883 }
884 else if (typeOf(v) == Graph::NodeType::highDegreeExpander ||
885 typeOf(v) == Graph::NodeType::lowDegreeExpander)
886 os << " fill \"#FFFF00\"\n";
887 else if (typeOf(v) == Graph::NodeType::dummy)
888 {
889 if (isCrossingType(v))
890 {
891 os << " fill \"#FF0000\"\n";
892 }
893 else os << " fill \"#FFFFFF\"\n";
894 os << " type \"oval\"\n";
895 }
896
897 else if (v->degree() > 4)
898 os << " fill \"#FFFF00\"\n";
899
900 else
901 os << " fill \"#000000\"\n";
902
903
904 os << " ]\n"; // graphics
905
906 os << " ]\n"; // node
907 }
908
909
910 for(edge e : G.edges) {
911 os << " edge [\n";
912
913 os << " source " << id[e->source()] << "\n";
914 os << " target " << id[e->target()] << "\n";
915
916 os << " generalization " << typeOf(e) << "\n";
917
918 os << " graphics [\n";
919
920 os << " type \"line\"\n";
921
922 if (typeOf(e) == Graph::EdgeType::generalization)
923 {
924 os << " arrow \"last\"\n";
925 if (m_alignUpward[e->adjSource()])
926 os << " fill \"#0000FF\"\n";
927 else
928 os << " fill \"#FF0000\"\n";
929 os << " width 3.0\n";
930 }
931 else
932 {
933 if (typeOf(e->source()) == Graph::NodeType::generalizationExpander ||
934 typeOf(e->source()) == Graph::NodeType::generalizationMerger ||
935 typeOf(e->target()) == Graph::NodeType::generalizationExpander ||
936 typeOf(e->target()) == Graph::NodeType::generalizationMerger)
937 {
938 os << " arrow \"none\"\n";
939 if (isBrother(e))
940 os << " fill \"#F0F000\"\n"; //gelb
941 else if (isHalfBrother(e))
942 os << " fill \"#FF00AF\"\n";
943 else
944 os << " fill \"#FF0000\"\n";
945 }
946 else
947 os << " arrow \"none\"\n";
948 if (isBrother(e))
949 os << " fill \"#F0F000\"\n"; //gelb
950 else if (isHalfBrother(e))
951 os << " fill \"#FF00AF\"\n";
952 else if (!(original(e)))
953 os << " fill \"#00F00F\"\n";
954 else
955 os << " fill \"#00000F\"\n";
956 os << " width 1.0\n";
957 }
958 os << " ]\n"; // graphics
959
960 os << " ]\n"; // edge
961 }
962
963 os << "]\n"; // graph
964 }
965
966 void PlanRepUML::writeGML(const char *fileName, const OrthoRep &OR, const Layout &drawing)
967 {
968 std::ofstream os(fileName);
969 writeGML(os,OR,drawing);
970 }
971
972 void PlanRepUML::writeGML(std::ostream &os, const OrthoRep &OR, const Layout &drawing)
973 {
974 const Graph &G = *this;
975
976 NodeArray<int> id(*this);
977 int nextId = 0;
978
979 os.setf(std::ios::showpoint);
980 os.precision(10);
981
982 os << "Creator \"ogdf::GraphAttributes::writeGML\"\n";
983 os << "graph [\n";
984 os << " directed 1\n";
985
986 for(node v : G.nodes) {
987 os << " node [\n";
988
989 os << " id " << (id[v] = nextId++) << "\n";
990
991 os << " label \"" << v->index() << "\"\n";
992
993 os << " graphics [\n";
994 os << " x " << drawing.x(v) << "\n";
995 os << " y " << drawing.y(v) << "\n";
996 os << " w " << 3.0 << "\n";
997 os << " h " << 3.0 << "\n";
998 os << " type \"rectangle\"\n";
999 os << " width 1.0\n";
1000 if (typeOf(v) == Graph::NodeType::generalizationMerger) {
1001 os << " type \"oval\"\n";
1002 os << " fill \"#0000A0\"\n";
1003 }
1004 else if (typeOf(v) == Graph::NodeType::generalizationExpander) {
1005 os << " type \"oval\"\n";
1006 os << " fill \"#00FF00\"\n";
1007 }
1008 else if (typeOf(v) == Graph::NodeType::highDegreeExpander ||
1009 typeOf(v) == Graph::NodeType::lowDegreeExpander)
1010 os << " fill \"#FFFF00\"\n";
1011 else if (typeOf(v) == Graph::NodeType::dummy)
1012 os << " type \"oval\"\n";
1013
1014 else if (v->degree() > 4)
1015 os << " fill \"#FFFF00\"\n";
1016
1017 else
1018 os << " fill \"#000000\"\n";
1019
1020
1021 os << " ]\n"; // graphics
1022
1023 os << " ]\n"; // node
1024 }
1025
1026 for(node v : nodes)
1027 {
1028 if (expandAdj(v) != nullptr && (typeOf(v) == Graph::NodeType::highDegreeExpander ||
1029 typeOf(v) == Graph::NodeType::lowDegreeExpander))
1030 {
1031 node vOrig = original(v);
1032 const OrthoRep::VertexInfoUML &vi = *OR.cageInfo(v);
1033 node ll = vi.m_corner[static_cast<int>(OrthoDir::North)]->theNode();
1034 node ur = vi.m_corner[static_cast<int>(OrthoDir::South)]->theNode();
1035
1036 os << " node [\n";
1037 os << " id " << nextId++ << "\n";
1038
1039 if (m_pGraphAttributes->has(GraphAttributes::nodeLabel)) {
1040 os << " label \"" << m_pGraphAttributes->label(vOrig) << "\"\n";
1041 }
1042
1043 os << " graphics [\n";
1044 os << " x " << 0.5 * (drawing.x(ur) + drawing.x(ll)) << "\n";
1045 os << " y " << 0.5 * (drawing.y(ur) + drawing.y(ll)) << "\n";
1046 os << " w " << widthOrig(vOrig) << "\n";
1047 os << " h " << heightOrig(vOrig) << "\n";
1048 os << " type \"rectangle\"\n";
1049 os << " width 1.0\n";
1050 os << " fill \"#FFFF00\"\n";
1051
1052 os << " ]\n"; // graphics
1053 os << " ]\n"; // node
1054 }
1055 }
1056
1057 for(edge e : G.edges)
1058 {
1059 os << " edge [\n";
1060
1061 os << " source " << id[e->source()] << "\n";
1062 os << " target " << id[e->target()] << "\n";
1063
1064 os << " generalization " << typeOf(e) << "\n";
1065
1066 os << " graphics [\n";
1067
1068 os << " type \"line\"\n";
1069
1070 if (typeOf(e) == Graph::EdgeType::generalization)
1071 {
1072 if (typeOf(e->target()) == Graph::NodeType::generalizationExpander)
1073 os << " arrow \"none\"\n";
1074 else
1075 os << " arrow \"last\"\n";
1076
1077 os << " fill \"#FF0000\"\n";
1078 os << " width 2.0\n";
1079 }
1080 else
1081 {
1082 if (typeOf(e->source()) == Graph::NodeType::generalizationExpander ||
1083 typeOf(e->source()) == Graph::NodeType::generalizationMerger ||
1084 typeOf(e->target()) == Graph::NodeType::generalizationExpander ||
1085 typeOf(e->target()) == Graph::NodeType::generalizationMerger)
1086 {
1087 os << " arrow \"none\"\n";
1088 os << " fill \"#FF0000\"\n";
1089 }
1090 else if (original(e) == nullptr)
1091 {
1092 os << " arrow \"none\"\n";
1093 os << " fill \"#AFAFAF\"\n";
1094 }
1095 else
1096 os << " arrow \"none\"\n";
1097 if (isBrother(e))
1098 os << " fill \"#00AF0F\"\n";
1099 if (isHalfBrother(e))
1100 os << " fill \"#0F00AF\"\n";
1101 os << " width 1.0\n";
1102 }
1103
1104 os << " ]\n"; // graphics
1105
1106 os << " ]\n"; // edge
1107 }
1108
1109 os << "]\n"; // graph
1110 }
1111
1112
1113 void PlanRepUML::writeGML(const char *fileName, const OrthoRep &OR, const GridLayoutMapped &drawing)
1114 {
1115 std::ofstream os(fileName);
1116 writeGML(os,OR,drawing);
1117 }
1118
1119 void PlanRepUML::writeGML(std::ostream &os, const OrthoRep &OR, const GridLayoutMapped &drawing)
1120 {
1121 const Graph &G = *this;
1122
1123 NodeArray<int> id(*this);
1124 int nextId = 0;
1125
1126 os.setf(std::ios::showpoint);
1127 os.precision(10);
1128
1129 os << "Creator \"ogdf::GraphAttributes::writeGML\"\n";
1130 os << "graph [\n";
1131 os << " directed 1\n";
1132
1133 for(node v : G.nodes) {
1134 os << " node [\n";
1135
1136 os << " id " << (id[v] = nextId++) << "\n";
1137
1138 os << " label \"" << v->index() << "\"\n";
1139
1140 os << " graphics [\n";
1141 os << " x " << drawing.toDouble(drawing.x(v)) << "\n";
1142 os << " y " << drawing.toDouble(drawing.y(v)) << "\n";
1143 os << " w " << 3.0 << "\n";
1144 os << " h " << 3.0 << "\n";
1145 os << " type \"rectangle\"\n";
1146 os << " width 1.0\n";
1147 if (typeOf(v) == Graph::NodeType::generalizationMerger) {
1148 os << " type \"oval\"\n";
1149 os << " fill \"#0000A0\"\n";
1150 }
1151 else if (typeOf(v) == Graph::NodeType::generalizationExpander) {
1152 os << " type \"oval\"\n";
1153 os << " fill \"#00FF00\"\n";
1154 }
1155 else if (typeOf(v) == Graph::NodeType::highDegreeExpander ||
1156 typeOf(v) == Graph::NodeType::lowDegreeExpander)
1157 os << " fill \"#FFFF00\"\n";
1158 else if (typeOf(v) == Graph::NodeType::dummy)
1159 os << " type \"oval\"\n";
1160
1161 else if (v->degree() > 4)
1162 os << " fill \"#FFFF00\"\n";
1163
1164 else
1165 os << " fill \"#000000\"\n";
1166
1167
1168 os << " ]\n"; // graphics
1169
1170 os << " ]\n"; // node
1171 }
1172
1173 for(node v : nodes)
1174 {
1175 if (expandAdj(v) != nullptr &&
1176 (typeOf(v) == Graph::NodeType::highDegreeExpander ||
1177 typeOf(v) == Graph::NodeType::lowDegreeExpander))
1178 {
1179 node vOrig = original(v);
1180 const OrthoRep::VertexInfoUML &vi = *OR.cageInfo(v);
1181 node ll = vi.m_corner[static_cast<int>(OrthoDir::North)]->theNode();
1182 node ur = vi.m_corner[static_cast<int>(OrthoDir::South)]->theNode();
1183
1184 os << " node [\n";
1185 os << " id " << nextId++ << "\n";
1186
1187 if (m_pGraphAttributes->has(GraphAttributes::nodeLabel)) {
1188 os << " label \"" << m_pGraphAttributes->label(vOrig) << "\"\n";
1189 } else {
1190 os << " label \"N " << vOrig->index() << "\"\n";
1191 }
1192
1193 os << " graphics [\n";
1194 os << " x " << 0.5 * drawing.toDouble(drawing.x(ur) + drawing.x(ll)) << "\n";
1195 os << " y " << 0.5 * drawing.toDouble(drawing.y(ur) + drawing.y(ll)) << "\n";
1196 os << " w " << widthOrig(vOrig) << "\n";
1197 os << " h " << heightOrig(vOrig) << "\n";
1198 os << " type \"rectangle\"\n";
1199 os << " width 1.0\n";
1200 os << " fill \"#FFFF00\"\n";
1201
1202 os << " ]\n"; // graphics
1203 os << " ]\n"; // node
1204 }
1205 }
1206
1207 for(edge e : G.edges) {
1208 os << " edge [\n";
1209
1210 os << " source " << id[e->source()] << "\n";
1211 os << " target " << id[e->target()] << "\n";
1212
1213 os << " generalization " << typeOf(e) << "\n";
1214
1215 os << " graphics [\n";
1216
1217 os << " type \"line\"\n";
1218
1219 if (typeOf(e) == Graph::EdgeType::generalization)
1220 {
1221 if (typeOf(e->target()) == Graph::NodeType::generalizationExpander)
1222 os << " arrow \"none\"\n";
1223 else
1224 os << " arrow \"last\"\n";
1225
1226 //check the vertical compaction mode
1227 if ((typeOf(e) == Graph::EdgeType::generalization) && //only generalizations
1228 !isExpansionEdge(e))
1229
1230 if ((e->adjSource() == OR.externalAdjEntry()) ||
1231 (e->adjTarget() == OR.externalAdjEntry()))
1232 os << " fill \"#00FF00\"\n";
1233 else
1234 if ((e->adjSource() == OR.alignAdjEntry()) ||
1235 (e->adjTarget() == OR.alignAdjEntry()))
1236 os << " fill \"#FFA000\"\n";
1237 else
1238 os << " fill \"#0000FF\"\n";
1239 else
1240 if ((e->adjSource() == OR.externalAdjEntry()) ||
1241 (e->adjTarget() == OR.externalAdjEntry()))
1242 os << " fill \"#00FF00\"\n";
1243 else
1244 if ((e->adjSource() == OR.alignAdjEntry()) ||
1245 (e->adjTarget() == OR.alignAdjEntry()))
1246 os << " fill \"#FFA000\"\n";
1247 else
1248 os << " fill \"#FF0000\"\n";
1249 os << " width 2.0\n";
1250 }
1251 else
1252 {
1253 if (typeOf(e->source()) == Graph::NodeType::generalizationExpander ||
1254 typeOf(e->source()) == Graph::NodeType::generalizationMerger ||
1255 typeOf(e->target()) == Graph::NodeType::generalizationExpander ||
1256 typeOf(e->target()) == Graph::NodeType::generalizationMerger)
1257 {
1258 os << " arrow \"none\"\n";
1259
1260 if (((e->adjSource() == OR.externalAdjEntry()) ||
1261 (e->adjTarget() == OR.externalAdjEntry())) ||
1262 ((e->adjSource() == OR.alignAdjEntry()) ||
1263 (e->adjTarget() == OR.alignAdjEntry())))
1264 os << " fill \"#00FF00\"\n";
1265 else
1266 os << " fill \"#F0F00F\"\n";
1267 //os << " fill \"#FF0000\"\n";
1268 }
1269 else if (original(e) == nullptr)
1270 {
1271 os << " arrow \"none\"\n";
1272 if (((e->adjSource() == OR.externalAdjEntry()) ||
1273 (e->adjTarget() == OR.externalAdjEntry())) ||
1274 ((e->adjSource() == OR.alignAdjEntry()) ||
1275 (e->adjTarget() == OR.alignAdjEntry())))
1276 os << " fill \"#00FF00\"\n";
1277 else
1278 os << " fill \"#AFAFAF\"\n";
1279 }
1280 else
1281 os << " arrow \"none\"\n";
1282
1283 os << " width 1.0\n";
1284 }
1285
1286 os << " ]\n"; // graphics
1287
1288 os << " ]\n"; // edge
1289 }
1290
1291 os << "]\n"; // graph
1292 }
1293
1294 }
1295