6 * $Date: 2012-07-07 17:14:54 +0200 (Sa, 07. Jul 2012) $
7 ***************************************************************/
10 * \brief Linear time layout algorithm for trees (TreeLayout)
11 * based on Walker's algorithm
13 * \author Christoph Buchheim
16 * This file is part of the Open Graph Drawing Framework (OGDF).
20 * See README.txt in the root directory of the OGDF installation for details.
23 * This program is free software; you can redistribute it and/or
24 * modify it under the terms of the GNU General Public License
25 * Version 2 or 3 as published by the Free Software Foundation;
26 * see the file LICENSE.txt included in the packaging of this file
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
36 * You should have received a copy of the GNU General Public
37 * License along with this program; if not, write to the Free
38 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
39 * Boston, MA 02110-1301, USA.
41 * \see http://www.gnu.org/copyleft/gpl.html
42 ***************************************************************/
45 #include <ogdf/tree/TreeLayout.h>
46 #include <ogdf/basic/List.h>
47 #include <ogdf/basic/Math.h>
50 #include <ogdf/basic/AdjEntryArray.h>
54 #include <ogdf/basic/simple_graph_alg.h>
55 #include <ogdf/basic/Stack.h>
61 TreeLayout::TreeLayout()
62 :m_siblingDistance(20),
63 m_subtreeDistance(20),
66 m_orthogonalLayout(false),
67 m_orientation(topToBottom
),
68 m_selectRoot(rootIsSource
),
73 TreeLayout::TreeLayout(const TreeLayout
&tl
)
74 :m_siblingDistance(tl
.m_siblingDistance
),
75 m_subtreeDistance(tl
.m_subtreeDistance
),
76 m_levelDistance(tl
.m_levelDistance
),
77 m_treeDistance(tl
.m_treeDistance
),
78 m_orthogonalLayout(tl
.m_orthogonalLayout
),
79 m_orientation(tl
.m_orientation
),
80 m_selectRoot(tl
.m_selectRoot
)
84 TreeLayout::~TreeLayout()
88 TreeLayout
&TreeLayout::operator=(const TreeLayout
&tl
)
90 m_siblingDistance
= tl
.m_siblingDistance
;
91 m_subtreeDistance
= tl
.m_subtreeDistance
;
92 m_levelDistance
= tl
.m_levelDistance
;
93 m_treeDistance
= tl
.m_treeDistance
;
94 m_orthogonalLayout
= tl
.m_orthogonalLayout
;
95 m_orientation
= tl
.m_orientation
;
96 m_selectRoot
= tl
.m_selectRoot
;
101 // comparer class used for sorting adjacency entries according to their angle
102 class TreeLayout::AdjComparer
105 AdjComparer(const AdjEntryArray
<double> &angle
) {
109 int compare(const adjEntry
&adjX
, const adjEntry
&adjY
) const {
110 if ((*m_pAngle
)[adjX
] < (*m_pAngle
)[adjY
])
113 if ((*m_pAngle
)[adjX
] > (*m_pAngle
)[adjY
])
118 OGDF_AUGMENT_COMPARER(adjEntry
)
121 const AdjEntryArray
<double> *m_pAngle
;
126 void TreeLayout::setRoot(GraphAttributes
&AG
, Graph
&tree
)
130 NodeArray
<bool> visited(tree
,false);
136 if(visited
[v
]) continue;
138 // process a new connected component
148 if(m_selectRoot
== rootIsSource
) {
151 } else if (m_selectRoot
== rootIsSink
) {
152 if (x
->outdeg() == 0)
154 } else { // selectByCoordinate
158 } else if(m_selectRoot
== rootByCoord
) {
159 switch(m_orientation
)
162 if(AG
.y(x
) < AG
.y(root
))
166 if(AG
.y(x
) > AG
.y(root
))
170 if(AG
.x(x
) < AG
.x(root
))
174 if(AG
.x(x
) > AG
.x(root
))
182 node w
= adj
->twinNode();
189 undoReverseEdges(AG
);
190 OGDF_THROW_PARAM(PreconditionViolatedException
, pvcForest
);
193 adjustEdgeDirections(tree
,root
,0);
198 void TreeLayout::adjustEdgeDirections(Graph
&G
, node v
, node parent
)
202 node w
= adj
->twinNode();
203 if(w
== parent
) continue;
204 edge e
= adj
->theEdge();
205 if(w
!= e
->target()) {
207 m_reversedEdges
.pushBack(e
);
209 adjustEdgeDirections(G
,w
,v
);
213 void TreeLayout::callSortByPositions(GraphAttributes
&AG
, Graph
&tree
)
215 OGDF_ASSERT(&tree
== &(AG
.constGraph()));
217 if (!isFreeForest(tree
))
218 OGDF_THROW_PARAM(PreconditionViolatedException
, pvcForest
);
222 // stores angle of adjacency entry
223 AdjEntryArray
<double> angle(tree
);
225 AdjComparer
cmp(angle
);
230 // position of node v
238 node w
= adj
->twinNode();
240 // relative position of w to v
241 double dx
= AG
.x(w
) - cx
;
242 double dy
= AG
.y(w
) - cy
;
244 // if v and w lie on the same point ...
245 if (dx
== 0 && dy
== 0) {
250 if(m_orientation
== leftToRight
|| m_orientation
== rightToLeft
)
252 if(m_orientation
== topToBottom
|| m_orientation
== rightToLeft
)
255 // compute angle of adj
256 double alpha
= atan2(fabs(dx
),fabs(dy
));
262 angle
[adj
] = Math::pi
- alpha
;
265 angle
[adj
] = Math::pi
+ alpha
;
267 angle
[adj
] = 2*Math::pi
- alpha
;
271 // get list of all adjacency entries at v
272 SListPure
<adjEntry
> entries
;
273 tree
.adjEntries(v
, entries
);
275 // sort entries according to angle
276 entries
.quicksort(cmp
);
278 // sort entries accordingly in tree
279 tree
.sort(v
,entries
);
282 // adjacency lists are now sorted, so we can apply the usual call
287 void TreeLayout::call(GraphAttributes
&AG
)
289 const Graph
&tree
= AG
.constGraph();
290 if(tree
.numberOfNodes() == 0) return;
293 OGDF_THROW_PARAM(PreconditionViolatedException
, pvcForest
);
295 OGDF_ASSERT(m_siblingDistance
> 0);
296 OGDF_ASSERT(m_subtreeDistance
> 0);
297 OGDF_ASSERT(m_levelDistance
> 0);
299 // compute the tree structure
301 initializeTreeStructure(tree
,roots
);
303 if(m_orientation
== topToBottom
|| m_orientation
== bottomToTop
)
305 ListConstIterator
<node
> it
;
306 double minX
= 0, maxX
= 0;
307 for(it
= roots
.begin(); it
.valid(); ++it
)
311 // compute x-coordinates
312 firstWalk(root
,AG
,true);
313 secondWalkX(root
,-m_preliminary
[root
],AG
);
315 // compute y-coordinates
316 computeYCoordinatesAndEdgeShapes(root
,AG
);
318 if(it
!= roots
.begin())
320 findMinX(AG
,root
,minX
);
322 double shift
= maxX
+ m_treeDistance
- minX
;
324 shiftTreeX(AG
,root
,shift
);
327 findMaxX(AG
,root
,maxX
);
330 // The computed layout draws a tree upwards. If we want to draw the
331 // tree downwards, we simply invert all y-coordinates.
332 if(m_orientation
== topToBottom
)
339 forall_edges(e
,tree
) {
340 ListIterator
<DPoint
> it
;
341 for(it
= AG
.bends(e
).begin(); it
.valid(); ++it
)
342 (*it
).m_y
= -(*it
).m_y
;
347 ListConstIterator
<node
> it
;
348 double minY
= 0, maxY
= 0;
349 for(it
= roots
.begin(); it
.valid(); ++it
)
353 // compute y-coordinates
354 firstWalk(root
,AG
,false);
355 secondWalkY(root
,-m_preliminary
[root
],AG
);
357 // compute y-coordinates
358 computeXCoordinatesAndEdgeShapes(root
,AG
);
360 if(it
!= roots
.begin())
362 findMinY(AG
,root
,minY
);
364 double shift
= maxY
+ m_treeDistance
- minY
;
366 shiftTreeY(AG
,root
,shift
);
369 findMaxY(AG
,root
,maxY
);
372 // The computed layout draws a tree upwards. If we want to draw the
373 // tree downwards, we simply invert all y-coordinates.
374 if(m_orientation
== rightToLeft
)
381 forall_edges(e
,tree
) {
382 ListIterator
<DPoint
> it
;
383 for(it
= AG
.bends(e
).begin(); it
.valid(); ++it
)
384 (*it
).m_x
= -(*it
).m_x
;
390 // delete the tree structure
391 deleteTreeStructure();
393 // restore temporarily removed edges again
394 undoReverseEdges(AG
);
397 void TreeLayout::undoReverseEdges(GraphAttributes
&AG
)
400 while(!m_reversedEdges
.empty()) {
401 edge e
= m_reversedEdges
.popFrontRet();
402 m_pGraph
->reverseEdge(e
);
403 AG
.bends(e
).reverse();
410 void TreeLayout::findMinX(GraphAttributes
&AG
, node root
, double &minX
)
419 double left
= AG
.x(v
) - AG
.width(v
)/2;
420 if(left
< minX
) minX
= left
;
423 forall_adj_edges(e
,v
) {
424 node w
= e
->target();
425 if(w
!= v
) S
.push(w
);
430 void TreeLayout::findMinY(GraphAttributes
&AG
, node root
, double &minY
)
439 double left
= AG
.y(v
) - AG
.height(v
)/2;
440 if(left
< minY
) minY
= left
;
443 forall_adj_edges(e
,v
) {
444 node w
= e
->target();
445 if(w
!= v
) S
.push(w
);
451 void TreeLayout::shiftTreeX(GraphAttributes
&AG
, node root
, double shift
)
462 forall_adj_edges(e
,v
) {
463 node w
= e
->target();
465 ListIterator
<DPoint
> itP
;
466 for(itP
= AG
.bends(e
).begin(); itP
.valid(); ++itP
)
474 void TreeLayout::shiftTreeY(GraphAttributes
&AG
, node root
, double shift
)
485 forall_adj_edges(e
,v
) {
486 node w
= e
->target();
488 ListIterator
<DPoint
> itP
;
489 for(itP
= AG
.bends(e
).begin(); itP
.valid(); ++itP
)
498 void TreeLayout::findMaxX(GraphAttributes
&AG
, node root
, double &maxX
)
506 double right
= AG
.x(v
) + AG
.width(v
)/2;
507 if(right
> maxX
) maxX
= right
;
510 forall_adj_edges(e
,v
) {
511 node w
= e
->target();
512 if(w
!= v
) S
.push(w
);
517 void TreeLayout::findMaxY(GraphAttributes
&AG
, node root
, double &maxY
)
525 double right
= AG
.y(v
) + AG
.height(v
)/2;
526 if(right
> maxY
) maxY
= right
;
529 forall_adj_edges(e
,v
) {
530 node w
= e
->target();
531 if(w
!= v
) S
.push(w
);
537 //node TreeLayout::initializeTreeStructure(const Graph &tree)
538 void TreeLayout::initializeTreeStructure(const Graph
&tree
, List
<node
> &roots
)
542 // initialize node arrays
543 m_number
.init(tree
,0);
544 m_parent
.init(tree
,0);
545 m_leftSibling
.init(tree
,0);
546 m_firstChild
.init(tree
,0);
547 m_lastChild
.init(tree
,0);
548 m_thread
.init(tree
,0);
549 m_ancestor
.init(tree
,0);
550 m_preliminary
.init(tree
,0);
551 m_modifier
.init(tree
,0);
552 m_change
.init(tree
,0);
553 m_shift
.init(tree
,0);
555 // compute the tree structure
559 forall_nodes(v
,tree
) {
565 forall_nodes(v
,tree
) {
568 // - the parent node of v
569 // - the leftmost and rightmost child of v
570 // - the numbers of the children of v
571 // - the left siblings of the children of v
572 // and initialize the actual ancestor of v
576 if(v
->indeg() == 0) { // is v a root
578 m_leftSibling
[v
] = 0;
581 m_firstChild
[v
] = m_lastChild
[v
] = 0;
582 m_parent
[v
] = v
->firstAdj()->theEdge()->source();
587 // traverse the adjacency list of v
588 adjEntry first
; // first leaving edge
589 adjEntry stop
; // successor of last leaving edge
590 first
= v
->firstAdj();
591 if(v
->indeg() == 0) { // is v a root
594 m_leftSibling
[v
] = 0;
598 // search for first leaving edge
599 while(first
->theEdge()->source() == v
)
600 first
= first
->cyclicSucc();
601 m_parent
[v
] = first
->theEdge()->source();
603 first
= first
->cyclicSucc();
606 // traverse the children of v
607 m_firstChild
[v
] = first
->theEdge()->target();
608 m_number
[m_firstChild
[v
]] = childCounter
= 0;
609 m_leftSibling
[m_firstChild
[v
]] = 0;
610 adjEntry previous
= first
;
611 while(first
->cyclicSucc() != stop
) {
612 first
= first
->cyclicSucc();
613 m_number
[first
->theEdge()->target()] = ++childCounter
;
614 m_leftSibling
[first
->theEdge()->target()]
615 = previous
->theEdge()->target();
618 m_lastChild
[v
] = first
->theEdge()->target();
624 void TreeLayout::deleteTreeStructure()
628 m_leftSibling
.init();
629 m_firstChild
.init();
633 m_preliminary
.init();
640 int TreeLayout::isLeaf(node v
) const
644 // node v is a leaf if and only if no edge leaves v
645 return v
->outdeg() == 0;
649 node
TreeLayout::nextOnLeftContour(node v
) const
652 OGDF_ASSERT(v
->graphOf() == m_firstChild
.graphOf());
653 OGDF_ASSERT(v
->graphOf() == m_thread
.graphOf());
655 // if v has children, the successor of v on the left contour
656 // is its leftmost child,
657 // otherwise, the successor is the thread of v (may be 0)
658 if(m_firstChild
[v
] != 0)
659 return m_firstChild
[v
];
665 node
TreeLayout::nextOnRightContour(node v
) const
668 OGDF_ASSERT(v
->graphOf() == m_lastChild
.graphOf());
669 OGDF_ASSERT(v
->graphOf() == m_thread
.graphOf());
671 // if v has children, the successor of v on the right contour
672 // is its rightmost child,
673 // otherwise, the successor is the thread of v (may be 0)
674 if(m_lastChild
[v
] != 0)
675 return m_lastChild
[v
];
681 void TreeLayout::firstWalk(node subtree
,const GraphAttributes
&AG
,bool upDown
)
683 OGDF_ASSERT(subtree
!= 0);
684 OGDF_ASSERT(subtree
->graphOf() == m_leftSibling
.graphOf());
685 OGDF_ASSERT(subtree
->graphOf() == m_preliminary
.graphOf());
686 OGDF_ASSERT(subtree
->graphOf() == m_firstChild
.graphOf());
687 OGDF_ASSERT(subtree
->graphOf() == m_lastChild
.graphOf());
688 OGDF_ASSERT(subtree
->graphOf() == m_modifier
.graphOf());
689 OGDF_ASSERT(subtree
->graphOf() == m_change
.graphOf());
690 OGDF_ASSERT(subtree
->graphOf() == m_shift
.graphOf());
692 // compute a preliminary x-coordinate for subtree
693 if(isLeaf(subtree
)) {
695 // place subtree close to the left sibling
696 node leftSibling
= m_leftSibling
[subtree
];
697 if(leftSibling
!= 0) {
699 m_preliminary
[subtree
] = m_preliminary
[leftSibling
]
700 + (AG
.width(subtree
) + AG
.width(leftSibling
)) / 2
703 m_preliminary
[subtree
] = m_preliminary
[leftSibling
]
704 + (AG
.height(subtree
) + AG
.height(leftSibling
)) / 2
708 else m_preliminary
[subtree
] = 0;
711 node defaultAncestor
= m_firstChild
[subtree
];
713 // collect the children of subtree
715 node v
= m_lastChild
[subtree
];
717 children
.pushFront(v
);
718 v
= m_leftSibling
[v
];
721 ListIterator
<node
> it
;
723 // apply firstwalk and apportion to the children
724 for(it
= children
.begin(); it
.valid(); it
= it
.succ()) {
725 firstWalk(*it
,AG
,upDown
);
726 apportion(*it
,defaultAncestor
,AG
,upDown
);
729 // shift the small subtrees
733 for(it
= children
.begin(); it
.valid(); it
= it
.succ()) {
734 m_preliminary
[*it
] += shift
;
735 m_modifier
[*it
] += shift
;
736 change
+= m_change
[*it
];
737 shift
+= m_shift
[*it
] + change
;
740 // place the parent node
741 double midpoint
= (m_preliminary
[children
.front()] + m_preliminary
[children
.back()]) / 2;
742 node leftSibling
= m_leftSibling
[subtree
];
743 if(leftSibling
!= 0) {
745 m_preliminary
[subtree
] = m_preliminary
[leftSibling
]
746 + (AG
.width(subtree
) + AG
.width(leftSibling
)) / 2
749 m_preliminary
[subtree
] = m_preliminary
[leftSibling
]
750 + (AG
.height(subtree
) + AG
.height(leftSibling
)) / 2
753 m_modifier
[subtree
] =
754 m_preliminary
[subtree
] - midpoint
;
756 else m_preliminary
[subtree
] = midpoint
;
760 void TreeLayout::apportion(
762 node
&defaultAncestor
,
763 const GraphAttributes
&AG
,
766 OGDF_ASSERT(subtree
!= 0);
767 OGDF_ASSERT(subtree
->graphOf() == defaultAncestor
->graphOf());
768 OGDF_ASSERT(subtree
->graphOf() == m_leftSibling
.graphOf());
769 OGDF_ASSERT(subtree
->graphOf() == m_firstChild
.graphOf());
770 OGDF_ASSERT(subtree
->graphOf() == m_modifier
.graphOf());
771 OGDF_ASSERT(subtree
->graphOf() == m_ancestor
.graphOf());
772 OGDF_ASSERT(subtree
->graphOf() == m_change
.graphOf());
773 OGDF_ASSERT(subtree
->graphOf() == m_shift
.graphOf());
774 OGDF_ASSERT(subtree
->graphOf() == m_thread
.graphOf());
776 if(m_leftSibling
[subtree
] == 0) return;
778 // check distance to the left of the subtree
779 // and traverse left/right inside/outside contour
781 double leftModSumOut
= 0; // sum of modifiers on left outside contour
782 double leftModSumIn
= 0; // sum of modifiers on left inside contour
783 double rightModSumIn
= 0; // sum of modifiers on right inside contour
784 double rightModSumOut
= 0; // sum of modifiers on right outside contour
787 int numberOfSubtrees
;
788 node leftAncestor
,rightAncestor
;
790 // start the traversal at the actual level
791 node leftContourOut
= m_firstChild
[m_parent
[subtree
]];
792 node leftContourIn
= m_leftSibling
[subtree
];
793 node rightContourIn
= subtree
;
794 node rightContourOut
= subtree
;
799 leftModSumOut
+= m_modifier
[leftContourOut
];
800 leftModSumIn
+= m_modifier
[leftContourIn
];
801 rightModSumIn
+= m_modifier
[rightContourIn
];
802 rightModSumOut
+= m_modifier
[rightContourOut
];
804 // actualize ancestor for right contour
805 m_ancestor
[rightContourOut
] = subtree
;
807 if(nextOnLeftContour(leftContourOut
) != 0 && nextOnRightContour(rightContourOut
) != 0)
809 // continue traversal
810 leftContourOut
= nextOnLeftContour(leftContourOut
);
811 leftContourIn
= nextOnRightContour(leftContourIn
);
812 rightContourIn
= nextOnLeftContour(rightContourIn
);
813 rightContourOut
= nextOnRightContour(rightContourOut
);
815 // check if subtree has to be moved
817 moveDistance
= m_preliminary
[leftContourIn
] + leftModSumIn
818 + (AG
.width(leftContourIn
) + AG
.width(rightContourIn
)) / 2
820 - m_preliminary
[rightContourIn
] - rightModSumIn
;
822 moveDistance
= m_preliminary
[leftContourIn
] + leftModSumIn
823 + (AG
.height(leftContourIn
) + AG
.height(rightContourIn
)) / 2
825 - m_preliminary
[rightContourIn
] - rightModSumIn
;
827 if(moveDistance
> 0) {
829 // compute highest different ancestors of leftContourIn
830 // and rightContourIn
831 if(m_parent
[m_ancestor
[leftContourIn
]] == m_parent
[subtree
])
832 leftAncestor
= m_ancestor
[leftContourIn
];
833 else leftAncestor
= defaultAncestor
;
834 rightAncestor
= subtree
;
836 // compute the number of small subtrees in between (plus 1)
838 m_number
[rightAncestor
] - m_number
[leftAncestor
];
840 // compute the shifts and changes of shift
841 m_change
[rightAncestor
] -= moveDistance
/ numberOfSubtrees
;
842 m_shift
[rightAncestor
] += moveDistance
;
843 m_change
[leftAncestor
] += moveDistance
/ numberOfSubtrees
;
845 // move subtree to the right by moveDistance
846 m_preliminary
[rightAncestor
] += moveDistance
;
847 m_modifier
[rightAncestor
] += moveDistance
;
848 rightModSumIn
+= moveDistance
;
849 rightModSumOut
+= moveDistance
;
856 if(nextOnRightContour(rightContourOut
) == 0 && nextOnRightContour(leftContourIn
) != 0)
858 // right subtree smaller than left subforest
859 m_thread
[rightContourOut
] = nextOnRightContour(leftContourIn
);
860 m_modifier
[rightContourOut
] += leftModSumIn
- rightModSumOut
;
863 if(nextOnLeftContour(leftContourOut
) == 0 && nextOnLeftContour(rightContourIn
) != 0)
865 // left subforest smaller than right subtree
866 m_thread
[leftContourOut
] = nextOnLeftContour(rightContourIn
);
867 m_modifier
[leftContourOut
] += rightModSumIn
- leftModSumOut
;
868 defaultAncestor
= subtree
;
873 void TreeLayout::secondWalkX(node subtree
,
877 OGDF_ASSERT(subtree
!= 0);
878 OGDF_ASSERT(subtree
->graphOf() == m_preliminary
.graphOf());
879 OGDF_ASSERT(subtree
->graphOf() == m_modifier
.graphOf());
881 // compute final x-coordinates for the subtree
882 // by recursively aggregating modifiers
883 AG
.x(subtree
) = m_preliminary
[subtree
] + modifierSum
;
884 modifierSum
+= m_modifier
[subtree
];
886 forall_adj_edges(e
,subtree
) if(e
->target() != subtree
)
887 secondWalkX(e
->target(),modifierSum
,AG
);
890 void TreeLayout::secondWalkY(node subtree
,
894 OGDF_ASSERT(subtree
!= 0);
895 OGDF_ASSERT(subtree
->graphOf() == m_preliminary
.graphOf());
896 OGDF_ASSERT(subtree
->graphOf() == m_modifier
.graphOf());
898 // compute final y-coordinates for the subtree
899 // by recursively aggregating modifiers
900 AG
.y(subtree
) = m_preliminary
[subtree
] + modifierSum
;
901 modifierSum
+= m_modifier
[subtree
];
903 forall_adj_edges(e
,subtree
) if(e
->target() != subtree
)
904 secondWalkY(e
->target(),modifierSum
,AG
);
908 void TreeLayout::computeYCoordinatesAndEdgeShapes(node root
, GraphAttributes
&AG
)
910 OGDF_ASSERT(root
!= 0);
912 // compute y-coordinates and edge shapes
915 List
<node
> oldLevel
; // the nodes of the old level
916 List
<node
> newLevel
; // the nodes of the new level
917 ListIterator
<node
> it
;
918 double yCoordinate
; // the y-coordinate for the new level
919 double edgeCoordinate
; // the y-coordinate for edge bends
920 double oldHeight
; // the maximal node height on the old level
921 double newHeight
; // the maximal node height on the new level
923 // traverse the tree level by level
924 newLevel
.pushBack(root
);
925 AG
.y(root
) = yCoordinate
= 0;
926 newHeight
= AG
.height(root
);
927 while(!newLevel
.empty()) {
928 oldHeight
= newHeight
;
930 oldLevel
.conc(newLevel
);
931 while(!oldLevel
.empty()) {
932 v
= oldLevel
.popFrontRet();
933 forall_adj_edges(e
,v
) if(e
->target() != v
) {
935 newLevel
.pushBack(w
);
937 // compute the shape of edge e
938 DPolyline
&edgeBends
= AG
.bends(e
);
940 if(m_orthogonalLayout
) {
942 yCoordinate
+ (oldHeight
+ m_levelDistance
) / 2;
943 edgeBends
.pushBack(DPoint(AG
.x(v
),edgeCoordinate
));
944 edgeBends
.pushBack(DPoint(AG
.x(w
),edgeCoordinate
));
947 // compute the maximal node height on the new level
948 if(AG
.height(e
->target()) > newHeight
)
949 newHeight
= AG
.height(e
->target());
953 // assign y-coordinate to the nodes of the new level
954 yCoordinate
+= (oldHeight
+ newHeight
) / 2 + m_levelDistance
;
955 for(it
= newLevel
.begin(); it
.valid(); it
= it
.succ())
956 AG
.y(*it
) = yCoordinate
;
960 void TreeLayout::computeXCoordinatesAndEdgeShapes(node root
, GraphAttributes
&AG
)
962 OGDF_ASSERT(root
!= 0);
964 // compute y-coordinates and edge shapes
967 List
<node
> oldLevel
; // the nodes of the old level
968 List
<node
> newLevel
; // the nodes of the new level
969 ListIterator
<node
> it
;
970 double xCoordinate
; // the x-coordinate for the new level
971 double edgeCoordinate
; // the x-coordinate for edge bends
972 double oldWidth
; // the maximal node width on the old level
973 double newWidth
; // the maximal node width on the new level
975 // traverse the tree level by level
976 newLevel
.pushBack(root
);
977 AG
.x(root
) = xCoordinate
= 0;
978 newWidth
= AG
.width(root
);
979 while(!newLevel
.empty()) {
982 oldLevel
.conc(newLevel
);
983 while(!oldLevel
.empty()) {
984 v
= oldLevel
.popFrontRet();
985 forall_adj_edges(e
,v
) if(e
->target() != v
) {
987 newLevel
.pushBack(w
);
989 // compute the shape of edge e
990 DPolyline
&edgeBends
= AG
.bends(e
);
992 if(m_orthogonalLayout
) {
994 xCoordinate
+ (oldWidth
+ m_levelDistance
) / 2;
995 edgeBends
.pushBack(DPoint(edgeCoordinate
,AG
.y(v
)));
996 edgeBends
.pushBack(DPoint(edgeCoordinate
,AG
.y(w
)));
999 // compute the maximal node width on the new level
1000 if(AG
.width(e
->target()) > newWidth
)
1001 newWidth
= AG
.width(e
->target());
1005 // assign x-coordinate to the nodes of the new level
1006 xCoordinate
+= (oldWidth
+ newWidth
) / 2 + m_levelDistance
;
1007 for(it
= newLevel
.begin(); it
.valid(); it
= it
.succ())
1008 AG
.x(*it
) = xCoordinate
;
1013 } // end namespace ogdf