我的目的是想画一个结构紧凑、没有冲突、节点分布均匀的树。例如
pymag-trees是一种画树算法的python实现。 这是这种算法的介绍和具体分析。
OGDF
OGDF,Open Graph Drawing Framework,是一个强大的,独立的c++类库,用于图表的自动布局。她不能直接画图但可以输出svg等格式的文件。
代码
用于布局的代码是我从ogdf中“截取的”,再加上一个树结构保存布局完成后的节点坐标及其它信息。得到坐标后就可以用各种gui框架画出来了,例如qt。
树结构
TreeChart
TreeChart.h
#pragma once
#include <boost/format.hpp>
#include "tree.hh"
class STreeChart
{
struct ProcessInfo
{
std::string strName;
int nProcessId;
int nParentId;
double x;
double y;
double mod;
};
//! Determines the orientation in hierarchical layouts.
enum class Orientation {
topToBottom, //!< Edges are oriented from top to bottom.
bottomToTop, //!< Edges are oriented from bottom to top.
leftToRight, //!< Edges are oriented from left to right.
rightToLeft //!< Edges are oriented from right to left.
};
//! Determines how to select the root of the tree.
enum class RootSelectionType {
Source, //!< Select a source in the graph.
Sink, //!< Select a sink in the graph.
ByCoord //!< Use the coordinates, e.g., select the topmost node if orientation is topToBottom.
};
public:
STreeChart():m_siblingDistance(20),
m_subtreeDistance(30),
m_levelDistance(150),
m_treeDistance(50),
m_orthogonalLayout(false),
m_orientation(Orientation::leftToRight)
{
m_nLeft = m_nRight = m_nBottom = m_nTop = 8;
m_bDown = false;
xMove = 0;
yMove = 0;
ProcessInfo info;
info.strName = "root";
root = m_Tree.set_head(info);
//test graph
for (int i = 0; i < 3; i++)
{
info.strName = str(boost::format("%d.exe") % i);
tree<ProcessInfo>::iterator node = m_Tree.append_child(root, info);
if (i == 1)
{
continue;
}
if (i == 5)
{
for (int j = 0; j < 1; j++)
{
info.strName = str(boost::format("%d-%d.exe") % i % j);
m_Tree.append_child(node, info);
}
continue;
}
for (int j = 0; j < 2; j++)
{
info.strName = str(boost::format("%d-%d.exe") % i % j);
tree<ProcessInfo>::iterator nodeNext = m_Tree.append_child(node, info);
/*if (j == 1)
{
info.strName = "xxxx.exe";
m_Tree.append_child(nodeNext, info);
}*/
}
}
}
~STreeChart()
{
}
void call();
private:
struct TreeStructure;
double m_siblingDistance; //!< The minimal distance between siblings.
double m_subtreeDistance; //!< The minimal distance between subtrees.
double m_levelDistance; //!< The minimal distance between levels.
double m_treeDistance; //!< The minimal distance between trees.
bool m_orthogonalLayout; //!< Option for orthogonal style (yes/no).
Orientation m_orientation; //!< Option for orientation of tree layout.
RootSelectionType m_selectRoot; //!< Option for how to determine the root.
void firstWalk(TreeStructure &ts, tree<ProcessInfo>::iterator_base &subtree, bool upDown);
void apportion(TreeStructure &ts, tree<ProcessInfo>::iterator_base &subtree, tree<ProcessInfo>::iterator_base &defaultAncestor, bool upDown);
void secondWalkX(TreeStructure &ts, tree<ProcessInfo>::iterator_base &subtree, double modifierSum);
void secondWalkY(TreeStructure &ts, tree<ProcessInfo>::iterator_base &subtree, double modifierSum);
// compute y-coordinates and edge shapes
void computeYCoordinates();
void computeXCoordinates();
int m_nLeft;
int m_nTop;
int m_nRight;
int m_nBottom;
tree<ProcessInfo> m_Tree;
tree<ProcessInfo>::iterator root;
bool m_bDown;
int xMove;
int yMove;
};
TreeChart.cpp
#include "pch.h"
#include "TreeChart.h"
#include <map>
struct STreeChart::TreeStructure {
//GraphAttributes &m_ga;
std::map<tree<ProcessInfo>::iterator_base, int, tree<ProcessInfo>::iterator_base_less> m_number; //!< Consecutive numbers for children.
std::map<tree<ProcessInfo>::iterator_base, tree<ProcessInfo>::iterator_base, tree<ProcessInfo>::iterator_base_less> m_parent; //!< Parent node, 0 if root.
std::map<tree<ProcessInfo>::iterator_base, tree<ProcessInfo>::iterator_base, tree<ProcessInfo>::iterator_base_less> m_leftSibling; //!< Left sibling, 0 if none.
std::map<tree<ProcessInfo>::iterator_base, tree<ProcessInfo>::iterator_base, tree<ProcessInfo>::iterator_base_less> m_firstChild; //!< Leftmost child, 0 if leaf.
std::map<tree<ProcessInfo>::iterator_base, tree<ProcessInfo>::iterator_base, tree<ProcessInfo>::iterator_base_less> m_lastChild; //!< Rightmost child, 0 if leaf.
std::map<tree<ProcessInfo>::iterator_base, tree<ProcessInfo>::iterator_base, tree<ProcessInfo>::iterator_base_less> m_thread; //!< Thread, 0 if none.
std::map<tree<ProcessInfo>::iterator_base, tree<ProcessInfo>::iterator_base, tree<ProcessInfo>::iterator_base_less> m_ancestor; //!< Actual highest ancestor.
std::map<tree<ProcessInfo>::iterator_base, double, tree<ProcessInfo>::iterator_base_less> m_preliminary; //!< Preliminary x-coordinates.
std::map<tree<ProcessInfo>::iterator_base, double, tree<ProcessInfo>::iterator_base_less> m_modifier; //!< Modifier of x-coordinates.
std::map<tree<ProcessInfo>::iterator_base, double, tree<ProcessInfo>::iterator_base_less> m_change; //!< Change of shift applied to subtrees.
std::map<tree<ProcessInfo>::iterator_base, double, tree<ProcessInfo>::iterator_base_less> m_shift; //!< Shift applied to subtrees.
// initialize all node arrays and
// compute the tree structure from the adjacency lists
//
// returns the root nodes in roots
TreeStructure(tree<ProcessInfo> &m_Tree) //:
//m_ga(GA),
//m_number(tree, 0),
//m_parent(tree, nullptr),
//m_leftSibling(tree, nullptr),
//m_firstChild(tree, nullptr),
//m_lastChild(tree, nullptr),
//m_thread(tree, nullptr),
//m_ancestor(tree, nullptr),
//m_preliminary(tree, 0),
//m_modifier(tree, 0),
//m_change(tree, 0),
//m_shift(tree, 0)
{
//init data
tree<ProcessInfo>::iterator poit;
for (poit = m_Tree.begin(); poit != m_Tree.end(); ++poit) {
m_number.insert(std::make_pair(poit, 0));
m_parent.insert(std::make_pair(poit, tree<ProcessInfo>::iterator_base()));
m_leftSibling.insert(std::make_pair(poit, tree<ProcessInfo>::iterator_base()));
m_firstChild.insert(std::make_pair(poit, tree<ProcessInfo>::iterator_base()));
m_lastChild.insert(std::make_pair(poit, tree<ProcessInfo>::iterator_base()));
m_thread.insert(std::make_pair(poit, tree<ProcessInfo>::iterator_base()));
m_ancestor.insert(std::make_pair(poit, tree<ProcessInfo>::iterator_base()));
m_preliminary.insert(std::make_pair(poit, 0));
m_modifier.insert(std::make_pair(poit, 0));
m_change.insert(std::make_pair(poit, 0));
m_shift.insert(std::make_pair(poit, 0));
}
// compute the tree structure
int childCounter;
tree<ProcessInfo>::iterator v;
for (v = m_Tree.begin(); v != m_Tree.end(); ++v) {
// determine
// - the parent node of v
// - the leftmost and rightmost child of v
// - the numbers of the children of v
// - the left siblings of the children of v
// and initialize the actual ancestor of v
m_ancestor[v] = v;
if (isLeaf(v)) {
if (!m_Tree.is_valid(m_Tree.parent(v))) { // is v a root
m_parent[v] = tree<ProcessInfo>::iterator_base();
m_leftSibling[v] = tree<ProcessInfo>::iterator_base();
}
else {
m_firstChild[v] = m_lastChild[v] = tree<ProcessInfo>::iterator_base();
m_parent[v] = m_Tree.parent(v);
}
}
else {
// traverse the adjacency list of v
if (!m_Tree.is_valid(m_Tree.parent(v))) { // is v a root
m_parent[v] = tree<ProcessInfo>::iterator_base();
m_leftSibling[v] = tree<ProcessInfo>::iterator_base();
}
else {
// search for first leaving edge
//while (first->theEdge()->source() == v)
//first = first->cyclicSucc();
m_parent[v] = m_Tree.parent(v);
//stop = first;
//first = first->cyclicSucc();
}
// traverse the children of v
tree<ProcessInfo>::sibling_iterator sit, psit;
m_firstChild[v] = sit = psit = v.begin();
m_number[m_firstChild[v]] = childCounter = 0;
m_leftSibling[m_firstChild[v]] = tree<ProcessInfo>::iterator_base();
for (++sit; sit != v.end(); ++sit, ++psit) {
m_number[sit] = ++childCounter;
m_leftSibling[sit] = psit;
}
m_lastChild[v] = psit;
}
}
}
// returns whether node v is a leaf
bool isLeaf(tree<ProcessInfo>::iterator_base &v) const
{
//OGDF_ASSERT(v != nullptr);
// node v is a leaf if and only if no edge leaves v
return tree<ProcessInfo>::number_of_children(v) == 0;
}
// returns the successor of node v on the left contour
// returns 0 if there is none
tree<ProcessInfo>::iterator_base nextOnLeftContour(tree<ProcessInfo> &m_Tree, tree<ProcessInfo>::iterator_base &v)
{
//OGDF_ASSERT(v != nullptr);
//OGDF_ASSERT(v->graphOf() == m_firstChild.graphOf());
//OGDF_ASSERT(v->graphOf() == m_thread.graphOf());
// if v has children, the successor of v on the left contour
// is its leftmost child,
// otherwise, the successor is the thread of v (may be 0)
tree<ProcessInfo>::iterator it;
if (m_Tree.is_valid(m_firstChild[v]))
return m_firstChild[v];
else
return m_thread[v];
}
// returns the successor of node v on the right contour
// returns 0 if there is none
tree<ProcessInfo>::iterator_base nextOnRightContour(tree<ProcessInfo> &m_Tree, tree<ProcessInfo>::iterator_base &v)
{
//OGDF_ASSERT(v != nullptr);
//OGDF_ASSERT(v->graphOf() == m_lastChild.graphOf());
//OGDF_ASSERT(v->graphOf() == m_thread.graphOf());
// if v has children, the successor of v on the right contour
// is its rightmost child,
// otherwise, the successor is the thread of v (may be 0)
if (m_Tree.is_valid(m_lastChild[v]))
return m_lastChild[v];
else
return m_thread[v];
}
};
void STreeChart::apportion(TreeStructure &ts, tree<ProcessInfo>::iterator_base &subtree, tree<ProcessInfo>::iterator_base &defaultAncestor, bool upDown)
{
//OGDF_ASSERT(subtree != nullptr);
//OGDF_ASSERT(subtree->graphOf() == defaultAncestor->graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_leftSibling.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_firstChild.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_modifier.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_ancestor.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_change.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_shift.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_thread.graphOf());
if (!m_Tree.is_valid(ts.m_leftSibling[subtree])) return;
// check distance to the left of the subtree
// and traverse left/right inside/outside contour
double leftModSumOut = 0; // sum of modifiers on left outside contour
double leftModSumIn = 0; // sum of modifiers on left inside contour
double rightModSumIn = 0; // sum of modifiers on right inside contour
double rightModSumOut = 0; // sum of modifiers on right outside contour
double moveDistance;
int numberOfSubtrees;
tree<ProcessInfo>::iterator_base leftAncestor, rightAncestor;
// start the traversal at the actual level
tree<ProcessInfo>::iterator_base leftContourOut = ts.m_firstChild[ts.m_parent[subtree]];
tree<ProcessInfo>::iterator_base leftContourIn = ts.m_leftSibling[subtree];
tree<ProcessInfo>::iterator_base rightContourIn = subtree;
tree<ProcessInfo>::iterator_base rightContourOut = subtree;
bool stop = false;
do {
// add modifiers
leftModSumOut += ts.m_modifier[leftContourOut];
leftModSumIn += ts.m_modifier[leftContourIn];
rightModSumIn += ts.m_modifier[rightContourIn];
rightModSumOut += ts.m_modifier[rightContourOut];
// actualize ancestor for right contour
ts.m_ancestor[rightContourOut] = subtree;
if (m_Tree.is_valid(ts.nextOnLeftContour(m_Tree, leftContourOut)) && m_Tree.is_valid(ts.nextOnRightContour(m_Tree, rightContourOut)))
{
// continue traversal
leftContourOut = ts.nextOnLeftContour(m_Tree, leftContourOut);
leftContourIn = ts.nextOnRightContour(m_Tree, leftContourIn);
rightContourIn = ts.nextOnLeftContour(m_Tree, rightContourIn);
rightContourOut = ts.nextOnRightContour(m_Tree, rightContourOut);
// check if subtree has to be moved
if (upDown) {
moveDistance = ts.m_preliminary[leftContourIn] + leftModSumIn
//+ (ts.m_ga.width(leftContourIn) + ts.m_ga.width(rightContourIn)) / 2
+ 10 //node size;
+ m_subtreeDistance
- ts.m_preliminary[rightContourIn] - rightModSumIn;
}
else {
moveDistance = ts.m_preliminary[leftContourIn] + leftModSumIn
//+ (ts.m_ga.height(leftContourIn) + ts.m_ga.height(rightContourIn)) / 2
+ 10 //node size
+ m_subtreeDistance
- ts.m_preliminary[rightContourIn] - rightModSumIn;
}
if (moveDistance > 0) {
// compute highest different ancestors of leftContourIn
// and rightContourIn
tree<ProcessInfo>::iterator tmp1(ts.m_parent[ts.m_ancestor[leftContourIn]]);
tree<ProcessInfo>::iterator tmp2(ts.m_parent[subtree]);
if (tmp1 == tmp2)
leftAncestor = ts.m_ancestor[leftContourIn];
else leftAncestor = defaultAncestor;
rightAncestor = subtree;
// compute the number of small subtrees in between (plus 1)
numberOfSubtrees =
ts.m_number[rightAncestor] - ts.m_number[leftAncestor];
// compute the shifts and changes of shift
ts.m_change[rightAncestor] -= moveDistance / numberOfSubtrees;
ts.m_shift[rightAncestor] += moveDistance;
ts.m_change[leftAncestor] += moveDistance / numberOfSubtrees;
// move subtree to the right by moveDistance
ts.m_preliminary[rightAncestor] += moveDistance;
ts.m_modifier[rightAncestor] += moveDistance;
rightModSumIn += moveDistance;
rightModSumOut += moveDistance;
}
}
else stop = true;
} while (!stop);
// adjust threads
if (!m_Tree.is_valid(ts.nextOnRightContour(m_Tree, rightContourOut)) && m_Tree.is_valid(ts.nextOnRightContour(m_Tree, leftContourIn)))
{
// right subtree smaller than left subforest
ts.m_thread[rightContourOut] = ts.nextOnRightContour(m_Tree, leftContourIn);
ts.m_modifier[rightContourOut] += leftModSumIn - rightModSumOut;
}
if (!m_Tree.is_valid(ts.nextOnLeftContour(m_Tree, leftContourOut))&& m_Tree.is_valid(ts.nextOnLeftContour(m_Tree, rightContourIn)))
{
// left subforest smaller than right subtree
ts.m_thread[leftContourOut] = ts.nextOnLeftContour(m_Tree, rightContourIn);
ts.m_modifier[leftContourOut] += rightModSumIn - leftModSumOut;
defaultAncestor = subtree;
}
}
void STreeChart::firstWalk(TreeStructure &ts, tree<ProcessInfo>::iterator_base &subtree, bool upDown)
{
//OGDF_ASSERT(subtree != nullptr);
//OGDF_ASSERT(subtree->graphOf() == ts.m_leftSibling.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_preliminary.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_firstChild.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_lastChild.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_modifier.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_change.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_shift.graphOf());
// compute a preliminary x-coordinate for subtree
if (ts.isLeaf(subtree)) {
// place subtree close to the left sibling
tree<ProcessInfo>::iterator_base leftSibling = ts.m_leftSibling[subtree];
if (m_Tree.is_valid(leftSibling)) {
if (upDown) {
ts.m_preliminary[subtree] = ts.m_preliminary[leftSibling]
//+ (ts.m_ga.width(subtree) + ts.m_ga.width(leftSibling)) / 2
+ 10 //node size
+ m_siblingDistance;
}
else {
ts.m_preliminary[subtree] = ts.m_preliminary[leftSibling]
//+ (ts.m_ga.height(subtree) + ts.m_ga.height(leftSibling)) / 2
+ 10 //node size
+ m_siblingDistance;
}
}
else ts.m_preliminary[subtree] = 0;
}
else {
tree<ProcessInfo>::iterator_base defaultAncestor = ts.m_firstChild[subtree];
// apply firstwalk and apportion to the children
tree<ProcessInfo>::sibling_iterator sit;
for (sit = subtree.begin(); sit != subtree.end(); ++sit) {
firstWalk(ts, sit, upDown);
apportion(ts, sit, defaultAncestor, upDown);
}
// shift the small subtrees
double shift = 0;
double change = 0;
sit = subtree.end();
do {
--sit;
ts.m_preliminary[sit] += shift;
ts.m_modifier[sit] += shift;
change += ts.m_change[sit];
shift += ts.m_shift[sit] + change;
printf("%s temp change: %f, shift: %f\n", sit->strName.c_str(), ts.m_change[sit], ts.m_shift[sit]);
printf("%s temp modifier: %f\n", sit->strName.c_str(), ts.m_modifier[sit]);
} while (sit != subtree.begin());
// place the parent node
sit = subtree.end();
--sit;
double midpoint = (ts.m_preliminary[subtree.begin()] + ts.m_preliminary[sit]) / 2;
tree<ProcessInfo>::iterator_base leftSibling = ts.m_leftSibling[subtree];
if (m_Tree.is_valid(leftSibling)) {
if (upDown) {
ts.m_preliminary[subtree] = ts.m_preliminary[leftSibling]
//+ (ts.m_ga.width(subtree) + ts.m_ga.width(leftSibling)) / 2
+ 10 //node size
+ m_siblingDistance;
}
else {
ts.m_preliminary[subtree] = ts.m_preliminary[leftSibling]
//+ (ts.m_ga.height(subtree) + ts.m_ga.height(leftSibling)) / 2
+ 10 //node size
+ m_siblingDistance;
}
ts.m_modifier[subtree] =
ts.m_preliminary[subtree] - midpoint;
}
else ts.m_preliminary[subtree] = midpoint;
}
printf("%s\n", subtree->strName.c_str());
printf("preliminary: %f\n", ts.m_preliminary[subtree]);
printf("modifier: %f\n", ts.m_modifier[subtree]);
}
void STreeChart::secondWalkX(TreeStructure &ts, tree<ProcessInfo>::iterator_base &subtree, double modifierSum)
{
//OGDF_ASSERT(subtree != nullptr);
//OGDF_ASSERT(subtree->graphOf() == ts.m_preliminary.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_modifier.graphOf());
// compute final x-coordinates for the subtree
// by recursively aggregating modifiers
subtree->x = ts.m_preliminary[subtree] + modifierSum;
modifierSum += ts.m_modifier[subtree];
tree<ProcessInfo>::sibling_iterator sit;
for (sit = subtree.begin(); sit != subtree.end(); ++sit) {
secondWalkX(ts, sit, modifierSum);
}
}
void STreeChart::secondWalkY(TreeStructure &ts, tree<ProcessInfo>::iterator_base &subtree, double modifierSum)
{
//OGDF_ASSERT(subtree != nullptr);
//OGDF_ASSERT(subtree->graphOf() == ts.m_preliminary.graphOf());
//OGDF_ASSERT(subtree->graphOf() == ts.m_modifier.graphOf());
// compute final y-coordinates for the subtree
// by recursively aggregating modifiers
subtree->y = ts.m_preliminary[subtree] + modifierSum;
modifierSum += ts.m_modifier[subtree];
tree<ProcessInfo>::sibling_iterator sit;
for (sit = subtree.begin(); sit != subtree.end(); ++sit) {
secondWalkY(ts, sit, modifierSum);
}
}
void STreeChart::computeYCoordinates()
{
//OGDF_ASSERT(root != nullptr);
// compute y-coordinates
double yCoordinate; // the y-coordinate for the new level
root->y = yCoordinate = m_nTop;
// traverse the tree level by level
//one way
int level = m_Tree.max_depth();
for (int i = 1; i <= level; ++i) {
yCoordinate += 10 + m_levelDistance; //node size
tree<ProcessInfo>::fixed_depth_iterator fdit;
for (fdit = m_Tree.begin_fixed(root, i); m_Tree.is_valid(fdit); ++fdit) {
fdit->y = yCoordinate;
}
}
//two way
/*int level = 0;
tree<ProcessInfo>::breadth_first_iterator bfit = m_Tree.begin_breadth_first();
for (++bfit ; bfit != m_Tree.end_breadth_first(); ++bfit) {
if (level != m_Tree.depth(bfit)) {
yCoordinate += 10 + m_levelDistance; //node size
}
bfit->y = yCoordinate;
}*/
}
void STreeChart::computeXCoordinates()
{
//OGDF_ASSERT(root != nullptr);
// compute x-coordinates
double xCoordinate; // the x-coordinate for the new level
root->x = xCoordinate = m_nLeft;
// traverse the tree level by level
//one way
int level = m_Tree.max_depth();
for (int i = 1; i <= level; ++i) {
xCoordinate += 10 + m_levelDistance; //node size
tree<ProcessInfo>::fixed_depth_iterator fdit;
for (fdit = m_Tree.begin_fixed(root, i); m_Tree.is_valid(fdit); ++fdit) {
fdit->x = xCoordinate;
}
}
//two way
/*int level = 0;
tree<ProcessInfo>::breadth_first_iterator bfit = m_Tree.begin_breadth_first();
for (++bfit ; bfit != m_Tree.end_breadth_first(); ++bfit) {
if (level != m_Tree.depth(bfit)) {
xCoordinate += 10 + m_levelDistance; //node size
}
bfit->x = xCoordinate;
}*/
}
void STreeChart::call()
{
if (m_Tree.size() == 0) return;
/*OGDF_ASSERT(m_siblingDistance > 0);
OGDF_ASSERT(m_subtreeDistance > 0);
OGDF_ASSERT(m_levelDistance > 0);*/
TreeStructure ts(m_Tree);
if (m_orientation == Orientation::topToBottom || m_orientation == Orientation::bottomToTop)
{
firstWalk(ts, root, true);
secondWalkX(ts, root, m_nLeft);
// compute y-coordinates
computeYCoordinates();
// The computed layout draws a tree downwards. If we want to draw the
// tree upwards, we simply invert all y-coordinates.
if (m_orientation == Orientation::bottomToTop)
{
tree<ProcessInfo>::iterator it;
for (it = m_Tree.begin(); it != m_Tree.end(); ++it) {
it->y = -it->y;
}
}
}
else {
// compute y-coordinates
firstWalk(ts, root, false);
tree<ProcessInfo>::iterator tmp;
for (tmp = m_Tree.begin(); tmp != m_Tree.end(); ++tmp) {
printf("%s y %f\n", tmp->strName.c_str(), tmp->y);
printf("%s y %f\n", tmp->strName.c_str(), ts.m_preliminary[tmp]);
}
secondWalkY(ts, root, m_nTop);
//tree<ProcessInfo>::iterator tmp;
for (tmp = m_Tree.begin(); tmp != m_Tree.end(); ++tmp) {
printf("%s y %f\n", tmp->strName.c_str(), tmp->y);
printf("%s y %f\n", tmp->strName.c_str(), ts.m_preliminary[tmp]);
}
// compute y-coordinates
computeXCoordinates();
// The computed layout draws a tree upwards. If we want to draw the
// tree downwards, we simply invert all y-coordinates.
if (m_orientation == Orientation::rightToLeft)
{
tree<ProcessInfo>::iterator it;
for (it = m_Tree.begin(); it != m_Tree.end(); ++it) {
it->x = -it->x;
}
}
}
}
其它
有需要的欢迎交流。