6 * $Date: 2012-07-10 17:25:20 +0200 (Di, 10. Jul 2012) $
7 ***************************************************************/
10 * \brief Implementation of class GraphAttributes.
12 * Class GraphAttributes extends a graph by graphical attributes like
13 * node position, color, etc.
15 * \author Carsten Gutwenger
18 * This file is part of the Open Graph Drawing Framework (OGDF).
22 * See README.txt in the root directory of the OGDF installation for details.
25 * This program is free software; you can redistribute it and/or
26 * modify it under the terms of the GNU General Public License
27 * Version 2 or 3 as published by the Free Software Foundation;
28 * see the file LICENSE.txt included in the packaging of this file
32 * This program is distributed in the hope that it will be useful,
33 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 * GNU General Public License for more details.
38 * You should have received a copy of the GNU General Public
39 * License along with this program; if not, write to the Free
40 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
41 * Boston, MA 02110-1301, USA.
43 * \see http://www.gnu.org/copyleft/gpl.html
44 ***************************************************************/
47 #include <ogdf/basic/GraphAttributes.h>
48 #include <ogdf/fileformats/GmlParser.h>
49 #include <ogdf/fileformats/XmlParser.h>
54 //---------------------------------------------------------
56 // graph topology + graphical attributes
57 //---------------------------------------------------------
59 GraphAttributes::GraphAttributes() : m_pGraph(0), m_directed(true) { }
63 GraphAttributes::GraphAttributes(const Graph
&G
, long initAttr
) :
64 m_pGraph(&G
), m_directed(true), m_attributes(0)
66 initAttributes(m_attributes
= initAttr
);
71 void GraphAttributes::initAttributes(long attr
)
75 //no color without graphics
76 OGDF_ASSERT( (m_attributes
& nodeGraphics
) != 0 || (m_attributes
& nodeColor
) == 0);
77 //no fill and linewithout graphics
78 OGDF_ASSERT( (m_attributes
& nodeGraphics
) != 0 || (attr
& nodeStyle
) == 0);
79 //no color without graphics
80 OGDF_ASSERT( (m_attributes
& edgeGraphics
) != 0 || (attr
& edgeColor
) == 0);
82 if (attr
& nodeGraphics
) {
83 m_x
.init(*m_pGraph
,0.0);
84 m_y
.init(*m_pGraph
,0.0);
85 m_width
.init(*m_pGraph
,0.0);
86 m_height
.init(*m_pGraph
,0.0);
87 m_nodeShape
.init(*m_pGraph
,rectangle
);
92 m_nodeColor
.init(*m_pGraph
, "");
93 m_nodeLine
.init(*m_pGraph
, "");
98 m_nodePattern
.init(*m_pGraph
, bpNone
);
99 m_nodeStyle
.init(*m_pGraph
, esSolid
);
100 m_nodeLineWidth
.init(*m_pGraph
, 1);
101 //images should not be added here as nodestyle, purely experimental
102 //such that it fits PG code
104 m_imageUri
.init(*m_pGraph
, "");
105 m_imageStyle
.init(*m_pGraph
, GraphAttributes::FreeScale
);
106 m_imageAlign
.init(*m_pGraph
, GraphAttributes::Center
);
107 m_imageDrawLine
.init(*m_pGraph
, false);
108 m_imageWidth
.init(*m_pGraph
, 0);
109 m_imageHeight
.init(*m_pGraph
, 0);
112 if (attr
& edgeGraphics
) {
113 m_bends
.init(*m_pGraph
,DPolyline());
116 if (attr
& edgeColor
)
117 m_edgeColor
.init(*m_pGraph
);
119 if (attr
& edgeStyle
)
121 m_edgeStyle
.init(*m_pGraph
, esSolid
);
122 m_edgeWidth
.init(*m_pGraph
, 1.0);
125 if (attr
& nodeLevel
) {
126 m_level
.init(*m_pGraph
,0);
128 if (attr
& nodeWeight
) {
129 m_nodeIntWeight
.init(*m_pGraph
,0);
131 if (attr
& edgeIntWeight
) {
132 m_intWeight
.init(*m_pGraph
,1);
134 if (attr
& edgeDoubleWeight
) {
135 m_doubleWeight
.init(*m_pGraph
,1.0);
137 if (attr
& nodeLabel
) {
138 m_nodeLabel
.init(*m_pGraph
);
140 if (attr
& edgeLabel
) {
141 m_edgeLabel
.init(*m_pGraph
);
143 if (attr
& edgeType
) {
144 m_eType
.init(*m_pGraph
,Graph::association
);//should be Graph::standard end explicitly set
146 if (attr
& nodeType
) {
147 m_vType
.init(*m_pGraph
,Graph::vertex
);
150 m_nodeId
.init(*m_pGraph
, -1);
152 if (attr
& edgeArrow
) {
153 m_edgeArrow
.init(*m_pGraph
, undefined
);
155 if (attr
& nodeTemplate
) {
156 m_nodeTemplate
.init(*m_pGraph
);
158 if (attr
& edgeSubGraph
) {
159 m_subGraph
.init(*m_pGraph
,0);
163 void GraphAttributes::destroyAttributes(long attr
)
165 m_attributes
&= ~attr
;
167 if (attr
& nodeGraphics
) {
173 if (attr
& nodeColor
)
175 if (attr
& nodeStyle
)
177 m_nodePattern
.init();
179 m_nodeLineWidth
.init();
180 //should have its own trigger attribute
185 m_imageDrawLine
.init();
187 m_imageHeight
.init();
191 if (attr
& edgeGraphics
) {
194 if (attr
& edgeColor
)
198 if (attr
& edgeStyle
)
204 if (attr
& nodeLevel
) {
207 if (attr
& nodeWeight
) {
208 m_nodeIntWeight
.init();
210 if (attr
& edgeIntWeight
) {
213 if (attr
& edgeDoubleWeight
) {
214 m_doubleWeight
.init();
216 if (attr
& nodeLabel
) {
219 if (attr
& edgeLabel
) {
225 if (attr
& edgeArrow
) {
228 if (attr
& nodeTemplate
) {
229 m_nodeTemplate
.init();
231 if (attr
& edgeSubGraph
) {
237 void GraphAttributes::init(const Graph
&G
, long initAttr
)
240 destroyAttributes(m_attributes
);
242 initAttributes(m_attributes
= initAttr
);
245 void GraphAttributes::setAllWidth(double w
)
248 forall_nodes(v
,*m_pGraph
)
253 void GraphAttributes::setAllHeight(double h
)
256 forall_nodes(v
,*m_pGraph
)
261 void GraphAttributes::clearAllBends()
264 forall_edges(e
,*m_pGraph
)
269 bool GraphAttributes::readGML(Graph
&G
, const String
&fileName
)
271 ifstream
is(fileName
.cstr());
273 return false; // couldn't open file
275 return readGML(G
,is
);
279 bool GraphAttributes::readGML(Graph
&G
, istream
&is
)
285 return gml
.read(G
,*this);
289 bool GraphAttributes::readRudy(Graph
&G
, const String
&fileName
)
291 ifstream
is(fileName
.cstr());
294 return readRudy(G
,is
);
298 bool GraphAttributes::readRudy(Graph
&G
, istream
&is
)
311 Array
<node
> mapToNode(0,n
-1,0);
313 if (attributes() & edgeDoubleWeight
){
315 is
>> src
>> tgt
>> weight
;
318 if(mapToNode
[src
] == 0) mapToNode
[src
] = G
.newNode(src
);
319 if(mapToNode
[tgt
] == 0) mapToNode
[tgt
] = G
.newNode(tgt
);
320 e
= G
.newEdge(mapToNode
[src
],mapToNode
[tgt
]);
321 this->doubleWeight(e
)=weight
;
328 void GraphAttributes::writeRudy(const String
&fileName
) const
330 ofstream
os(fileName
.cstr());
335 void GraphAttributes::writeRudy(ostream
&os
) const
337 const Graph
&G
= this->constGraph();
338 os
<< G
.numberOfNodes() << " " << G
.numberOfEdges() << endl
;
341 if (attributes() & edgeDoubleWeight
){
343 os
<< (e
->source()->index())+1 << " " << (e
->target()->index())+1;
344 os
<< " " << this->doubleWeight(e
) << endl
;
347 else forall_edges(e
,G
) os
<< (e
->source()->index())+1 << " " << (e
->target()->index())+1 << endl
;
351 const int c_maxLengthPerLine
= 200;
353 void GraphAttributes::writeLongString(ostream
&os
, const String
&str
) const
358 const char *p
= str
.cstr();
371 // ignored white space
382 if(num
>= c_maxLengthPerLine
) {
394 void GraphAttributes::writeGML(const String
&fileName
) const
396 ofstream
os(fileName
.cstr());
401 void GraphAttributes::writeGML(ostream
&os
) const
403 NodeArray
<int> id(*m_pGraph
);
406 os
.setf(ios::showpoint
);
409 os
<< "Creator \"ogdf::GraphAttributes::writeGML\"\n";
413 os
<< (m_directed
? " directed 1\n" : " directed 0\n");
416 forall_nodes(v
,*m_pGraph
) {
419 os
<< " id " << (id
[v
] = nextId
++) << "\n";
421 if (attributes() & nodeTemplate
) {
423 writeLongString(os
, templateNode(v
));
427 if (attributes() & nodeLabel
) {
428 //os << "label \"" << labelNode(v) << "\"\n";
430 writeLongString(os
, labelNode(v
));
434 if (m_attributes
& nodeGraphics
) {
435 os
<< " graphics [\n";
436 os
<< " x " << m_x
[v
] << "\n";
437 os
<< " y " << m_y
[v
] << "\n";
438 os
<< " w " << m_width
[v
] << "\n";
439 os
<< " h " << m_height
[v
] << "\n";
440 if (m_attributes
& nodeColor
)
442 os
<< " fill \"" << m_nodeColor
[v
] << "\"\n";
443 os
<< " line \"" << m_nodeLine
[v
] << "\"\n";
445 if (m_attributes
& nodeStyle
)
447 os
<< " pattern \"" << m_nodePattern
[v
] << "\"\n";
448 os
<< " stipple " << styleNode(v
) << "\n";
449 os
<< " lineWidth " << lineWidthNode(v
) << "\n";
451 switch (m_nodeShape
[v
])
453 case rectangle
: os
<< " type \"rectangle\"\n"; break;
454 case oval
: os
<< " type \"oval\"\n"; break;
456 os
<< " width 1.0\n";
457 os
<< " ]\n"; // graphics
460 os
<< " ]\n"; // node
464 forall_edges(e
,*m_pGraph
) {
467 os
<< " source " << id
[e
->source()] << "\n";
468 os
<< " target " << id
[e
->target()] << "\n";
470 if (attributes() & edgeLabel
){
472 writeLongString(os
, labelEdge(e
));
475 if (attributes() & edgeType
)
476 os
<< " generalization " << type(e
) << "\n";
478 if (attributes() & edgeSubGraph
)
479 os
<< " subgraph " << subGraphBits(e
) << "\n";
481 if (m_attributes
& edgeGraphics
) {
482 os
<< " graphics [\n";
484 os
<< " type \"line\"\n";
486 if (attributes() & GraphAttributes::edgeType
) {
487 if (attributes() & GraphAttributes::edgeArrow
) {
488 switch(arrowEdge(e
)) {
489 case GraphAttributes::none
:
490 os
<< " arrow \"none\"\n";
493 case GraphAttributes::last
:
494 os
<< " arrow \"last\"\n";
497 case GraphAttributes::first
:
498 os
<< " arrow \"first\"\n";
501 case GraphAttributes::both
:
502 os
<< " arrow \"both\"\n";
505 case GraphAttributes::undefined
:
514 if (type(e
) == Graph::generalization
)
515 os
<< " arrow \"last\"\n";
517 os
<< " arrow \"none\"\n";
520 } else { // GraphAttributes::edgeType not used
522 os
<< " arrow \"last\"\n";
524 os
<< " arrow \"none\"\n";
528 if (attributes() & GraphAttributes::edgeStyle
)
530 os
<< " stipple " << styleEdge(e
) << "\n";
531 os
<< " lineWidth " << edgeWidth(e
) << "\n";
532 }//edgestyle is gml graphlet extension!!!
533 if (attributes() & edgeDoubleWeight
)
535 os
<< " weight " << doubleWeight(e
) << "\n";
537 //hier noch Unterscheidung Knotentypen, damit die Berechnung
538 //fuer Ellipsen immer bis zum echten Rand rechnet
539 const DPolyline
&dpl
= m_bends
[e
];
543 node v
= e
->source();
544 if(dpl
.front().m_x
< m_x
[v
] - m_width
[v
]/2 ||
545 dpl
.front().m_x
> m_x
[v
] + m_width
[v
]/2 ||
546 dpl
.front().m_y
< m_y
[v
] - m_height
[v
]/2 ||
547 dpl
.front().m_y
> m_y
[v
] + m_height
[v
]/2)
549 os
<< " point [ x " << m_x
[e
->source()] << " y " <<
550 m_y
[e
->source()] << " ]\n";
553 ListConstIterator
<DPoint
> it
;
554 for(it
= dpl
.begin(); it
.valid(); ++it
)
555 os
<< " point [ x " << (*it
).m_x
<< " y " << (*it
).m_y
<< " ]\n";
558 if(dpl
.back().m_x
< m_x
[v
] - m_width
[v
]/2 ||
559 dpl
.back().m_x
> m_x
[v
] + m_width
[v
]/2 ||
560 dpl
.back().m_y
< m_y
[v
] - m_height
[v
]/2 ||
561 dpl
.back().m_y
> m_y
[v
] + m_height
[v
]/2)
563 os
<< " point [ x " << m_x
[e
->target()] << " y " <<
564 m_y
[e
->target()] << " ]\n";
567 os
<< " ]\n"; // Line
570 //output width and color
571 if ((m_attributes
& edgeColor
) &&
572 (m_edgeColor
[e
].length() != 0))
573 os
<< " fill \"" << m_edgeColor
[e
] << "\"\n";
575 os
<< " ]\n"; // graphics
578 os
<< " ]\n"; // edge
581 os
<< "]\n"; // graph
585 bool GraphAttributes::readXML(Graph
&G
, const String
&fileName
)
587 ifstream
is(fileName
.cstr());
588 return readXML(G
,is
);
592 bool GraphAttributes::readXML(Graph
&G
, istream
&is
)
594 // need at least these attributes
595 initAttributes(~m_attributes
&
596 (nodeGraphics
| edgeGraphics
| nodeLabel
| edgeLabel
));
599 if (xml
.error()) return false;
601 return xml
.read(G
,*this);
607 // calculates the bounding box of the graph
608 const DRect
GraphAttributes::boundingBox() const
610 double minx
, maxx
, miny
, maxy
;
611 const Graph
&G
= constGraph();
612 const GraphAttributes
&AG
= *this;
613 node v
= G
.firstNode();
616 minx
= maxx
= miny
= maxy
= 0.0;
619 minx
= AG
.x(v
) - AG
.width(v
)/2;
620 maxx
= AG
.x(v
) + AG
.width(v
)/2;
621 miny
= AG
.y(v
) - AG
.height(v
)/2;
622 maxy
= AG
.y(v
) + AG
.height(v
)/2;
625 double x1
= AG
.x(v
) - AG
.width(v
)/2;
626 double x2
= AG
.x(v
) + AG
.width(v
)/2;
627 double y1
= AG
.y(v
) - AG
.height(v
)/2;
628 double y2
= AG
.y(v
) + AG
.height(v
)/2;
630 if (x1
< minx
) minx
= x1
;
631 if (x2
> maxx
) maxx
= x2
;
632 if (y1
< miny
) miny
= y1
;
633 if (y2
> maxy
) maxy
= y2
;
639 const DPolyline
&dpl
= AG
.bends(e
);
640 ListConstIterator
<DPoint
> iter
;
641 for (iter
= dpl
.begin(); iter
.valid(); ++iter
) {
642 if ((*iter
).m_x
< minx
) minx
= (*iter
).m_x
;
643 if ((*iter
).m_x
> maxx
) maxx
= (*iter
).m_x
;
644 if ((*iter
).m_y
< miny
) miny
= (*iter
).m_y
;
645 if ((*iter
).m_y
> maxy
) maxy
= (*iter
).m_y
;
649 return DRect(minx
, miny
, maxx
, maxy
);
654 // returns a list of all hierachies in the graph (a hierachy consists of a set of nodes)
655 // at least one list is returned, which is the list of all nodes not belonging to any hierachy
656 // this is always the first list
657 // the return-value of this function is the number of hierachies
658 int GraphAttributes::hierarchyList(List
<List
<node
>* > &list
) const
660 // list must be empty during startup
661 OGDF_ASSERT(list
.empty());
663 const Graph
&G
= constGraph();
664 Array
<bool> processed(0, G
.maxNodeIndex(), false);
668 // initialize the first list of all single nodes
669 List
<node
> *firstList
= OGDF_NEW List
<node
>;
670 list
.pushBack(firstList
);
672 forall_nodes(v
, G
) { // scan all nodes
674 // skip, if already processed
675 if (processed
[v
->index()])
678 List
<node
> nodeSet
; // set of nodes in this hierachy,
679 // whose neighbours have to be processed
680 List
<node
> *hierachy
= OGDF_NEW List
<node
>; // holds all nodes in this hierachy
682 nodeSet
.pushBack(v
); // push the unprocessed node to the list
683 processed
[v
->index()] = true; // and mark it as processed
685 do { // scan all neighbours of nodes in 'nodeSet'
686 node v
= nodeSet
.popFrontRet();
687 hierachy
->pushBack(v
); // push v to the list of nodes in this hierachy
689 // process all the neighbours of v, e.g. push them into 'nodeSet'
690 forall_adj_edges(e
, v
) {
691 if (type(e
) == Graph::generalization
) {
692 node w
= e
->source() == v
? e
->target() : e
->source();
693 if (!processed
[w
->index()]) {
695 processed
[w
->index()] = true;
699 } while (!nodeSet
.empty());
701 // skip adding 'hierachy', if it contains only one node
702 if (hierachy
->size() == 1) {
703 firstList
->conc(*hierachy
);
707 list
.pushBack(hierachy
);
710 return list
.size() - 1 + (*list
.begin())->size();
715 // returns a list of all hierarchies in the graph (in this case, a hierarchy consists of a set of edges)
716 // list may be empty, if no generalizations are used
717 // the return-value of this function is the number of hierarchies with generalizations
718 int GraphAttributes::hierarchyList(List
<List
<edge
>* > &list
) const
720 // list must be empty during startup
721 OGDF_ASSERT(list
.empty());
723 const Graph
&G
= constGraph();
724 Array
<bool> processed(0, G
.maxNodeIndex(), false);
728 forall_nodes(v
, G
) { // scan all nodes
730 // skip, if already processed
731 if (processed
[v
->index()])
734 List
<node
> nodeSet
; // set of nodes in this hierarchy,
735 // whose neighbours have to be processed
736 List
<edge
> *hierarchy
= OGDF_NEW List
<edge
>; // holds all edges in this hierarchy
738 nodeSet
.pushBack(v
); // push the unprocessed node to the list
739 processed
[v
->index()] = true; // and mark it as processed
741 do { // scan all neighbours of nodes in 'nodeSet'
742 node v
= nodeSet
.popFrontRet();
744 // process all the neighbours of v, e.g. push them into 'nodeSet'
745 forall_adj_edges(e
, v
) {
746 if (type(e
) == Graph::generalization
) {
747 node w
= e
->source() == v
? e
->target() : e
->source();
748 if (!processed
[w
->index()]) {
750 processed
[w
->index()] = true;
751 hierarchy
->pushBack(e
); // push e to the list of edges in this hierarchy
755 } while (!nodeSet
.empty());
757 // skip adding 'hierarchy', if it contains only one node
758 if (hierarchy
->empty())
761 list
.pushBack(hierarchy
);
769 void GraphAttributes::removeUnnecessaryBendsHV()
772 forall_edges(e
,*m_pGraph
)
774 DPolyline
&dpl
= m_bends
[e
];
779 ListIterator
<DPoint
> it1
, it2
, it3
;
786 if(((*it1
).m_x
== (*it2
).m_x
&& (*it2
).m_x
== (*it3
).m_x
) ||
787 ((*it1
).m_y
== (*it2
).m_y
&& (*it2
).m_y
== (*it3
).m_y
))
797 } while(it3
.valid());
802 void GraphAttributes::writeXML(
803 const String
&fileName
,
804 const char* delimiter
,
805 const char* offset
) const
807 ofstream
os(fileName
.cstr());
808 writeXML(os
,delimiter
,offset
);
812 void GraphAttributes::writeXML(
814 const char* delimiter
,
815 const char* offset
) const
817 NodeArray
<int> id(*m_pGraph
);
821 os
.setf(ios::showpoint
);
824 os
<< "<GRAPH TYPE=\"SSJ\">" << delimiter
;
827 forall_nodes(v
,*m_pGraph
) {
828 if (m_attributes
& nodeLabel
)
830 os
<< "<NODE NAME=\"" << m_nodeLabel
[v
] << "\">" << delimiter
;
834 if (m_attributes
& nodeGraphics
) {
835 os
<< offset
<< "<POSITION X=\"" << m_x
[v
] << "\" ";
836 os
<< "Y=\"" << m_y
[v
] << "\" /> " << delimiter
;
837 os
<< offset
<< "<SIZE WIDTH=\"" << m_width
[v
] << "\" ";
838 os
<< "HEIGHT=\"" << m_height
[v
] << "\" />" << delimiter
;
841 os
<< "</NODE>" << delimiter
;
845 forall_edges(e
,*m_pGraph
) {
846 if (m_attributes
& edgeLabel
)
848 os
<< "<EDGE NAME=\"" << m_edgeLabel
[e
] << "\" ";
850 if (m_attributes
& nodeLabel
)
852 os
<< "SOURCE=\"" << m_nodeLabel
[e
->source()] << "\" ";
853 os
<< "TARGET=\"" << m_nodeLabel
[e
->target()] << "\" ";
854 os
<< "GENERALIZATION=\"" << (m_eType
[e
]==Graph::generalization
?1:0) << "\">" << delimiter
;
857 if (m_attributes
& edgeGraphics
) {
859 const DPolyline
&dpl
= m_bends
[e
];
861 os
<< offset
<< "<PATH TYPE=\"polyline\">" << delimiter
;
862 /* if (m_backward[e])
864 os << (*dpl.rbegin()).m_x << " " << (*dpl.rbegin()).m_y << " ";
866 os << (*dpl.begin()).m_x << " " << (*dpl.begin()).m_y << " ";
871 ListConstIterator
<DPoint
> iter
;
872 for (iter
= dpl
.begin(); iter
.valid(); ++iter
)
873 os
<< offset
<< offset
<< "<POSITION X=\"" << (*iter
).m_x
<< "\" "
874 << "Y=\"" << (*iter
).m_y
<< "\" />" << delimiter
;
875 // if (dpl.size() > 1)
876 // os << "<POSITION X=\"" << (*dpl.rbegin()).m_x << "\" "
877 // << "Y=\"" << (*dpl.rbegin()).m_y << "\" />";
879 os
<< offset
<< "</PATH>" << delimiter
;
884 os
<< "</EDGE>" << delimiter
; // edge
890 void GraphAttributes::addNodeCenter2Bends(int mode
)
893 forall_edges(e
, *m_pGraph
) {
894 node v
= e
->source();
895 node w
= e
->target();
896 DPolyline
&bendpoints
= bends(e
);
898 case 0 : // push center to the bends and return
899 bendpoints
.pushFront(DPoint(x(v
), y(v
)));
900 bendpoints
.pushBack (DPoint(x(w
), y(w
)));
902 case 1 : // determine intersection with node and [center, last-bend-point]
903 bendpoints
.pushFront(DPoint(x(v
), y(v
)));
904 bendpoints
.pushBack (DPoint(x(w
), y(w
)));
905 case 2 : // determine intersection between node and last bend-segment
907 DPoint
sp1(x(v
) - width(v
)/2, y(v
) - height(v
)/2);
908 DPoint
sp2(x(v
) - width(v
)/2, y(v
) + height(v
)/2);
909 DPoint
sp3(x(v
) + width(v
)/2, y(v
) + height(v
)/2);
910 DPoint
sp4(x(v
) + width(v
)/2, y(v
) - height(v
)/2);
911 DLine sourceRect
[4] = {
918 DPoint
tp1(x(w
) - width(w
)/2, y(w
) - height(w
)/2);
919 DPoint
tp2(x(w
) - width(w
)/2, y(w
) + height(w
)/2);
920 DPoint
tp3(x(w
) + width(w
)/2, y(w
) + height(w
)/2);
921 DPoint
tp4(x(w
) + width(w
)/2, y(w
) - height(w
)/2);
922 DLine targetRect
[4] = {
929 DRect
source(sp1
, sp3
);
930 DRect
target(tp1
, tp3
);
932 DPoint c1
= bendpoints
.popFrontRet();
933 DPoint c2
= bendpoints
.popBackRet();
935 while (!bendpoints
.empty() && source
.contains(bendpoints
.front()))
936 c1
= bendpoints
.popFrontRet();
937 while (!bendpoints
.empty() && target
.contains(bendpoints
.back()))
938 c2
= bendpoints
.popBackRet();
942 if (bendpoints
.size() == 0) {
944 for (i
= 0; i
< 4; i
++)
945 if (cross
.intersection(sourceRect
[i
], a1
)) break;
946 for (i
= 0; i
< 4; i
++)
947 if (cross
.intersection(targetRect
[i
], a2
)) break;
950 DLine
cross1(c1
, bendpoints
.front());
951 for (i
= 0; i
< 4; i
++)
952 if (cross1
.intersection(sourceRect
[i
], a1
)) break;
953 DLine
cross2(bendpoints
.back(), c2
);
954 for (i
= 0; i
< 4; i
++)
955 if (cross2
.intersection(targetRect
[i
], a2
)) break;
957 bendpoints
.pushFront(a1
);
958 bendpoints
.pushBack(a2
);
963 bendpoints
.normalize();
967 /* Methods for OGML serialization */
969 // static helper method for mapping edge styles to ogml
970 const char * GraphAttributes::edgeStyleToOGML(const GraphAttributes::EdgeStyle
& edgeStyle
)
973 case GraphAttributes::esNoPen
:
975 case GraphAttributes::esSolid
:
977 case GraphAttributes::esDash
:
979 case GraphAttributes::esDot
:
981 case GraphAttributes::esDashdot
:
983 case GraphAttributes::esDashdotdot
:
984 return "esDashdotdot";
990 // static helper method for mapping image alignments to ogml
991 const char * GraphAttributes::imageAlignmentToOGML(const GraphAttributes::ImageAlignment
&imgAlign
)
994 case GraphAttributes::TopLeft
:
996 case GraphAttributes::TopCenter
:
998 case GraphAttributes::TopRight
:
1000 case GraphAttributes::CenterLeft
:
1001 return "centerLeft";
1002 case GraphAttributes::Center
:
1004 case GraphAttributes::CenterRight
:
1005 return "centerRight";
1006 case GraphAttributes::BottomLeft
:
1007 return "bottomLeft";
1008 case GraphAttributes::BottomCenter
:
1009 return "bottomCenter";
1010 case GraphAttributes::BottomRight
:
1011 return "bottomRight";
1018 // static helper method for mapping image style to ogml
1019 const char * GraphAttributes::imageStyleToOGML(const GraphAttributes::ImageStyle
&imgStyle
)
1022 case GraphAttributes::FreeScale
: return "freeScale";
1023 case GraphAttributes::FixScale
: return "fixScale";
1024 default: return "freeScale";
1029 // static helper method for mapping brush patterns styles to ogml
1030 const char * GraphAttributes::brushPatternToOGML(const GraphAttributes::BrushPattern
& brushPattern
)
1032 switch (brushPattern
) {
1033 case GraphAttributes::bpNone
:
1035 case GraphAttributes::bpSolid
:
1037 case GraphAttributes::bpDense1
:
1039 case GraphAttributes::bpDense2
:
1041 case GraphAttributes::bpDense3
:
1043 case GraphAttributes::bpDense4
:
1045 case GraphAttributes::bpDense5
:
1047 case GraphAttributes::bpDense6
:
1049 case GraphAttributes::bpDense7
:
1051 case GraphAttributes::bpHorizontal
:
1052 return "bpHorizontal";
1053 case GraphAttributes::bpVertical
:
1054 return "bpVertical";
1055 case GraphAttributes::bpCross
:
1057 case GraphAttributes::BackwardDiagonal
:
1058 return "BackwardDiagonal";
1059 case GraphAttributes::ForwardDiagonal
:
1060 return "ForwardDiagonal";
1061 case GraphAttributes::DiagonalCross
:
1062 return "DiagonalCross";
1069 // static helper method for exchanging X(HT)ML-tag specific chars
1070 String
GraphAttributes::formatLabel(const String
& labelText
)
1072 size_t length
= labelText
.length();
1073 String formattedString
;
1075 for (size_t i
= 0; i
< length
; ++i
) {
1076 char c
= labelText
[i
];
1078 formattedString
+= "<";
1081 formattedString
+= ">";
1082 if ((i
+1 < length
) && (labelText
[i
+1] != '\n'))
1083 formattedString
+= '\n';
1085 formattedString
+= c
;
1089 return formattedString
;
1092 void GraphAttributes::writeSVG(const String
&fileName
, int fontSize
, const String
&fontColor
) const
1094 ofstream
os(fileName
.cstr());
1095 writeSVG(os
, fontSize
, fontColor
);
1098 void GraphAttributes::writeSVG(ostream
&os
, int fontSize
, const String
&fontColor
) const
1100 os
.setf(ios::showpoint
);
1103 os
<< "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1104 os
<< "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:ev=\"http://www.w3.org/2001/xml-events\" version=\"1.1\" baseProfile=\"full\" ";
1106 // determine bounding box of svg
1107 OGDF_ASSERT((*m_pGraph
).numberOfNodes() > 0);
1108 double maxX
= x((*m_pGraph
).firstNode());
1109 double maxY
= y((*m_pGraph
).firstNode());
1110 double minX
= x((*m_pGraph
).firstNode());
1111 double minY
= y((*m_pGraph
).firstNode());
1112 double nodeStrokeWidth
;
1115 forall_nodes(v
, *m_pGraph
) {
1116 if (m_attributes
& nodeStyle
) {
1117 nodeStrokeWidth
= lineWidthNode(v
);
1119 nodeStrokeWidth
= 1.0;
1121 maxX
= max(maxX
, x(v
) + m_width
[v
]/2 + nodeStrokeWidth
);
1122 maxY
= max(maxY
, y(v
) + m_height
[v
]/2 + nodeStrokeWidth
);
1123 minX
= min(minX
, x(v
) - m_width
[v
]/2 - nodeStrokeWidth
);
1124 minY
= min(minY
, y(v
) - m_height
[v
]/2 - nodeStrokeWidth
);
1128 ListConstIterator
<DPoint
> it
;
1129 double edgeStrokeWidth
;
1130 forall_edges(e
, *m_pGraph
) {
1131 if (m_attributes
& edgeGraphics
) {
1132 if (attributes() & GraphAttributes::edgeStyle
) {
1133 edgeStrokeWidth
= edgeWidth(e
);
1135 edgeStrokeWidth
= 1.0;
1137 const DPolyline
&dpl
= m_bends
[e
];
1139 for(it
= dpl
.begin(); it
.valid(); ++it
) {
1140 maxX
= max(maxX
, (*it
).m_x
+ edgeStrokeWidth
);
1141 maxY
= max(maxY
, (*it
).m_y
+ edgeStrokeWidth
);
1142 minX
= min(minX
, (*it
).m_x
- edgeStrokeWidth
);
1143 minY
= min(minY
, (*it
).m_y
- edgeStrokeWidth
);
1149 os
<< "width=\"" << (maxX
- minX
) << "px\" ";
1150 os
<< "height=\"" << (maxY
- minY
) << "px\" ";
1151 os
<< "viewBox=\"" << 0 << " " << 0 << " " << (maxX
- minX
) << " " << (maxY
- minY
) << "\">\n";
1153 forall_edges(e
, *m_pGraph
) {
1155 const DPolyline
&dpl
= m_bends
[e
];
1156 if (m_attributes
& edgeGraphics
) {
1157 if (!dpl
.empty()) { //polyline
1158 os
<< "<polyline fill=\"none\" ";
1160 if ((m_attributes
& edgeColor
) && (m_edgeColor
[e
].length() != 0)) {
1161 os
<< "stroke=\"" << m_edgeColor
[e
] << "\" ";
1164 if (attributes() & GraphAttributes::edgeStyle
) {
1165 os
<< "stroke-width=\"" << edgeWidth(e
) << "px\" ";
1167 os
<< "stroke=\"#000000\" ";
1171 node v
= e
->source();
1172 if(dpl
.front().m_x
< m_x
[v
] - m_width
[v
]/2 ||
1173 dpl
.front().m_x
> m_x
[v
] + m_width
[v
]/2 ||
1174 dpl
.front().m_y
< m_y
[v
] - m_height
[v
]/2 ||
1175 dpl
.front().m_y
> m_y
[v
] + m_height
[v
]/2)
1177 os
<< (m_x
[e
->source()] - minX
) << "," << (m_y
[e
->source()] - minY
) << " ";
1180 for(it
= dpl
.begin(); it
.valid(); ++it
)
1181 os
<< ((*it
).m_x
- minX
) << "," << ((*it
).m_y
- minY
) << " ";
1184 if(dpl
.back().m_x
< m_x
[v
] - m_width
[v
]/2 ||
1185 dpl
.back().m_x
> m_x
[v
] + m_width
[v
]/2 ||
1186 dpl
.back().m_y
< m_y
[v
] - m_height
[v
]/2 ||
1187 dpl
.back().m_y
> m_y
[v
] + m_height
[v
]/2)
1189 os
<< (m_x
[e
->target()] - minX
) << "," << (m_y
[e
->target()] - minY
) << " ";
1193 } else { // single line
1195 os
<< "x1=\"" << x(e
->source()) - minX
<< "\" ";
1196 os
<< "y1=\"" << y(e
->source()) - minY
<< "\" ";
1197 os
<< "x2=\"" << x(e
->target()) - minX
<< "\" ";
1198 os
<< "y2=\"" << y(e
->target()) - minY
<< "\" ";
1200 if ((m_attributes
& edgeColor
) && (m_edgeColor
[e
].length() != 0)) {
1201 os
<< "stroke=\"" << m_edgeColor
[e
] << "\" ";
1203 os
<< "stroke=\"#000000\" ";
1206 if (attributes() & GraphAttributes::edgeStyle
) {
1207 os
<< "stroke-width=\"" << edgeWidth(e
) << "px\" ";
1215 forall_nodes(v
,*m_pGraph
) {
1216 if (m_attributes
& nodeGraphics
) {
1217 switch (m_nodeShape
[v
])
1221 os
<< "x=\"" << m_x
[v
] - minX
- m_width
[v
]/2 << "\" ";
1222 os
<< "y=\"" << m_y
[v
] - minY
- m_height
[v
]/2 << "\" ";
1223 os
<< "width=\"" << m_width
[v
] << "\" ";
1224 os
<< "height=\"" << m_height
[v
] << "\" ";
1228 os
<< "cx=\"" << m_x
[v
] - minX
<< "\" ";
1229 os
<< "cy=\"" << m_y
[v
] - minY
<< "\" ";
1230 os
<< "rx=\"" << m_width
[v
]/2 << "\" ";
1231 os
<< "ry=\"" << m_height
[v
]/2 << "\" ";
1235 if (m_attributes
& nodeColor
) {
1236 os
<< "fill=\"" << m_nodeColor
[v
] << "\" ";
1237 os
<< "stroke=\"" << m_nodeLine
[v
] << "\" ";
1240 if (m_attributes
& nodeStyle
)
1242 os
<< "stroke-width=\"" << lineWidthNode(v
) << "px\" ";
1247 if(m_attributes
& nodeLabel
){
1248 os
<< "<text x=\"" << m_x
[v
] - minX
- m_width
[v
]/2 << "\" y=\"" << m_y
[v
] - minY
<< "\" textLength=\"" << m_width
[v
] << "\" font-size=\"" << fontSize
<< "\" fill=\"" << fontColor
<< "\" lengthAdjust=\"spacingAndGlyphs\">" << m_nodeLabel
[v
] << "</text>\n";
1256 } // end namespace ogdf