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 &umlGraph) :
40 	PlanRep(umlGraph),
41 	m_pUmlGraph(&umlGraph)
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