6 * $Date: 2012-07-07 17:14:54 +0200 (Sa, 07. Jul 2012) $
7 ***************************************************************/
10 * \brief implementation of PlanRepUML class
12 * \author Carsten Gutwenger
15 * This file is part of the Open Graph Drawing Framework (OGDF).
19 * See README.txt in the root directory of the OGDF installation for details.
22 * This program is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU General Public License
24 * Version 2 or 3 as published by the Free Software Foundation;
25 * see the file LICENSE.txt included in the packaging of this file
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
35 * You should have received a copy of the GNU General Public
36 * License along with this program; if not, write to the Free
37 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
38 * Boston, MA 02110-1301, USA.
40 * \see http://www.gnu.org/copyleft/gpl.html
41 ***************************************************************/
44 #include <ogdf/basic/simple_graph_alg.h>
46 //-------------------------------------
47 //zwei Moeglichkeiten: Elemente verstecken mit hide/activate
48 //oder Elemente, die nicht akiv sind, loeschen
50 #include <ogdf/planarity/PlanRepInc.h>
51 #include <ogdf/basic/TopologyModule.h>
52 #include <ogdf/basic/Math.h>
57 PlanRepInc::PlanRepInc(const UMLGraph
&UG
) : PlanRepUML(UG
)
62 PlanRepInc::PlanRepInc(const UMLGraph
&UG
, const NodeArray
<bool> &fixed
)
68 //we start the node activation status with the fixed input values
72 m_activeNodes
[v
] = fixed
[v
];
78 void PlanRepInc::initMembers(const UMLGraph
&UG
)
80 m_activeNodes
.init(UG
, true);
81 //braucht man vielleicht gar nicht mehr (Kreuzungen?)
82 //zumindest spaeter durch type in typefields ersetzen
83 m_treeEdge
.init(*this, false);
88 //activate a cc with at least one node, even if all nodes are
90 node
PlanRepInc::initMinActiveCC(int i
)
92 node v
= initActiveCCGen(i
, true);
96 void PlanRepInc::initActiveCC(int i
)
98 initActiveCCGen(i
, false);
101 //activates a cc, if minnode==true, at least one node is inserted
102 node
PlanRepInc::initActiveCCGen(int i
, bool minNode
)
104 //node to be returned
106 //list to be filled wih activated nodes
107 List
<node
> activeOrigCCNodes
;
108 // a) delete copy / chain fields for originals of nodes in current cc,
109 // since we change the CC number...
110 // (since we remove all these copies in initByNodes(...)
111 // b) create list of currently active original nodes
113 const List
<node
> &origInCC
= nodesInCC(i
);
115 ListConstIterator
<node
> itV
;
117 for(itV
= origInCC
.begin(); itV
.valid(); ++itV
)
119 if (m_activeNodes
[(*itV
)])
120 activeOrigCCNodes
.pushBack((*itV
));
121 if (m_currentCC
>= 0)
130 if ((adj
->index() & 1) == 0) continue;
131 edge eG
= adj
->theEdge();
139 //now we check if we have to activate a single node
142 if (activeOrigCCNodes
.size() == 0)
144 //Simple strategy: take the first node
145 minActive
= origInCC
.front();
148 m_activeNodes
[minActive
] = true;
149 activeOrigCCNodes
.pushFront(minActive
);
156 //double feature: liste und nodearray, besser
157 GraphCopy::initByActiveNodes(activeOrigCCNodes
, m_activeNodes
, m_eAuxCopy
);
159 // set type of edges (gen. or assoc.) in the current CC
161 if (m_pGraphAttributes
->attributes() & GraphAttributes::edgeType
)
162 forall_edges(e
,*this)
164 m_eType
[e
] = m_pGraphAttributes
->type(original(e
));
167 switch (m_pGraphAttributes
->type(original(e
)))
169 case Graph::generalization
: setGeneralization(e
); break;
170 case Graph::association
: setAssociation(e
); break;
176 if (m_pGraphAttributes
->attributes() & GraphAttributes::nodeType
)
177 forall_nodes(v
,*this)
178 m_vType
[v
] = m_pGraphAttributes
->type(original(v
));
179 //TODO:check only in CCs or global?
185 //node activation automatically activates all
187 //CCs can be joined by this method
188 void PlanRepInc::activateNode(node v
)
190 if (m_activeNodes
[v
]) return;
192 m_activeNodes
[v
] = true;
196 //Connect parts of partial active current CC.
197 //Note that this only makes sense when the CC parts are
198 //already correctly embedded. Crossings are already replaced
200 bool PlanRepInc::makeTreeConnected(adjEntry
/* adjExternal */)
203 //we compute node numbers for the partial CCs in order to
204 //identify the treeConnect Edges that can be deleted later
205 m_component
.init(*this, -1);
207 //if there is only one CC, we don't need to connect
208 if (isConnected(*this)) return false;
209 //We have to insert edges connnecting nodes lying on
210 //the "external" face of the active parts
212 //First, we activate the CC's parts one by one and compute
213 //the 'external' face from the layout information
214 //If the PlanRepInc is not embedded corresponding to
215 //this layout, we may introduce edges that are non-planar
216 //in the drawing, leading to problems when we compute paths
219 List
<node
> isolatedNodes
;
220 const int numPartialCC
= connectedIsolatedComponents(*this,
221 isolatedNodes
, m_component
);
223 //CombinatorialEmbedding can cope with unconnected graphs
224 //but does not provide faces for isolated nodes
225 CombinatorialEmbedding
E(*this);
227 List
<adjEntry
> extAdjs
;
228 //we run through all faces searching for all outer faces
232 //TODO: check if we should select special adjEntry instead of first
233 if (tm
.faceSum(*this, *m_pGraphAttributes
, f
) < 0)
234 extAdjs
.pushBack(f
->firstAdj());
237 cout
<< "FaceSum in Face " << f
->index() << " Groesse " << f
->size()
238 << " ist: " << tm
.faceSum(*this, *m_pGraphAttributes
, f
) <<"\n" << flush
;
242 //now we have faces for all partial CCs that are not isolated nodes
244 OGDF_ASSERT(extAdjs
.size() + isolatedNodes
.size() == numPartialCC
)
245 //OGDF_ASSERT(extAdjs.size() > 1) //eigentlich: = #partial CCs
246 //OGDF_ASSERT(extAdjs.size() == numPartialCC)
248 const int n1
= numPartialCC
-1;
249 m_eTreeArray
.init(0, n1
, 0, n1
, 0);
252 //Three cases: only CCs, only isolated nodes, and both (where
253 //only one CC + isolated nodes is possible
255 //now we connect all partial CCs by inserting edges at the adjEntries
256 //in extAdjs and adding all isolated nodes
257 adjEntry lastAdj
= 0;
258 ListIterator
<adjEntry
> it
= extAdjs
.begin();
261 //for the case: one external face, multiple isolated nodes
263 adjEntry adj
= (*it
);
264 ListIterator
<adjEntry
> it2
= it
.succ();
267 adjEntry adj2
= (*it2
);
268 edge eTree
= newEdge(adj
, adj2
);
269 m_treeEdge
[eTree
] = true;
270 lastAdj
= eTree
->adjTarget();
271 //save the connection edge by CC number index
272 //this is used when deleting obsolete edges later
273 //after edge reinsertion
274 m_eTreeArray(componentNumber(adj
->theNode()), componentNumber(adj2
->theNode())) =
275 m_eTreeArray(m_component
[adj2
->theNode()], m_component
[adj
->theNode()])
277 }//if CCs left to connect
281 while (!isolatedNodes
.empty())
283 node uvw
= isolatedNodes
.popFrontRet();
286 //same block as above
287 edge eTree
= newEdge(uvw
, lastAdj
);
288 m_treeEdge
[eTree
] = true;
289 //save the connection edge by CC number index
290 //this is used when deleting obsolete edges later
291 //after edge reinsertion
292 m_eTreeArray(componentNumber(lastAdj
->theNode()), componentNumber(uvw
)) =
293 m_eTreeArray(m_component
[uvw
], m_component
[lastAdj
->theNode()])
295 lastAdj
= eTree
->adjSource();
297 else //connect the first two isolated nodes / only iso nodes exist
299 //MUST BE #isonodes>1, else we returned already because CC connected
300 OGDF_ASSERT(!isolatedNodes
.empty())
301 node secv
= isolatedNodes
.popFrontRet();
302 //same block as above
303 edge eTree
= newEdge(uvw
, secv
);
304 m_treeEdge
[eTree
] = true;
305 //save the connection edge by CC number index
306 //this is used when deleting obsolete edges later
307 //after edge reinsertion
308 m_eTreeArray(componentNumber(secv
), componentNumber(uvw
)) =
309 m_eTreeArray(m_component
[uvw
], m_component
[secv
])
311 lastAdj
= eTree
->adjSource();
314 }//while isolated nodes
316 OGDF_ASSERT(isConnected(*this));
319 //List<adjEntry> extAdjs;
320 //getExtAdjs(extAdjs);
326 //is only called when CC not connected => m_eTreeArray is initialized
327 void PlanRepInc::deleteTreeConnection(int i
, int j
)
330 edge e
= m_eTreeArray(i
, j
);
334 OGDF_ASSERT(m_treeEdge
[e
]);
335 //we have to take care of treeConnection edges that
336 //are already crossed
337 while ((e
->target()->degree() == 4) &&
338 m_treeEdge
[e
->adjTarget()->cyclicSucc()->cyclicSucc()->theEdge()])
340 nexte
= e
->adjTarget()->cyclicSucc()->cyclicSucc()->theEdge();
341 OGDF_ASSERT(original(nexte
) == 0)
346 m_eTreeArray(i
, j
) = 0;
347 m_eTreeArray(j
, i
) = 0;
349 OGDF_ASSERT(isConnected(*this));
351 }//deleteTreeConnection
353 //is only called when CC not connected => m_eTreeArray is initialized
354 void PlanRepInc::deleteTreeConnection(int i
, int j
, CombinatorialEmbedding
&E
)
357 edge e
= m_eTreeArray(i
, j
);
361 OGDF_ASSERT(m_treeEdge
[e
]);
362 //we have to take care of treeConnection edges that
363 //are already crossed
364 while ((e
->target()->degree() == 4) &&
365 m_treeEdge
[e
->adjTarget()->cyclicSucc()->cyclicSucc()->theEdge()])
367 nexte
= e
->adjTarget()->cyclicSucc()->cyclicSucc()->theEdge();
368 OGDF_ASSERT(original(nexte
) == 0)
373 m_eTreeArray(i
, j
) = 0;
374 m_eTreeArray(j
, i
) = 0;
376 OGDF_ASSERT(isConnected(*this));
378 }//deleteTreeConnection
381 //use the layout information in the umlgraph to find nodes in
382 //unconnected active parts of a CC that can be connected without
383 //crossings in the given embedding
384 void PlanRepInc::getExtAdjs(List
<adjEntry
> & /* extAdjs */)
386 //in order not to change the current CC initialization,
387 //we construct a copy of the active parts (one by one)
388 //and use the layout information to compute a external
389 //face for that part. An (original) adjEntry on this face
390 //is then inserted into the extAdjs list.
392 //derive the unconnected parts by a run through the current
394 //compute connected component of current CC
396 NodeArray
<int> component(*this);
397 int numPartialCC
= connectedComponents(*this, component
);
398 EdgeArray
<edge
> copyEdge
;//copy edges in partial CC copy
399 //now we compute a copy for every CC
400 //initialize an array of lists of nodes contained in a CC
401 Array
<List
<node
> > nodesInPartialCC
;
402 nodesInPartialCC
.init(numPartialCC
);
405 forall_nodes(v
, *this)
406 nodesInPartialCC
[component
[v
]].pushBack(v
);
409 for (i
= 0; i
< numPartialCC
; i
++)
411 List
<node
> &theNodes
= nodesInPartialCC
[i
];
413 GC
.createEmpty(*this);
414 GC
.initByNodes(theNodes
, copyEdge
);
415 //now we derive an outer face of GC by using the
416 //layout information on it's original
419 //TODO: Insert the bend points into the copy
422 //CombinatorialEmbedding E(GC);
424 //run through the faces and compute angles to
426 //we dont care about the original structure of
427 //the graph, i.e., if crossings are inserted aso
428 //we only take the given partial CC and its layout
429 //adjEntry extAdj = getExtAdj(GC, E);
431 //forall_nodes(v, GC)
441 //return one adjEntry on the outer face of GC where GC
442 //is a partial copy of this PlanRepInc
443 adjEntry
PlanRepInc::getExtAdj(GraphCopy
& /* GC */, CombinatorialEmbedding
& /* E */)
448 //-------------------------------------
449 //structure updates of underlying graph
450 //signaled by graph structure
451 void PlanRepInc::nodeDeleted(node
/* v */)
455 void PlanRepInc::nodeAdded(node
/* v */) {}
456 void PlanRepInc::edgeDeleted(edge
/* e */) {}
457 void PlanRepInc::edgeAdded(edge
/* e */) {}
458 void PlanRepInc::reInit() {}
459 void PlanRepInc::cleared() {}
465 int PlanRepInc::genusLayout(Layout
&drawing
) const
468 GraphAttributes
AG(testGraph
, GraphAttributes::nodeGraphics
|
469 GraphAttributes::edgeGraphics
|
470 GraphAttributes::nodeColor
|
471 GraphAttributes::nodeStyle
|
472 GraphAttributes::edgeColor
475 NodeArray
<node
> tcopy(*this, 0);
476 EdgeArray
<bool> finished(*this, false);
477 EdgeArray
<edge
> eOrig(testGraph
, 0);
478 EdgeArray
<String
> eCol(*this, "");
480 if (numberOfNodes() == 0) return 0;
484 forall_nodes(v
,*this)
485 if (v
->degree() == 0) ++nIsolated
;
487 NodeArray
<int> component(*this);
488 int nCC
= connectedComponents(*this,component
);
490 AdjEntryArray
<bool> visited(*this,false);
495 forall_nodes(v
,*this)
499 ogdf::sprintf(prints
,10,"#%.2X%.2X%.2X\0",colBase
,colBase2
,colBase
);
500 colBase
= (colBase
*3) % 233;
501 colBase2
= (colBase
*2) % 233;
506 node u
= testGraph
.newNode();
508 AG
.x(u
) = drawing
.x(v
);
509 AG
.y(u
) = drawing
.y(v
);
510 AG
.colorNode(u
) = col
;
511 AG
.nodeLine(u
) = "#FF0000";
512 AG
.lineWidthNode(u
) = 8;
517 bool handled
= visited
[adj1
];
521 node z
= adj
->theNode();
524 node u1
= testGraph
.newNode();
526 AG
.x(u1
) = drawing
.x(z
);
527 AG
.y(u1
) = drawing
.y(z
);
528 AG
.colorNode(u1
) = col
;
529 }//if not yet inserted in the copy
530 if (!finished
[adj
->theEdge()])
532 node w
= adj
->theEdge()->opposite(z
);
536 if (w
== adj
->theEdge()->source())
537 e
= testGraph
.newEdge(tcopy
[w
], tcopy
[z
]);
538 else e
= testGraph
.newEdge(tcopy
[z
], tcopy
[w
]);
539 eOrig
[e
] = adj
->theEdge();
540 if (eCol
[adj
->theEdge()].length() > 0)
541 AG
.colorEdge(e
) = eCol
[adj
->theEdge()];
544 eCol
[adj
->theEdge()] = col
;
545 AG
.colorEdge(e
) = col
;
547 finished
[adj
->theEdge()] = true;
552 eCol[adj->theEdge()] = col;
557 adj
= adj
->faceCycleSucc();
558 } while (adj
!= adj1
);
560 if (handled
) continue;
565 //insert the current embedding order by setting bends
566 //forall_nodes(v, testGraph)
568 // adjEntry ad1 = v->firstAdj();
571 int genus
= (numberOfEdges() - numberOfNodes() - nIsolated
- nFaceCycles
+ 2*nCC
) / 2;
574 AG
.writeGML("GenusErrorLayout.gml");
581 void PlanRepInc::writeGML(const char *fileName
, GraphAttributes
&AG
, bool colorEmbed
)
583 OGDF_ASSERT(m_pGraphAttributes
== &(AG
));
585 ofstream
os(fileName
);
586 writeGML(os
, AG
);//drawing, colorEmbed);
588 }//writegml with AG layout
590 void PlanRepInc::writeGML(ostream
&os
, const GraphAttributes
&AG
)
592 OGDF_ASSERT(m_pGraphAttributes
== &(AG
))
593 const Graph
&G
= *this;
595 NodeArray
<int> id(*this);
598 os
.setf(ios::showpoint
);
601 os
<< "Creator \"ogdf::PlanRepInc::writeGML\"\n";
603 os
<< " directed 1\n";
607 if (!original(v
)) continue;
610 os
<< " id " << (id
[v
] = nextId
++) << "\n";
612 os
<< " graphics [\n";
613 os
<< " x " << AG
.x(original(v
)) << "\n";
614 os
<< " y " << AG
.y(original(v
)) << "\n";
615 os
<< " w " << 10.0 << "\n";
616 os
<< " h " << 10.0 << "\n";
617 os
<< " type \"rectangle\"\n";
618 os
<< " width 1.0\n";
619 if (typeOf(v
) == Graph::generalizationMerger
) {
620 os
<< " type \"oval\"\n";
621 os
<< " fill \"#0000A0\"\n";
623 else if (typeOf(v
) == Graph::generalizationExpander
) {
624 os
<< " type \"oval\"\n";
625 os
<< " fill \"#00FF00\"\n";
627 else if (typeOf(v
) == Graph::highDegreeExpander
||
628 typeOf(v
) == Graph::lowDegreeExpander
)
629 os
<< " fill \"#FFFF00\"\n";
630 else if (typeOf(v
) == Graph::dummy
)
632 if (isCrossingType(v
))
634 os
<< " fill \"#FF0000\"\n";
637 os
<< " fill \"#FFFFFF\"\n";
638 os
<< " type \"oval\"\n";
641 else if (v
->degree() > 4)
642 os
<< " fill \"#FFFF00\"\n";
645 os
<< " fill \"#000000\"\n";
647 os
<< " ]\n"; // graphics
654 if (!(original(e
->source()) && original(e
->target()))) continue;
657 os
<< " source " << id
[e
->source()] << "\n";
658 os
<< " target " << id
[e
->target()] << "\n";
660 os
<< " generalization " << typeOf(e
) << "\n";
662 os
<< " graphics [\n";
664 os
<< " type \"line\"\n";
666 if (typeOf(e
) == Graph::generalization
)
668 os
<< " arrow \"last\"\n";
669 if (m_alignUpward
[e
->adjSource()])
670 os
<< " fill \"#0000FF\"\n";
672 os
<< " fill \"#FF0000\"\n";
673 os
<< " width 3.0\n";
677 if (typeOf(e
->source()) == Graph::generalizationExpander
||
678 typeOf(e
->source()) == Graph::generalizationMerger
||
679 typeOf(e
->target()) == Graph::generalizationExpander
||
680 typeOf(e
->target()) == Graph::generalizationMerger
)
682 os
<< " arrow \"none\"\n";
684 os
<< " fill \"#F0F000\"\n"; //gelb
685 else if (isHalfBrother(e
))
686 os
<< " fill \"#FF00AF\"\n";
688 os
<< " fill \"#FF0000\"\n";
691 os
<< " arrow \"none\"\n";
693 os
<< " fill \"#F0F000\"\n"; //gelb
694 else if (isHalfBrother(e
))
695 os
<< " fill \"#FF00AF\"\n";
697 os
<< " fill \"#00000F\"\n";
698 os
<< " width 1.0\n";
699 }//else generalization
701 if (original(e
) != 0)
703 const DPolyline
&dpl
= AG
.bends(original(e
));
706 os
<< " point [ x " << AG
.x(original(e
->source())) << " y " <<
707 AG
.y(original(e
->source())) << " ]\n";
709 ListConstIterator
<DPoint
> it
;
710 for(it
= dpl
.begin(); it
.valid(); ++it
)
711 os
<< " point [ x " << (*it
).m_x
<< " y " << (*it
).m_y
<< " ]\n";
713 os
<< " point [ x " << AG
.x(original(e
->target())) << " y " <<
714 AG
.y(original(e
->target())) << " ]\n";
716 os
<< " ]\n"; // Line
720 os
<< " ]\n"; // graphics
722 os
<< " ]\n"; // edge
725 os
<< "]\n"; // graph
729 double angle(DPoint p
, DPoint q
, DPoint r
)
731 double dx1
= q
.m_x
- p
.m_x
, dy1
= q
.m_y
- p
.m_y
;
732 double dx2
= r
.m_x
- p
.m_x
, dy2
= r
.m_y
- p
.m_y
;
734 //two vertices on the same place!
735 if ((dx1
== 0 && dy1
== 0) || (dx2
== 0 && dy2
== 0))
738 double norm
= (dx1
*dx1
+dy1
*dy1
)*(dx2
*dx2
+dy2
*dy2
);
740 double cosphi
= (dx1
*dx2
+dy1
*dy2
) / sqrt(norm
);
742 if (cosphi
>= 1.0 ) return 0; if (cosphi
<= -1.0 ) return Math::pi
;
744 double phi
= acos(cosphi
);
746 if (dx1
*dy2
< dy1
*dx2
) phi
= -phi
;
748 if (phi
< 0) phi
+= 2*Math::pi
;
753 double fAngle(DPoint p
, DPoint q
, DPoint r
)
755 return angle(p
, q
, r
)*360.0/(2*Math::pi
);
759 void PlanRepInc::writeGML(ostream
&os
, const Layout
&drawing
, bool colorEmbed
)
761 const Graph
&G
= *this;
763 NodeArray
<int> id(*this);
766 os
.setf(ios::showpoint
);
769 os
<< "Creator \"ogdf::GraphAttributes::writeGML\"\n";
772 os
<< " directed 1\n";
778 os
<< " id " << (id
[v
] = nextId
++) << "\n";
780 os
<< " graphics [\n";
781 os
<< " x " << drawing
.x(v
) << "\n";
782 os
<< " y " << drawing
.y(v
) << "\n";
783 os
<< " w " << 10.0 << "\n";
784 os
<< " h " << 10.0 << "\n";
785 os
<< " type \"rectangle\"\n";
786 os
<< " width 1.0\n";
787 if (typeOf(v
) == Graph::generalizationMerger
) {
788 os
<< " type \"oval\"\n";
789 os
<< " fill \"#0000A0\"\n";
791 else if (typeOf(v
) == Graph::generalizationExpander
) {
792 os
<< " type \"oval\"\n";
793 os
<< " fill \"#00FF00\"\n";
795 else if (typeOf(v
) == Graph::highDegreeExpander
||
796 typeOf(v
) == Graph::lowDegreeExpander
)
797 os
<< " fill \"#FFFF00\"\n";
798 else if (typeOf(v
) == Graph::dummy
)
800 if (isCrossingType(v
))
802 os
<< " fill \"#FF0000\"\n";
804 else os
<< " fill \"#FFFFFF\"\n";
805 os
<< " type \"oval\"\n";
808 else if (v
->degree() > 4)
809 os
<< " fill \"#FFFF00\"\n";
812 os
<< " fill \"#000000\"\n";
815 os
<< " ]\n"; // graphics
817 os
<< " ]\n"; // node
821 NodeArray
<bool> proc(*this, false);
827 os
<< " source " << id
[e
->source()] << "\n";
828 os
<< " target " << id
[e
->target()] << "\n";
830 os
<< " generalization " << typeOf(e
) << "\n";
831 os
<< " graphics [\n";
833 os
<< " type \"line\"\n";
840 //Find out embedding order number
841 //dirty hack, quadratic time
842 //color after higher degree order
845 if (proc
[e
->target()] && !proc
[e
->source()]) w
= e
->target();
846 else if (proc
[e
->source()] && !proc
[e
->target()]) w
= e
->source();
849 w
= (e
->source()->degree() > e
->target()->degree() ? e
->source() : e
->target());
852 adjEntry adf
= w
->firstAdj();
853 while (adf
->theEdge() != e
) //ugly
856 adf
= adf
->cyclicSucc();
859 node uvw
= e
->source();
860 adf
= uvw
->firstAdj();
861 while (adf
->theEdge() != e
) //ugly
864 adf
= adf
->cyclicSucc();
867 adf
= uvw
->firstAdj();
868 while (adf
->theEdge() != e
) //ugly
871 adf
= adf
->cyclicSucc();
877 if (typeOf(e
) == Graph::generalization
)
879 os
<< " arrow \"last\"\n";
881 if (m_alignUpward
[e
->adjSource()])
882 os
<< " fill \"#0000FF\"\n";
887 case 1: os
<< " fill \"#F00000\"\n";break;
888 case 2: os
<< " fill \"#D00000\"\n";break;
889 case 3: os
<< " fill \"#B00000\"\n";break;
890 case 4: os
<< " fill \"#900000\"\n";break;
891 case 5: os
<< "fill \"#800000\"\n";break;
892 case 6: os
<< " fill \"#600000\"\n";break;
893 case 7: os
<< " fill \"#400000\"\n";break;
894 default: os
<< " fill \"#FF0000\"\n";
897 os
<< " width 3.0\n";
901 if (typeOf(e
->source()) == Graph::generalizationExpander
||
902 typeOf(e
->source()) == Graph::generalizationMerger
||
903 typeOf(e
->target()) == Graph::generalizationExpander
||
904 typeOf(e
->target()) == Graph::generalizationMerger
)
906 os
<< " arrow \"none\"\n";
908 os
<< " fill \"#F0F000\"\n"; //gelb
909 else if (isHalfBrother(e
))
910 os
<< " fill \"#FF00AF\"\n";
912 os
<< " fill \"#FF0000\"\n";
915 os
<< " arrow \"none\"\n";
917 os
<< " fill \"#F0F000\"\n"; //gelb
918 else if (isHalfBrother(e
))
919 os
<< " fill \"#FF00AF\"\n";
923 case 1: os
<< " fill \"#000030\"\n";break;
924 case 2: os
<< " fill \"#000060\"\n";break;
925 case 3: os
<< " fill \"#200090\"\n";break;
926 case 4: os
<< " fill \"#3000B0\"\n";break;
927 case 5: os
<< " fill \"#4000E0\"\n";break;
928 case 6: os
<< " fill \"#5000F0\"\n";break;
929 case 7: os
<< " fill \"#8000FF\"\n";break;
930 default: os
<< " fill \"#000000\"\n";;
934 os
<< " width 1.0\n";
935 }//else generalization
937 //insert a bend at each end corresponding to the
942 node vs
= e
->source();
943 node vt
= e
->target();
944 double sx
, sy
, tx
, ty
;
946 const double xs
= drawing
.x(vs
),
947 ys
= drawing
.y(vs
); //aufpunkt
948 const double xt
= drawing
.x(vt
),
949 yt
= drawing
.y(vt
); //aufpunkt
951 //double dx = drawing.x(vt) - drawing.x(vs);
952 //double dy = drawing.y(vt) - drawing.y(vs);
953 //double length = sqrt(dx*dx + dy*dy);
956 node rws
= vs
->firstAdj()->twinNode();
957 node rwt
= vt
->firstAdj()->twinNode();
959 double rdxs
= drawing
.x(rws
) - xs
;
960 double rdys
= drawing
.y(rws
) - ys
;
961 double rdxt
= drawing
.x(rwt
) - xt
;
962 double rdyt
= drawing
.y(rwt
) - yt
;
964 double rslength
= sqrt(rdxs
*rdxs
+ rdys
*rdys
);
965 double rtlength
= sqrt(rdxt
*rdxt
+ rdyt
*rdyt
);
967 double refSx
= drawing
.x(vs
) + (rad
/rslength
)*rdxs
;
968 double refSy
= drawing
.y(vs
) + (rad
/rslength
)*rdys
;
969 double refTx
= drawing
.x(vt
) + (rad
/rtlength
)*rdxt
;
970 double refTy
= drawing
.y(vt
) + (rad
/rtlength
)*rdyt
;
972 double refSx2
= drawing
.x(vs
) + rdxs
/rslength
;
973 double refSy2
= drawing
.y(vs
) + rdys
/rslength
;
974 double refTx2
= drawing
.x(vt
) + rdxt
/rslength
;
975 double refTy2
= drawing
.y(vt
) + rdyt
/rslength
;
977 //do not change reference edge
985 OGDF_ASSERT(sNum
< vs
->degree())
986 double angleS
= sNum
*360.0/vs
->degree();
988 double refAngleS
= fAngle(DPoint(xs
, ys
),
989 DPoint(refSx2
, refSy2
),
992 double testAngleS
= refAngleS
-angleS
;
995 double dTS
= testAngleS
*2*Math::pi
/360.0;
997 sx
= xs
+ rad
*cos(dTS
);//testAngleS);
998 sy
= ys
+ rad
*sin(dTS
);//testAngleS);
1007 OGDF_ASSERT(tNum
< vt
->degree())
1008 double angleT
= tNum
*360/vt
->degree();
1010 double refAngleT
= fAngle(DPoint(xt
, yt
),
1011 DPoint(refTx2
, refTy2
),
1014 double testAngleT
= refAngleT
-angleT
;
1017 double dTT
= testAngleT
*2*Math::pi
/360.0;
1020 tx
= xt
+ rad
*cos(dTT
);//testAngleT);
1021 ty
= yt
+ rad
*sin(dTT
);//testAngleT);
1026 os
<< " point [ x " << xs
<< " y " << ys
<< " ]\n";
1027 os
<< " point [ x " << sx
<< " y " << sy
<< " ]\n";
1028 os
<< " point [ x " << tx
<< " y " << ty
<< " ]\n";
1029 os
<< " point [ x " << xt
<< " y " << yt
<< " ]\n";
1031 os
<< " ]\n"; // Line
1034 os
<< " ]\n"; // graphics
1036 os
<< " ]\n"; // edge
1039 os
<< "]\n"; // graph
1046 }//end namespace ogdf